diff --git a/apps/temporary-admin/Dockerfile b/apps/temporary-admin/Dockerfile index 9c43e40..0911e21 100644 --- a/apps/temporary-admin/Dockerfile +++ b/apps/temporary-admin/Dockerfile @@ -44,7 +44,7 @@ RUN chown -R nginx:nginx /var/run/nginx.pid \ USER nginx # expose a specific port on the docker container -ENV PORT=80 +ENV PORT=3001 EXPOSE ${PORT} # start the server using the previously build application diff --git a/apps/temporary-admin/README.md b/apps/temporary-admin/README.md index 715d898..cc7c387 100644 --- a/apps/temporary-admin/README.md +++ b/apps/temporary-admin/README.md @@ -1,12 +1,12 @@

- amplication-logo + amplication-logo

# Introduction -This service was generated with Amplication. It serves as the client-side for the generated server component. The client-side consist of a React application with ready-made forms for creating and editing the different data models of the application. It is pre-conffigured to work with the server and comes with the boilerplate and foundation for the client - i.e., routing, navigation, authentication, premissions, menu, breadcrumbs, error handling and much more. Additional information about the admin component and the architecture around it, can be found on the [documentation](https://docs.amplication.com/guides/getting-started) site. This side of the generated project was bootstrapped with [create-react-app](https://github.com/facebook/create-react-app) and built with [react-admin](https://marmelab.com/react-admin/). +This service was generated with Amplication. It serves as the client-side for the generated server component. The client-side consist of a React application with ready-made forms for creating and editing the different data models of the application. It is pre-conffigured to work with the server and comes with the boilerplate and foundation for the client - i.e., routing, navigation, authentication, permissions, menu, breadcrumbs, error handling and much more. Additional information about the admin component and the architecture around it, can be found on the [documentation](https://docs.amplication.com/guides/getting-started) site. This side of the generated project was bootstrapped with [create-react-app](https://github.com/facebook/create-react-app) and built with [react-admin](https://marmelab.com/react-admin/).

diff --git a/apps/temporary-admin/configuration/nginx.conf b/apps/temporary-admin/configuration/nginx.conf index 88dad6e..c907b5c 100644 --- a/apps/temporary-admin/configuration/nginx.conf +++ b/apps/temporary-admin/configuration/nginx.conf @@ -1,7 +1,7 @@ server_tokens off; server { - listen 8080; + listen 3001; server_name localhost; location / { root /usr/share/nginx/html; diff --git a/apps/temporary-admin/src/index.tsx b/apps/temporary-admin/src/index.tsx index 5e2de69..2da129c 100644 --- a/apps/temporary-admin/src/index.tsx +++ b/apps/temporary-admin/src/index.tsx @@ -1,8 +1,7 @@ import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; -// @ts-ignore -// eslint-disable-next-line import/no-unresolved + import App from "./App"; import reportWebVitals from "./reportWebVitals"; diff --git a/apps/temporary-admin/src/user/RolesOptions.ts b/apps/temporary-admin/src/user/RolesOptions.ts index 2f12fcf..5e30fe9 100644 --- a/apps/temporary-admin/src/user/RolesOptions.ts +++ b/apps/temporary-admin/src/user/RolesOptions.ts @@ -1,4 +1,3 @@ -//@ts-ignore import { ROLES } from "./roles"; declare interface Role { diff --git a/apps/temporary/.env b/apps/temporary/.env index 04605cd..f7678fa 100644 --- a/apps/temporary/.env +++ b/apps/temporary/.env @@ -1,10 +1,10 @@ BCRYPT_SALT=10 COMPOSE_PROJECT_NAME=amp_clmypp40t05ojm801iwsykclg -PORT=3000 -DB_URL=postgres://admin:admin@localhost:5432/my-db -DB_USER=admin +DB_NAME=my-db DB_PASSWORD=admin DB_PORT=5432 -DB_NAME=my-db +DB_URL=postgres://admin:admin@localhost:5432/my-db +DB_USER=admin +JWT_EXPIRATION=2d JWT_SECRET_KEY=Change_ME!!! -JWT_EXPIRATION=2d \ No newline at end of file +PORT=3000 \ No newline at end of file diff --git a/apps/temporary/README.md b/apps/temporary/README.md index 6db8e75..bca0445 100644 --- a/apps/temporary/README.md +++ b/apps/temporary/README.md @@ -1,6 +1,6 @@

- amplication-logo + amplication-logo

diff --git a/apps/temporary/docker-compose.yml b/apps/temporary/docker-compose.yml index 6b231f2..2a466f3 100644 --- a/apps/temporary/docker-compose.yml +++ b/apps/temporary/docker-compose.yml @@ -14,6 +14,7 @@ services: DB_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} depends_on: - migrate + restart: on-failure migrate: build: context: . diff --git a/apps/temporary/nest-cli.json b/apps/temporary/nest-cli.json index fe51713..b7b60ae 100644 --- a/apps/temporary/nest-cli.json +++ b/apps/temporary/nest-cli.json @@ -1,6 +1,10 @@ { "sourceRoot": "src", "compilerOptions": { - "assets": ["swagger"] + "assets": [ + { + "include": "swagger/**/*" + } + ] } } diff --git a/apps/temporary/package.json b/apps/temporary/package.json index 5d54604..e768e95 100644 --- a/apps/temporary/package.json +++ b/apps/temporary/package.json @@ -10,7 +10,7 @@ "seed": "ts-node scripts/seed.ts", "db:migrate-save": "prisma migrate dev", "db:migrate-up": "prisma migrate deploy", - "db:clean": "ts-node scripts/clean.ts", + "db:clean": "prisma migrate reset", "db:init": "run-s \"db:migrate-save -- --name 'initial version'\" db:migrate-up seed", "prisma:generate": "prisma generate", "docker:dev": "docker-compose -f docker-compose.dev.yml up -d", @@ -19,57 +19,59 @@ "compose:down": "docker-compose down --volumes" }, "dependencies": { - "@nestjs/common": "8.4.7", - "@nestjs/config": "1.1.5", - "@nestjs/core": "8.4.7", - "@nestjs/graphql": "9.1.2", - "@nestjs/platform-express": "8.4.7", - "@nestjs/serve-static": "2.2.2", - "@nestjs/swagger": "5.1.5", + "@apollo/server": "^4.9.4", + "@nestjs/apollo": "12.0.9", + "@nestjs/common": "10.2.7", + "@nestjs/config": "3.1.1", + "@nestjs/core": "10.2.7", + "@nestjs/graphql": "12.0.9", + "@nestjs/jwt": "^10.1.1", + "@nestjs/passport": "^10.0.2", + "@nestjs/platform-express": "10.2.7", + "@nestjs/serve-static": "4.0.0", + "@nestjs/swagger": "7.1.13", + "@prisma/client": "^5.4.2", "@types/bcrypt": "5.0.0", - "@prisma/client": "4.6.1", - "apollo-server-express": "3.6.1", - "bcrypt": "5.0.1", + "bcrypt": "5.1.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", - "dotenv": "^16.1.4", - "graphql": "15.7.2", + "dotenv": "16.3.1", + "graphql": "^16.8.1", "graphql-type-json": "0.3.2", - "nest-access-control": "2.0.3", - "nest-morgan": "1.0.1", + "nest-access-control": "^3.1.0", "npm-run-all": "4.1.5", - "reflect-metadata": "0.1.13", - "swagger-ui-express": "4.3.0", - "ts-node": "10.9.1", - "type-fest": "0.13.1", - "validator": "^13.9.0", - "@nestjs/jwt": "^10.0.2", - "@nestjs/passport": "^9.0.0", "passport": "0.6.0", "passport-http": "0.3.0", - "passport-jwt": "4.0.1" + "passport-jwt": "4.0.1", + "reflect-metadata": "0.1.13", + "ts-node": "10.9.2", + "type-fest": "2.19.0", + "validator": "13.11.0" }, "devDependencies": { - "@nestjs/cli": "8.2.5", - "@nestjs/testing": "8.4.7", - "@types/express": "4.17.9", - "@types/graphql-type-json": "0.3.2", - "@types/jest": "26.0.19", + "@nestjs/cli": "^10.1.18", + "@nestjs/testing": "^10.2.7", + "@types/express": "^4.17.19", + "@types/graphql-type-json": "0.3.3", + "@types/jest": "^29.5.5", "@types/normalize-path": "3.0.0", - "@types/supertest": "2.0.11", - "@types/validator": "^13.7.15", - "jest": "27.0.6", - "jest-mock-extended": "^2.0.4", - "prisma": "4.6.1", - "supertest": "4.0.2", - "ts-jest": "27.0.3", - "typescript": "4.3.5", "@types/passport-http": "0.3.9", - "@types/passport-jwt": "3.0.8" + "@types/passport-jwt": "3.0.10", + "@types/supertest": "^2.0.14", + "@types/validator": "^13.11.2", + "jest": "^29.7.0", + "jest-mock-extended": "^3.0.5", + "prisma": "^5.4.2", + "supertest": "^6.3.3", + "ts-jest": "^29.1.1", + "typescript": "^5.4.3" }, "jest": { "preset": "ts-jest", "testEnvironment": "node", + "moduleNameMapper": { + "@app/custom-validators": "/src/validators" + }, "modulePathIgnorePatterns": ["/dist/"] } } diff --git a/apps/temporary/scripts/clean.ts b/apps/temporary/scripts/clean.ts deleted file mode 100644 index a3887c8..0000000 --- a/apps/temporary/scripts/clean.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Clean all the tables and types created by Prisma in the database - */ - -import { PrismaClient } from "@prisma/client"; - -if (require.main === module) { - clean().catch((error) => { - console.error(error); - process.exit(1); - }); -} - -async function clean() { - console.info("Dropping all tables in the database..."); - const prisma = new PrismaClient(); - const tables = await getTables(prisma); - const types = await getTypes(prisma); - await dropTables(prisma, tables); - await dropTypes(prisma, types); - console.info("Cleaned database successfully"); - await prisma.$disconnect(); -} - -async function dropTables( - prisma: PrismaClient, - tables: string[] -): Promise { - for (const table of tables) { - await prisma.$executeRawUnsafe(`DROP TABLE public."${table}" CASCADE;`); - } -} - -async function dropTypes(prisma: PrismaClient, types: string[]) { - for (const type of types) { - await prisma.$executeRawUnsafe(`DROP TYPE IF EXISTS "${type}" CASCADE;`); - } -} - -async function getTables(prisma: PrismaClient): Promise { - const results: Array<{ - tablename: string; - }> = - await prisma.$queryRaw`SELECT tablename from pg_tables where schemaname = 'public';`; - return results.map((result) => result.tablename); -} - -async function getTypes(prisma: PrismaClient): Promise { - const results: Array<{ - typname: string; - }> = await prisma.$queryRaw` - SELECT t.typname - FROM pg_type t - JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace - WHERE n.nspname = 'public'; - `; - return results.map((result) => result.typname); -} diff --git a/apps/temporary/src/app.module.ts b/apps/temporary/src/app.module.ts index e33968e..2fcb3b5 100644 --- a/apps/temporary/src/app.module.ts +++ b/apps/temporary/src/app.module.ts @@ -1,6 +1,4 @@ -import { Module, Scope } from "@nestjs/common"; -import { APP_INTERCEPTOR } from "@nestjs/core"; -import { MorganInterceptor, MorganModule } from "nest-morgan"; +import { Module } from "@nestjs/common"; import { UserModule } from "./user/user.module"; import { HealthModule } from "./health/health.module"; import { PrismaModule } from "./prisma/prisma.module"; @@ -9,6 +7,7 @@ import { ServeStaticModule } from "@nestjs/serve-static"; import { ServeStaticOptionsService } from "./serveStaticOptions.service"; import { ConfigModule, ConfigService } from "@nestjs/config"; import { GraphQLModule } from "@nestjs/graphql"; +import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo"; import { ACLModule } from "./auth/acl.module"; import { AuthModule } from "./auth/auth.module"; @@ -22,13 +21,13 @@ import { AuthModule } from "./auth/auth.module"; HealthModule, PrismaModule, SecretsManagerModule, - MorganModule, ConfigModule.forRoot({ isGlobal: true }), ServeStaticModule.forRootAsync({ useClass: ServeStaticOptionsService, }), - GraphQLModule.forRootAsync({ - useFactory: (configService) => { + GraphQLModule.forRootAsync({ + driver: ApolloDriver, + useFactory: (configService: ConfigService) => { const playground = configService.get("GRAPHQL_PLAYGROUND"); const introspection = configService.get("GRAPHQL_INTROSPECTION"); return { @@ -42,12 +41,6 @@ import { AuthModule } from "./auth/auth.module"; imports: [ConfigModule], }), ], - providers: [ - { - provide: APP_INTERCEPTOR, - scope: Scope.REQUEST, - useClass: MorganInterceptor("combined"), - }, - ], + providers: [], }) export class AppModule {} diff --git a/apps/temporary/src/auth/IAuthStrategy.ts b/apps/temporary/src/auth/IAuthStrategy.ts index 5db10cf..7406267 100644 --- a/apps/temporary/src/auth/IAuthStrategy.ts +++ b/apps/temporary/src/auth/IAuthStrategy.ts @@ -1,5 +1,6 @@ import { UserInfo } from "./UserInfo"; export interface IAuthStrategy { + // eslint-disable-next-line @typescript-eslint/no-explicit-any validate: (...any: any) => Promise; } diff --git a/apps/temporary/src/auth/abac.util.ts b/apps/temporary/src/auth/abac.util.ts index 7047513..2f0dcab 100644 --- a/apps/temporary/src/auth/abac.util.ts +++ b/apps/temporary/src/auth/abac.util.ts @@ -9,6 +9,11 @@ export function getInvalidAttributes( // eslint-disable-next-line @typescript-eslint/ban-types data: Object ): string[] { - const filteredData = permission.filter(data); + // The structuredClone call is necessary because the + // `Permission.filter` function doesn't consider objects + // with null prototypes. And in graphql requests, the + // object passed here by the request interceptor is an object + // with a null prototype. + const filteredData = permission.filter(structuredClone(data)); return Object.keys(data).filter((key) => !(key in filteredData)); } diff --git a/apps/temporary/src/auth/acl.module.ts b/apps/temporary/src/auth/acl.module.ts index eb90005..040e7e5 100644 --- a/apps/temporary/src/auth/acl.module.ts +++ b/apps/temporary/src/auth/acl.module.ts @@ -1,6 +1,5 @@ import { AccessControlModule, RolesBuilder } from "nest-access-control"; -// @ts-ignore -// eslint-disable-next-line import/no-unresolved + import grants from "../grants.json"; // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/temporary/src/auth/auth.module.ts b/apps/temporary/src/auth/auth.module.ts index 73c8725..d69fd95 100644 --- a/apps/temporary/src/auth/auth.module.ts +++ b/apps/temporary/src/auth/auth.module.ts @@ -2,9 +2,10 @@ import { forwardRef, Module } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { JwtModule } from "@nestjs/jwt"; import { PassportModule } from "@nestjs/passport"; -import { JWT_EXPIRATION, JWT_SECRET_KEY } from "../constants"; +import { JWT_EXPIRATION } from "../constants"; import { SecretsManagerModule } from "../providers/secrets/secretsManager.module"; import { SecretsManagerService } from "../providers/secrets/secretsManager.service"; +import { EnumSecretsNameKey } from "../providers/secrets/secretsNameKey.enum"; import { AuthController } from "./auth.controller"; import { AuthResolver } from "./auth.resolver"; import { AuthService } from "./auth.service"; @@ -25,7 +26,9 @@ import { UserModule } from "../user/user.module"; secretsService: SecretsManagerService, configService: ConfigService ) => { - const secret = await secretsService.getSecret(JWT_SECRET_KEY); + const secret = await secretsService.getSecret( + EnumSecretsNameKey.JwtSecretKey + ); const expiresIn = configService.get(JWT_EXPIRATION); if (!secret) { throw new Error("Didn't get a valid jwt secret"); diff --git a/apps/temporary/src/auth/auth.service.spec.ts b/apps/temporary/src/auth/auth.service.spec.ts index 72fd2d3..90b04f4 100644 --- a/apps/temporary/src/auth/auth.service.spec.ts +++ b/apps/temporary/src/auth/auth.service.spec.ts @@ -27,7 +27,7 @@ const USER: any = { const SIGN_TOKEN = "SIGN_TOKEN"; const authEntityService = { - findOne(args: { where: { username: string } }): any | null { + user(args: { where: { username: string } }): any | null { if (args.where.username === VALID_CREDENTIALS.username) { return USER; } diff --git a/apps/temporary/src/auth/auth.service.ts b/apps/temporary/src/auth/auth.service.ts index f5a74ba..9f6add2 100644 --- a/apps/temporary/src/auth/auth.service.ts +++ b/apps/temporary/src/auth/auth.service.ts @@ -17,7 +17,7 @@ export class AuthService { username: string, password: string ): Promise { - const user = await this.userService.findOne({ + const user = await this.userService.user({ where: { username }, }); if (user && (await this.passwordService.compare(password, user.password))) { diff --git a/apps/temporary/src/auth/gqlDefaultAuth.guard.ts b/apps/temporary/src/auth/gqlDefaultAuth.guard.ts index 17143e1..abebd93 100644 --- a/apps/temporary/src/auth/gqlDefaultAuth.guard.ts +++ b/apps/temporary/src/auth/gqlDefaultAuth.guard.ts @@ -1,13 +1,11 @@ import { ExecutionContext } from "@nestjs/common"; import { GqlExecutionContext } from "@nestjs/graphql"; import type { Request } from "express"; -// @ts-ignore -// eslint-disable-next-line import { DefaultAuthGuard } from "./defaultAuth.guard"; export class GqlDefaultAuthGuard extends DefaultAuthGuard { // This method is required for the interface - do not delete it. - getRequest(context: ExecutionContext): Request { + getRequest(context: ExecutionContext): Request { const ctx = GqlExecutionContext.create(context); return ctx.getContext<{ req: Request }>().req; } diff --git a/apps/temporary/src/auth/jwt/base/jwt.strategy.base.ts b/apps/temporary/src/auth/jwt/base/jwt.strategy.base.ts index 3bbc919..26da6a7 100644 --- a/apps/temporary/src/auth/jwt/base/jwt.strategy.base.ts +++ b/apps/temporary/src/auth/jwt/base/jwt.strategy.base.ts @@ -22,7 +22,7 @@ export class JwtStrategyBase async validate(payload: UserInfo): Promise { const { username } = payload; - const user = await this.userService.findOne({ + const user = await this.userService.user({ where: { username }, }); if (!user) { diff --git a/apps/temporary/src/auth/jwt/jwt.strategy.ts b/apps/temporary/src/auth/jwt/jwt.strategy.ts index 9e75f4c..13395e5 100644 --- a/apps/temporary/src/auth/jwt/jwt.strategy.ts +++ b/apps/temporary/src/auth/jwt/jwt.strategy.ts @@ -1,12 +1,12 @@ import { Inject, Injectable } from "@nestjs/common"; -import { JWT_SECRET_KEY } from "../../constants"; +import { JWT_SECRET_KEY_PROVIDER_NAME } from "../../constants"; import { JwtStrategyBase } from "./base/jwt.strategy.base"; import { UserService } from "../../user/user.service"; @Injectable() export class JwtStrategy extends JwtStrategyBase { constructor( - @Inject(JWT_SECRET_KEY) secretOrKey: string, + @Inject(JWT_SECRET_KEY_PROVIDER_NAME) secretOrKey: string, protected readonly userService: UserService ) { super(secretOrKey, userService); diff --git a/apps/temporary/src/auth/jwt/jwtSecretFactory.ts b/apps/temporary/src/auth/jwt/jwtSecretFactory.ts index 151e793..8ca4d74 100644 --- a/apps/temporary/src/auth/jwt/jwtSecretFactory.ts +++ b/apps/temporary/src/auth/jwt/jwtSecretFactory.ts @@ -1,12 +1,15 @@ -import { JWT_SECRET_KEY } from "../../constants"; +import { JWT_SECRET_KEY_PROVIDER_NAME } from "../../constants"; import { SecretsManagerService } from "../../providers/secrets/secretsManager.service"; +import { EnumSecretsNameKey } from "../../providers/secrets/secretsNameKey.enum"; export const jwtSecretFactory = { - provide: JWT_SECRET_KEY, + provide: JWT_SECRET_KEY_PROVIDER_NAME, useFactory: async ( secretsService: SecretsManagerService ): Promise => { - const secret = await secretsService.getSecret(JWT_SECRET_KEY); + const secret = await secretsService.getSecret( + EnumSecretsNameKey.JwtSecretKey + ); if (secret) { return secret; } diff --git a/apps/temporary/src/auth/token.service.ts b/apps/temporary/src/auth/token.service.ts index b102b72..2ee079c 100644 --- a/apps/temporary/src/auth/token.service.ts +++ b/apps/temporary/src/auth/token.service.ts @@ -1,7 +1,5 @@ -//@ts-ignore import { ITokenService } from "./ITokenService"; -// eslint-disable-next-line import/no-unresolved -//@ts-ignore + import { TokenServiceBase } from "./base/token.service.base"; export class TokenService extends TokenServiceBase implements ITokenService {} diff --git a/apps/temporary/src/constants.ts b/apps/temporary/src/constants.ts index 08f98bf..e131049 100644 --- a/apps/temporary/src/constants.ts +++ b/apps/temporary/src/constants.ts @@ -1,2 +1,2 @@ -export const JWT_SECRET_KEY = "JWT_SECRET_KEY"; +export const JWT_SECRET_KEY_PROVIDER_NAME = "JWT_SECRET_KEY"; export const JWT_EXPIRATION = "JWT_EXPIRATION"; diff --git a/apps/temporary/src/prisma.util.ts b/apps/temporary/src/prisma.util.ts index 8e0779c..029b98a 100644 --- a/apps/temporary/src/prisma.util.ts +++ b/apps/temporary/src/prisma.util.ts @@ -1,10 +1,9 @@ export const PRISMA_QUERY_INTERPRETATION_ERROR = "P2016"; export const PRISMA_RECORD_NOT_FOUND = "RecordNotFound"; -export function isRecordNotFoundError( - error: Error & { code?: string } -): boolean { +export function isRecordNotFoundError(error: any): boolean { return ( + error instanceof Error && "code" in error && error.code === PRISMA_QUERY_INTERPRETATION_ERROR && error.message.includes(PRISMA_RECORD_NOT_FOUND) diff --git a/apps/temporary/src/prisma/prisma.service.ts b/apps/temporary/src/prisma/prisma.service.ts index 3fb4081..79ea4fa 100644 --- a/apps/temporary/src/prisma/prisma.service.ts +++ b/apps/temporary/src/prisma/prisma.service.ts @@ -6,10 +6,4 @@ export class PrismaService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect(); } - - async enableShutdownHooks(app: INestApplication) { - this.$on("beforeExit", async () => { - await app.close(); - }); - } } diff --git a/apps/temporary/src/providers/secrets/base/secretsManager.service.base.spec.ts b/apps/temporary/src/providers/secrets/base/secretsManager.service.base.spec.ts index eb4dfb1..f161172 100644 --- a/apps/temporary/src/providers/secrets/base/secretsManager.service.base.spec.ts +++ b/apps/temporary/src/providers/secrets/base/secretsManager.service.base.spec.ts @@ -1,6 +1,7 @@ import { ConfigService } from "@nestjs/config"; import { mock } from "jest-mock-extended"; import { SecretsManagerServiceBase } from "./secretsManager.service.base"; +import { EnumSecretsNameKey } from "../secretsNameKey.enum"; describe("Testing the secrets manager base class", () => { const SECRET_KEY = "SECRET_KEY"; @@ -16,7 +17,9 @@ describe("Testing the secrets manager base class", () => { //ARRANGE configService.get.mockReturnValue(SECRET_VALUE); //ACT - const result = await secretsManagerServiceBase.getSecret(SECRET_KEY); + const result = await secretsManagerServiceBase.getSecret( + SECRET_KEY as unknown as EnumSecretsNameKey + ); //ASSERT expect(result).toBe(SECRET_VALUE); }); @@ -24,16 +27,15 @@ describe("Testing the secrets manager base class", () => { //ARRANGE configService.get.mockReturnValue(undefined); //ACT - const result = await secretsManagerServiceBase.getSecret(SECRET_KEY); + const result = await secretsManagerServiceBase.getSecret( + SECRET_KEY as unknown as EnumSecretsNameKey + ); //ASSERT expect(result).toBeNull(); }); - it("should throw error if dont get key", () => { - return expect(secretsManagerServiceBase.getSecret("")).rejects.toThrow(); - }); - it("should throw an exeption if getting null key", () => { + it("should throw an exception if getting null key", () => { return expect( - secretsManagerServiceBase.getSecret(null as unknown as string) + secretsManagerServiceBase.getSecret(null as unknown as EnumSecretsNameKey) ).rejects.toThrow(); }); }); diff --git a/apps/temporary/src/providers/secrets/base/secretsManager.service.base.ts b/apps/temporary/src/providers/secrets/base/secretsManager.service.base.ts index 18a340b..340818c 100644 --- a/apps/temporary/src/providers/secrets/base/secretsManager.service.base.ts +++ b/apps/temporary/src/providers/secrets/base/secretsManager.service.base.ts @@ -1,16 +1,14 @@ import { ConfigService } from "@nestjs/config"; +import { EnumSecretsNameKey } from "../secretsNameKey.enum"; export interface ISecretsManager { - getSecret: (key: string) => Promise; + getSecret: (key: EnumSecretsNameKey) => Promise; } export class SecretsManagerServiceBase implements ISecretsManager { constructor(protected readonly configService: ConfigService) {} - async getSecret(key: string): Promise { - if (!key) { - throw new Error("Didn't got the key"); - } - const value = this.configService.get(key); + async getSecret(key: EnumSecretsNameKey): Promise { + const value = this.configService.get(key.toString()); if (value) { return value; } diff --git a/apps/temporary/src/providers/secrets/secretsNameKey.enum.ts b/apps/temporary/src/providers/secrets/secretsNameKey.enum.ts new file mode 100644 index 0000000..52c18af --- /dev/null +++ b/apps/temporary/src/providers/secrets/secretsNameKey.enum.ts @@ -0,0 +1,3 @@ +export enum EnumSecretsNameKey { + JwtSecretKey = "JWT_SECRET_KEY" +} \ No newline at end of file diff --git a/apps/temporary/src/tests/auth/jwt/jwt.strategy.spec.ts b/apps/temporary/src/tests/auth/jwt/jwt.strategy.spec.ts index 2463c9b..36bbb24 100644 --- a/apps/temporary/src/tests/auth/jwt/jwt.strategy.spec.ts +++ b/apps/temporary/src/tests/auth/jwt/jwt.strategy.spec.ts @@ -5,14 +5,16 @@ import { TEST_USER } from "../constants"; import { UserService } from "../../../user/user.service"; describe("Testing the jwtStrategyBase.validate()", () => { const userService = mock(); - const jwtStrategy = new JwtStrategyBase(userService, "Secrete"); + const jwtStrategy = new JwtStrategyBase("Secrete", userService); beforeEach(() => { - userService.findOne.mockClear(); + userService.user.mockClear(); }); it("should throw UnauthorizedException where there is no user", async () => { //ARRANGE - userService.findOne - .calledWith({ where: { username: TEST_USER.username } }) + userService.user + .calledWith({ + where: { username: TEST_USER.username }, + }) .mockReturnValue(Promise.resolve(null)); //ACT const result = jwtStrategy.validate({ diff --git a/apps/temporary/src/user/base/User.ts b/apps/temporary/src/user/base/User.ts index 45adebe..348fd32 100644 --- a/apps/temporary/src/user/base/User.ts +++ b/apps/temporary/src/user/base/User.ts @@ -11,9 +11,9 @@ https://docs.amplication.com/how-to/custom-code */ import { ObjectType, Field } from "@nestjs/graphql"; import { ApiProperty } from "@nestjs/swagger"; -import { IsDate, IsString, IsOptional } from "class-validator"; +import { IsDate, IsString, MaxLength, IsOptional } from "class-validator"; import { Type } from "class-transformer"; -import { IsJSONValue } from "@app/custom-validators"; +import { IsJSONValue } from "../../validators"; import { GraphQLJSON } from "graphql-type-json"; import { JsonValue } from "type-fest"; @@ -32,6 +32,7 @@ class User { type: String, }) @IsString() + @MaxLength(256) @IsOptional() @Field(() => String, { nullable: true, @@ -51,6 +52,7 @@ class User { type: String, }) @IsString() + @MaxLength(256) @IsOptional() @Field(() => String, { nullable: true, diff --git a/apps/temporary/src/user/base/UserCreateInput.ts b/apps/temporary/src/user/base/UserCreateInput.ts index 6896f2a..a98430e 100644 --- a/apps/temporary/src/user/base/UserCreateInput.ts +++ b/apps/temporary/src/user/base/UserCreateInput.ts @@ -11,8 +11,8 @@ https://docs.amplication.com/how-to/custom-code */ import { InputType, Field } from "@nestjs/graphql"; import { ApiProperty } from "@nestjs/swagger"; -import { IsString, IsOptional } from "class-validator"; -import { IsJSONValue } from "@app/custom-validators"; +import { IsString, MaxLength, IsOptional } from "class-validator"; +import { IsJSONValue } from "../../validators"; import { GraphQLJSON } from "graphql-type-json"; import { InputJsonValue } from "../../types"; @@ -23,6 +23,7 @@ class UserCreateInput { type: String, }) @IsString() + @MaxLength(256) @IsOptional() @Field(() => String, { nullable: true, @@ -34,6 +35,7 @@ class UserCreateInput { type: String, }) @IsString() + @MaxLength(256) @IsOptional() @Field(() => String, { nullable: true, diff --git a/apps/temporary/src/user/base/UserUpdateInput.ts b/apps/temporary/src/user/base/UserUpdateInput.ts index b6e0e21..aefeb3f 100644 --- a/apps/temporary/src/user/base/UserUpdateInput.ts +++ b/apps/temporary/src/user/base/UserUpdateInput.ts @@ -11,8 +11,8 @@ https://docs.amplication.com/how-to/custom-code */ import { InputType, Field } from "@nestjs/graphql"; import { ApiProperty } from "@nestjs/swagger"; -import { IsString, IsOptional } from "class-validator"; -import { IsJSONValue } from "@app/custom-validators"; +import { IsString, MaxLength, IsOptional } from "class-validator"; +import { IsJSONValue } from "../../validators"; import { GraphQLJSON } from "graphql-type-json"; import { InputJsonValue } from "../../types"; @@ -23,6 +23,7 @@ class UserUpdateInput { type: String, }) @IsString() + @MaxLength(256) @IsOptional() @Field(() => String, { nullable: true, @@ -34,6 +35,7 @@ class UserUpdateInput { type: String, }) @IsString() + @MaxLength(256) @IsOptional() @Field(() => String, { nullable: true, diff --git a/apps/temporary/src/user/base/user.controller.base.spec.ts b/apps/temporary/src/user/base/user.controller.base.spec.ts index 63b8914..fb15280 100644 --- a/apps/temporary/src/user/base/user.controller.base.spec.ts +++ b/apps/temporary/src/user/base/user.controller.base.spec.ts @@ -6,7 +6,6 @@ import { CallHandler, } from "@nestjs/common"; import request from "supertest"; -import { MorganModule } from "nest-morgan"; import { ACGuard } from "nest-access-control"; import { DefaultAuthGuard } from "../../auth/defaultAuth.guard"; import { ACLModule } from "../../auth/acl.module"; @@ -58,11 +57,11 @@ const FIND_ONE_RESULT = { }; const service = { - create() { + createUser() { return CREATE_RESULT; }, - findMany: () => FIND_MANY_RESULT, - findOne: ({ where }: { where: { id: string } }) => { + users: () => FIND_MANY_RESULT, + user: ({ where }: { where: { id: string } }) => { switch (where.id) { case existingId: return FIND_ONE_RESULT; @@ -116,7 +115,7 @@ describe("User", () => { }, ], controllers: [UserController], - imports: [MorganModule.forRoot(), ACLModule], + imports: [ACLModule], }) .overrideGuard(DefaultAuthGuard) .useValue(basicAuthGuard) diff --git a/apps/temporary/src/user/base/user.controller.base.ts b/apps/temporary/src/user/base/user.controller.base.ts index 0a05e44..57e9815 100644 --- a/apps/temporary/src/user/base/user.controller.base.ts +++ b/apps/temporary/src/user/base/user.controller.base.ts @@ -22,11 +22,10 @@ import { UserService } from "../user.service"; import { AclValidateRequestInterceptor } from "../../interceptors/aclValidateRequest.interceptor"; import { AclFilterResponseInterceptor } from "../../interceptors/aclFilterResponse.interceptor"; import { UserCreateInput } from "./UserCreateInput"; -import { UserWhereInput } from "./UserWhereInput"; -import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; +import { User } from "./User"; import { UserFindManyArgs } from "./UserFindManyArgs"; +import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; import { UserUpdateInput } from "./UserUpdateInput"; -import { User } from "./User"; @swagger.ApiBearerAuth() @common.UseGuards(defaultAuthGuard.DefaultAuthGuard, nestAccessControl.ACGuard) @@ -46,8 +45,8 @@ export class UserControllerBase { @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException, }) - async create(@common.Body() data: UserCreateInput): Promise { - return await this.service.create({ + async createUser(@common.Body() data: UserCreateInput): Promise { + return await this.service.createUser({ data: data, select: { createdAt: true, @@ -73,9 +72,9 @@ export class UserControllerBase { @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException, }) - async findMany(@common.Req() request: Request): Promise { + async users(@common.Req() request: Request): Promise { const args = plainToClass(UserFindManyArgs, request.query); - return this.service.findMany({ + return this.service.users({ ...args, select: { createdAt: true, @@ -101,10 +100,10 @@ export class UserControllerBase { @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException, }) - async findOne( + async user( @common.Param() params: UserWhereUniqueInput ): Promise { - const result = await this.service.findOne({ + const result = await this.service.user({ where: params, select: { createdAt: true, @@ -136,12 +135,12 @@ export class UserControllerBase { @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException, }) - async update( + async updateUser( @common.Param() params: UserWhereUniqueInput, @common.Body() data: UserUpdateInput ): Promise { try { - return await this.service.update({ + return await this.service.updateUser({ where: params, data: data, select: { @@ -175,11 +174,11 @@ export class UserControllerBase { @swagger.ApiForbiddenResponse({ type: errors.ForbiddenException, }) - async delete( + async deleteUser( @common.Param() params: UserWhereUniqueInput ): Promise { try { - return await this.service.delete({ + return await this.service.deleteUser({ where: params, select: { createdAt: true, diff --git a/apps/temporary/src/user/base/user.module.base.ts b/apps/temporary/src/user/base/user.module.base.ts index 078e0b5..a8b6c68 100644 --- a/apps/temporary/src/user/base/user.module.base.ts +++ b/apps/temporary/src/user/base/user.module.base.ts @@ -10,10 +10,9 @@ https://docs.amplication.com/how-to/custom-code ------------------------------------------------------------------------------ */ import { Module } from "@nestjs/common"; -import { MorganModule } from "nest-morgan"; import { ACLModule } from "../../auth/acl.module"; @Module({ - imports: [ACLModule, MorganModule], - exports: [ACLModule, MorganModule], + imports: [ACLModule], + exports: [ACLModule], }) export class UserModuleBase {} diff --git a/apps/temporary/src/user/base/user.resolver.base.ts b/apps/temporary/src/user/base/user.resolver.base.ts index ad9e01a..0c60d95 100644 --- a/apps/temporary/src/user/base/user.resolver.base.ts +++ b/apps/temporary/src/user/base/user.resolver.base.ts @@ -10,7 +10,7 @@ https://docs.amplication.com/how-to/custom-code ------------------------------------------------------------------------------ */ import * as graphql from "@nestjs/graphql"; -import * as apollo from "apollo-server-express"; +import { GraphQLError } from "graphql"; import { isRecordNotFoundError } from "../../prisma.util"; import { MetaQueryPayload } from "../../util/MetaQueryPayload"; import * as nestAccessControl from "nest-access-control"; @@ -19,13 +19,13 @@ import { GqlDefaultAuthGuard } from "../../auth/gqlDefaultAuth.guard"; import * as common from "@nestjs/common"; import { AclFilterResponseInterceptor } from "../../interceptors/aclFilterResponse.interceptor"; import { AclValidateRequestInterceptor } from "../../interceptors/aclValidateRequest.interceptor"; -import { CreateUserArgs } from "./CreateUserArgs"; -import { UpdateUserArgs } from "./UpdateUserArgs"; -import { DeleteUserArgs } from "./DeleteUserArgs"; +import { User } from "./User"; import { UserCountArgs } from "./UserCountArgs"; import { UserFindManyArgs } from "./UserFindManyArgs"; import { UserFindUniqueArgs } from "./UserFindUniqueArgs"; -import { User } from "./User"; +import { CreateUserArgs } from "./CreateUserArgs"; +import { UpdateUserArgs } from "./UpdateUserArgs"; +import { DeleteUserArgs } from "./DeleteUserArgs"; import { UserService } from "../user.service"; @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) @graphql.Resolver(() => User) @@ -58,7 +58,7 @@ export class UserResolverBase { possession: "any", }) async users(@graphql.Args() args: UserFindManyArgs): Promise { - return this.service.findMany(args); + return this.service.users(args); } @common.UseInterceptors(AclFilterResponseInterceptor) @@ -69,7 +69,7 @@ export class UserResolverBase { possession: "own", }) async user(@graphql.Args() args: UserFindUniqueArgs): Promise { - const result = await this.service.findOne(args); + const result = await this.service.user(args); if (result === null) { return null; } @@ -84,7 +84,7 @@ export class UserResolverBase { possession: "any", }) async createUser(@graphql.Args() args: CreateUserArgs): Promise { - return await this.service.create({ + return await this.service.createUser({ ...args, data: args.data, }); @@ -99,13 +99,13 @@ export class UserResolverBase { }) async updateUser(@graphql.Args() args: UpdateUserArgs): Promise { try { - return await this.service.update({ + return await this.service.updateUser({ ...args, data: args.data, }); } catch (error) { if (isRecordNotFoundError(error)) { - throw new apollo.ApolloError( + throw new GraphQLError( `No resource was found for ${JSON.stringify(args.where)}` ); } @@ -121,10 +121,10 @@ export class UserResolverBase { }) async deleteUser(@graphql.Args() args: DeleteUserArgs): Promise { try { - return await this.service.delete(args); + return await this.service.deleteUser(args); } catch (error) { if (isRecordNotFoundError(error)) { - throw new apollo.ApolloError( + throw new GraphQLError( `No resource was found for ${JSON.stringify(args.where)}` ); } diff --git a/apps/temporary/src/user/base/user.service.base.ts b/apps/temporary/src/user/base/user.service.base.ts index 3c9de61..6a749c5 100644 --- a/apps/temporary/src/user/base/user.service.base.ts +++ b/apps/temporary/src/user/base/user.service.base.ts @@ -10,7 +10,7 @@ https://docs.amplication.com/how-to/custom-code ------------------------------------------------------------------------------ */ import { PrismaService } from "../../prisma/prisma.service"; -import { Prisma, User } from "@prisma/client"; +import { Prisma, User as PrismaUser } from "@prisma/client"; import { PasswordService } from "../../auth/password.service"; import { transformStringFieldUpdateInput } from "../../prisma.util"; @@ -20,26 +20,18 @@ export class UserServiceBase { protected readonly passwordService: PasswordService ) {} - async count( - args: Prisma.SelectSubset - ): Promise { + async count(args: Omit): Promise { return this.prisma.user.count(args); } - async findMany( - args: Prisma.SelectSubset - ): Promise { + async users(args: Prisma.UserFindManyArgs): Promise { return this.prisma.user.findMany(args); } - async findOne( - args: Prisma.SelectSubset - ): Promise { + async user(args: Prisma.UserFindUniqueArgs): Promise { return this.prisma.user.findUnique(args); } - async create( - args: Prisma.SelectSubset - ): Promise { - return this.prisma.user.create({ + async createUser(args: Prisma.UserCreateArgs): Promise { + return this.prisma.user.create({ ...args, data: { @@ -48,10 +40,8 @@ export class UserServiceBase { }, }); } - async update( - args: Prisma.SelectSubset - ): Promise { - return this.prisma.user.update({ + async updateUser(args: Prisma.UserUpdateArgs): Promise { + return this.prisma.user.update({ ...args, data: { @@ -66,9 +56,7 @@ export class UserServiceBase { }, }); } - async delete( - args: Prisma.SelectSubset - ): Promise { + async deleteUser(args: Prisma.UserDeleteArgs): Promise { return this.prisma.user.delete(args); } } diff --git a/apps/temporary/tsconfig.json b/apps/temporary/tsconfig.json index f084cf7..707e8cd 100644 --- a/apps/temporary/tsconfig.json +++ b/apps/temporary/tsconfig.json @@ -6,8 +6,8 @@ "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "es2017", - "lib": ["ES2020"], + "target": "es2022", + "lib": ["es2023"], "sourceMap": true, "outDir": "./dist", "incremental": true, @@ -15,10 +15,7 @@ "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "skipLibCheck": true, - "strict": true, - "paths": { - "@app/custom-validators": ["src/validators"] - } + "strict": true }, "include": ["src"] } diff --git a/helm/flights/templates/deployment.yaml b/helm/flights/templates/deployment.yaml index 4e87585..ff02491 100644 --- a/helm/flights/templates/deployment.yaml +++ b/helm/flights/templates/deployment.yaml @@ -30,6 +30,21 @@ spec: serviceAccountName: {{ include "flights.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if or .Values.variables.configmap.DB_URL .Values.variables.secret.DB_URL }} + initContainers: + - name: {{ .Chart.Name }}-prisma-migrate + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + command: ["sh", "-c", "npx prisma migrate deploy; exit 0"] + {{- if .Values.variables.configmap.DB_URL }} + env: + - name: DATABASE_URL + value: {{ required ".Values.variables.configmap.DB_URL is required" .Values.variables.configmap.DB_URL }} + {{- else if .Values.variables.secret.DB_URL }} + env: + - name: DATABASE_URL + value: {{ required ".Values.variables.configmap.DB_URL is required" .Values.variables.configmap.DB_URL }} + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: diff --git a/helm/flights/templates/hpa.yaml b/helm/flights/templates/hpa.yaml index e256d88..1c56f9b 100644 --- a/helm/flights/templates/hpa.yaml +++ b/helm/flights/templates/hpa.yaml @@ -1,5 +1,5 @@ {{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 +apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: {{ include "flights.fullname" . }} @@ -17,12 +17,16 @@ spec: - type: Resource resource: name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} {{- end }} {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - type: Resource resource: name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} {{- end }} {{- end }} diff --git a/helm/flights/values.yaml b/helm/flights/values.yaml index b7e4238..38a9e30 100644 --- a/helm/flights/values.yaml +++ b/helm/flights/values.yaml @@ -10,16 +10,16 @@ image: variables: configmap: - BCRYPT_SALT: 10 - COMPOSE_PROJECT_NAME: amp_${resourceId} - PORT: 3000 - DB_URL: postgres://admin:admin@localhost:5432/my-db - DB_USER: admin - DB_PASSWORD: admin - DB_PORT: 5432 - DB_NAME: my-db - JWT_SECRET_KEY: Change_ME!!! - JWT_EXPIRATION: 2d + BCRYPT_SALT: "10" + COMPOSE_PROJECT_NAME: "amp_${resourceId}" + PORT: "3000" + JWT_SECRET_KEY: "Change_ME!!!" + JWT_EXPIRATION: "2d" + DB_URL: "postgres://admin:admin@localhost:5432/my-db" + DB_USER: "admin" + DB_PASSWORD: "admin" + DB_PORT: "5432" + DB_NAME: "my-db" secret: {} imagePullSecrets: []