@nestledjs/api 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +11 -0
  3. package/eslint.config.cjs +28 -0
  4. package/generators.json +69 -0
  5. package/package.json +21 -0
  6. package/project.json +47 -0
  7. package/src/account/files/data-access/src/index.ts__tmpl__ +5 -0
  8. package/src/account/files/data-access/src/lib/api-account-data-access.module.ts__tmpl__ +10 -0
  9. package/src/account/files/data-access/src/lib/api-account-data-access.service.ts__tmpl__ +152 -0
  10. package/src/account/files/data-access/src/lib/dto/account-create-email.input.ts__tmpl__ +9 -0
  11. package/src/account/files/data-access/src/lib/dto/account-update-password.input.ts__tmpl__ +16 -0
  12. package/src/account/files/data-access/src/lib/dto/account-update-profile.input.ts__tmpl__ +25 -0
  13. package/src/account/files/feature/src/index.ts__tmpl__ +1 -0
  14. package/src/account/files/feature/src/lib/api-account-feature.module.ts__tmpl__ +9 -0
  15. package/src/account/files/feature/src/lib/api-account-feature.resolver.ts__tmpl__ +83 -0
  16. package/src/account/generator.spec.ts +71 -0
  17. package/src/account/generator.ts +20 -0
  18. package/src/account/schema.d.ts +3 -0
  19. package/src/account/schema.json +13 -0
  20. package/src/app/files/src/app.config.ts__tmpl__ +66 -0
  21. package/src/app/files/src/app.module.ts__tmpl__ +43 -0
  22. package/src/app/files/src/applogger.middleware.ts__tmpl__ +21 -0
  23. package/src/app/files/src/main.ts__tmpl__ +33 -0
  24. package/src/app/files/webpack.config.js__tmpl__ +54 -0
  25. package/src/app/generator.spec.ts +112 -0
  26. package/src/app/generator.ts +105 -0
  27. package/src/app/schema.d.ts +1 -0
  28. package/src/app/schema.json +9 -0
  29. package/src/config/files/src/index.ts__tmpl__ +3 -0
  30. package/src/config/files/src/lib/config.service.ts__tmpl__ +51 -0
  31. package/src/config/files/src/lib/configuration.ts__tmpl__ +32 -0
  32. package/src/config/files/src/lib/validation.ts__tmpl__ +21 -0
  33. package/src/config/generator.spec.ts +47 -0
  34. package/src/config/generator.ts +16 -0
  35. package/src/config/schema.d.ts +3 -0
  36. package/src/config/schema.json +13 -0
  37. package/src/core/files/data-access/src/index.ts__tmpl__ +5 -0
  38. package/src/core/files/data-access/src/lib/api-core-data-access.module.ts__tmpl__ +9 -0
  39. package/src/core/files/data-access/src/lib/api-core-data-access.service.ts__tmpl__ +97 -0
  40. package/src/core/files/data-access/src/lib/api-core-pub-sub.ts__tmpl__ +37 -0
  41. package/src/core/files/data-access/src/lib/dto/core-paging.input.ts__tmpl__ +26 -0
  42. package/src/core/files/data-access/src/lib/dto/multi-select-input.ts__tmpl__ +7 -0
  43. package/src/core/files/data-access/src/lib/models/core-paging.ts__tmpl__ +19 -0
  44. package/src/core/files/feature/src/index.ts__tmpl__ +2 -0
  45. package/src/core/files/feature/src/lib/api-core-feature.controller.ts__tmpl__ +12 -0
  46. package/src/core/files/feature/src/lib/api-core-feature.module.ts__tmpl__ +86 -0
  47. package/src/core/files/feature/src/lib/api-core-feature.resolver.ts__tmpl__ +12 -0
  48. package/src/core/files/feature/src/lib/api-core-feature.service.ts__tmpl__ +55 -0
  49. package/src/core/files/feature/src/lib/config/configuration.ts__tmpl__ +32 -0
  50. package/src/core/files/feature/src/lib/config/validation.ts__tmpl__ +25 -0
  51. package/src/core/files/feature/src/lib/plugins/complexity.plugin.ts__tmpl__ +51 -0
  52. package/src/core/files/feature/src/lib/plugins/logging.plugin.ts__tmpl__ +17 -0
  53. package/src/core/files/models/src/index.ts__tmpl__ +1 -0
  54. package/src/core/files/models/src/lib/generate-models.ts__tmpl__ +294 -0
  55. package/src/core/files/models/src/lib/models/core-paging.model.ts__tmpl__ +25 -0
  56. package/src/core/generator.spec.ts +85 -0
  57. package/src/core/generator.ts +35 -0
  58. package/src/core/schema.d.ts +3 -0
  59. package/src/core/schema.json +13 -0
  60. package/src/custom/generator.spec.ts +75 -0
  61. package/src/custom/generator.ts +239 -0
  62. package/src/custom/schema.json +21 -0
  63. package/src/custom/schema.ts +5 -0
  64. package/src/extended/generator.spec.ts +95 -0
  65. package/src/extended/generator.ts +161 -0
  66. package/src/extended/index.ts +1 -0
  67. package/src/extended/schema.json +12 -0
  68. package/src/extended/schema.ts +3 -0
  69. package/src/generate-crud/files/data-access/src/index.ts__tmpl__ +3 -0
  70. package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.module.ts__tmpl__ +11 -0
  71. package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.service.ts__tmpl__ +72 -0
  72. package/src/generate-crud/files/data-access/src/lib/dto/index.ts__tmpl__ +224 -0
  73. package/src/generate-crud/files/feature/.gitkeep +0 -0
  74. package/src/generate-crud/generator.spec.ts +84 -0
  75. package/src/generate-crud/generator.ts +354 -0
  76. package/src/generate-crud/schema.json +32 -0
  77. package/src/generate-crud/schema.ts +8 -0
  78. package/src/index.ts +13 -0
  79. package/src/plugin/generator.spec.ts +18 -0
  80. package/src/plugin/generator.ts +74 -0
  81. package/src/plugin/schema.json +14 -0
  82. package/src/plugin/schema.ts +4 -0
  83. package/src/prisma/files/src/index.ts__tmpl__ +1 -0
  84. package/src/prisma/files/src/lib/.gitkeep +1 -0
  85. package/src/prisma/files/src/lib/schemas/schema.prisma__tmpl__ +402 -0
  86. package/src/prisma/files/src/lib/seed/seed-data/iso-3166-countries.ts__tmpl__ +3239 -0
  87. package/src/prisma/files/src/lib/seed/seed-data/seed-users.ts__tmpl__ +32 -0
  88. package/src/prisma/files/src/lib/seed/seed.ts__tmpl__ +64 -0
  89. package/src/prisma/generator.spec.ts +60 -0
  90. package/src/prisma/generator.ts +61 -0
  91. package/src/prisma/schema.d.ts +3 -0
  92. package/src/prisma/schema.json +13 -0
  93. package/src/setup/generator.md +49 -0
  94. package/src/setup/generator.spec.ts +18 -0
  95. package/src/setup/generator.ts +106 -0
  96. package/src/setup/schema.json +8 -0
  97. package/src/smtp-mailer/files/data-access/src/index.ts__tmpl__ +2 -0
  98. package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.module.ts__tmpl__ +10 -0
  99. package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.service.ts__tmpl__ +61 -0
  100. package/src/smtp-mailer/generator.spec.ts +41 -0
  101. package/src/smtp-mailer/generator.ts +14 -0
  102. package/src/smtp-mailer/schema.d.ts +0 -0
  103. package/src/smtp-mailer/schema.json +7 -0
  104. package/src/user/files/data-access/src/index.ts__tmpl__ +5 -0
  105. package/src/user/files/data-access/src/lib/api-user-data-access.module.ts__tmpl__ +10 -0
  106. package/src/user/files/data-access/src/lib/api-user-data-access.service.ts__tmpl__ +119 -0
  107. package/src/user/files/data-access/src/lib/dto/admin-create-user.input.ts__tmpl__ +20 -0
  108. package/src/user/files/data-access/src/lib/dto/admin-update-user.input.ts__tmpl__ +29 -0
  109. package/src/user/files/feature/src/index.ts__tmpl__ +1 -0
  110. package/src/user/files/feature/src/lib/api-user-feature-admin.resolver.ts__tmpl__ +57 -0
  111. package/src/user/files/feature/src/lib/api-user-feature.module.ts__tmpl__ +10 -0
  112. package/src/user/files/feature/src/lib/api-user-feature.resolver.ts__tmpl__ +17 -0
  113. package/src/user/generator.spec.ts +41 -0
  114. package/src/user/generator.ts +15 -0
  115. package/src/user/schema.d.ts +0 -0
  116. package/src/user/schema.json +7 -0
  117. package/src/utils/files/src/index.ts__tmpl__ +3 -0
  118. package/src/utils/files/src/lib/decorators/ctx-user.decorator.ts__tmpl__ +6 -0
  119. package/src/utils/files/src/lib/guards/gql-auth-admin.guard.ts__tmpl__ +39 -0
  120. package/src/utils/files/src/lib/guards/gql-auth.guard.ts__tmpl__ +11 -0
  121. package/src/utils/generator.ts +14 -0
  122. package/src/utils/schema.json +8 -0
  123. package/src/workspace-setup/generator.md +39 -0
  124. package/src/workspace-setup/generator.spec.ts +82 -0
  125. package/src/workspace-setup/generator.ts +49 -0
  126. package/src/workspace-setup/lib/helpers.ts +142 -0
  127. package/src/workspace-setup/schema.d.ts +3 -0
  128. package/src/workspace-setup/schema.json +7 -0
  129. package/tsconfig.json +16 -0
  130. package/tsconfig.lib.json +23 -0
  131. package/tsconfig.spec.json +22 -0
  132. package/vite.config.mts +37 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## 0.0.1 (2025-06-17)
2
+
3
+ ### 🧱 Updated Dependencies
4
+
5
+ - Updated utils to 0.0.1
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # api
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build api` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test api` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -0,0 +1,28 @@
1
+ const baseConfig = require('../../eslint.config.js')
2
+
3
+ module.exports = [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}', '{projectRoot}/vite.config.{js,ts,mjs,mts}'],
12
+ },
13
+ ],
14
+ },
15
+ languageOptions: {
16
+ parser: require('jsonc-eslint-parser'),
17
+ },
18
+ },
19
+ {
20
+ files: ['**/package.json'],
21
+ rules: {
22
+ '@nx/nx-plugin-checks': 'error',
23
+ },
24
+ languageOptions: {
25
+ parser: require('jsonc-eslint-parser'),
26
+ },
27
+ },
28
+ ]
@@ -0,0 +1,69 @@
1
+ {
2
+ "generators": {
3
+ "setup": {
4
+ "factory": "./src/setup/generator.js",
5
+ "schema": "./src/setup/schema.json",
6
+ "description": "Set up all dependencies for the API"
7
+ },
8
+ "app": {
9
+ "factory": "./src/app/generator.js",
10
+ "schema": "./src/app/schema.json",
11
+ "description": "Set up main Nest.js application"
12
+ },
13
+ "prisma": {
14
+ "factory": "./src/prisma/generator.js",
15
+ "schema": "./src/prisma/schema.json",
16
+ "description": "Create the API Prisma library"
17
+ },
18
+ "config": {
19
+ "factory": "./src/config/generator.js",
20
+ "schema": "./src/config/schema.json",
21
+ "description": "Create the API Config library"
22
+ },
23
+ "account": {
24
+ "factory": "./src/account/generator.js",
25
+ "schema": "./src/account/schema.json",
26
+ "description": "Create an API Account Library"
27
+ },
28
+ "core": {
29
+ "factory": "./src/core/generator.js",
30
+ "schema": "./src/core/schema.json",
31
+ "description": "Create the API Core library"
32
+ },
33
+ "custom": {
34
+ "factory": "./src/custom/generator.js",
35
+ "schema": "./src/custom/schema.json",
36
+ "description": "Generate custom API libraries based on Prisma models"
37
+ },
38
+ "generate-crud": {
39
+ "factory": "./src/generate-crud/generator.js",
40
+ "schema": "./src/generate-crud/schema.json",
41
+ "description": "Generate CRUD libraries based on Prisma models"
42
+ },
43
+ "smtp-mailer": {
44
+ "factory": "./src/smtp-mailer/generator.js",
45
+ "schema": "./src/smtp-mailer/schema.json",
46
+ "description": "Create an API Mailer Library"
47
+ },
48
+ "user": {
49
+ "factory": "./src/user/generator.js",
50
+ "schema": "./src/user/schema.json",
51
+ "description": "Create an API User Library"
52
+ },
53
+ "workspace-setup": {
54
+ "factory": "./src/workspace-setup/generator.js",
55
+ "schema": "./src/workspace-setup/schema.json",
56
+ "description": "Set up the workspace environment"
57
+ },
58
+ "plugin": {
59
+ "factory": "./src/plugin/generator.js",
60
+ "schema": "./src/plugin/schema.json",
61
+ "description": "Generate a blank plugin (module, service, resolver) in the custom library's plugins folder."
62
+ },
63
+ "utils": {
64
+ "factory": "./src/utils/generator.js",
65
+ "schema": "./src/utils/schema.json",
66
+ "description": "Generate a utility plugin with auth utils."
67
+ }
68
+ }
69
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@nestledjs/api",
3
+ "version": "0.0.1",
4
+ "generators": "./generators.json",
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "description": "API generators for Nestled projects",
9
+ "author": "Nestled Contributors",
10
+ "license": "MIT",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/nestledjs/nestled"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "dependencies": {
19
+ "tslib": "^2.3.0"
20
+ }
21
+ }
package/project.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "api",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "generators/api/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "publishable": true,
8
+ "targets": {
9
+ "build": {
10
+ "executor": "@nx/js:tsc",
11
+ "outputs": ["{options.outputPath}"],
12
+ "options": {
13
+ "outputPath": "dist/generators/api",
14
+ "main": "generators/api/src/index.ts",
15
+ "tsConfig": "generators/api/tsconfig.lib.json",
16
+ "assets": [
17
+ "generators/api/*.md",
18
+ {
19
+ "input": "./generators/api/src",
20
+ "glob": "**/!(*.ts)",
21
+ "output": "./src"
22
+ },
23
+ {
24
+ "input": "./generators/api/src",
25
+ "glob": "**/*.d.ts",
26
+ "output": "./src"
27
+ },
28
+ {
29
+ "input": "./generators/api",
30
+ "glob": "generators.json",
31
+ "output": "."
32
+ },
33
+ {
34
+ "input": "./generators/api",
35
+ "glob": "executors.json",
36
+ "output": "."
37
+ },
38
+ {
39
+ "input": "./generators/api/src",
40
+ "glob": "*/files/**/*",
41
+ "output": "./src"
42
+ }
43
+ ]
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,5 @@
1
+ export * from './lib/api-account-data-access.module'
2
+ export * from './lib/api-account-data-access.service'
3
+ export * from './lib/dto/account-create-email.input'
4
+ export * from './lib/dto/account-update-password.input'
5
+ export * from './lib/dto/account-update-profile.input'
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common'
2
+ import { ApiCoreDataAccessModule } from '@<%= npmScope %>/api/core/data-access'
3
+ import { ApiAccountDataAccessService } from './api-account-data-access.service'
4
+
5
+ @Module({
6
+ imports: [ApiCoreDataAccessModule],
7
+ providers: [ApiAccountDataAccessService],
8
+ exports: [ApiAccountDataAccessService],
9
+ })
10
+ export class ApiAccountDataAccessModule {}
@@ -0,0 +1,152 @@
1
+ import {
2
+ BadRequestException,
3
+ Injectable,
4
+ Logger,
5
+ UnauthorizedException,
6
+ } from '@nestjs/common'
7
+ import { ApiCoreDataAccessService, Prisma } from '@<%= npmScope %>/api/core/data-access'
8
+ import {
9
+ hashPassword,
10
+ validatePassword,
11
+ } from '@<%= npmScope %>/api/auth/data-access'
12
+ import { AccountCreateEmailInput } from './dto/account-create-email.input'
13
+ import { AccountUpdatePasswordInput } from './dto/account-update-password.input'
14
+ import { AccountUpdateProfileInput } from './dto/account-update-profile.input'
15
+
16
+ @Injectable()
17
+ export class ApiAccountDataAccessService {
18
+ constructor(private readonly data: ApiCoreDataAccessService) {}
19
+
20
+ accountEmails(userId: string) {
21
+ return this.data.email.findMany({
22
+ where: { userId },
23
+ orderBy: { primary: 'desc' },
24
+ })
25
+ }
26
+
27
+ accountProfile(userId: string) {
28
+ return this.data.findUserById(userId)
29
+ }
30
+
31
+ async accountCreateEmail(userId: string, input: AccountCreateEmailInput) {
32
+ const exists = await this.data.findUserByEmail(input.email)
33
+ if (exists) {
34
+ throw new BadRequestException(`Can't add email address ${input.email}`)
35
+ }
36
+ return this.data.email.create({
37
+ data: { userId, email: input.email, primary: false },
38
+ })
39
+ }
40
+
41
+ async accountDeleteEmail(userId: string, userEmailId: string) {
42
+ const email = await this.data.email.findUnique({
43
+ where: { id: userEmailId },
44
+ include: { user: true },
45
+ })
46
+ if (!email || !email.user) {
47
+ throw new BadRequestException(`Email not found`)
48
+ }
49
+ if (email.user.id !== userId) {
50
+ throw new UnauthorizedException()
51
+ }
52
+ if (email.primary) {
53
+ throw new BadRequestException(`You can't delete your primary email`)
54
+ }
55
+ return this.data.email.delete({ where: { id: userEmailId } })
56
+ }
57
+
58
+ accountUpdateProfile(userId: string, input: AccountUpdateProfileInput) {
59
+ return this.data.user.update({
60
+ where: { id: userId },
61
+ data: {
62
+ firstName: input.firstName,
63
+ lastName: input.lastName,
64
+ bio: input.bio,
65
+ },
66
+ })
67
+ }
68
+
69
+ async accountUpdateDisplayName(userId: string, displayName: string) {
70
+ const available = await this.accountDisplayNameAvailable(userId, displayName)
71
+
72
+ if (!available) {
73
+ throw new BadRequestException(`Display name ${displayName} is not available`)
74
+ }
75
+
76
+ return this.data.user.update({ where: { id: userId }, data: { displayName } })
77
+ }
78
+
79
+ async accountDisplayNameAvailable(userId: string, displayName: string): Promise<boolean> {
80
+ const denied = await this.data.displayNameDenyList(displayName)
81
+
82
+ if (denied) {
83
+ throw new BadRequestException(`Display name ${displayName} is not available`)
84
+ }
85
+
86
+ const count = await this.data.user.count({
87
+ where: { displayName },
88
+ })
89
+ return count === 0
90
+ }
91
+
92
+ async userEmailOwner(userId: string, userEmailId: string) {
93
+ const email = await this.data.email.findUnique({
94
+ where: { id: userEmailId },
95
+ include: { user: true }
96
+ })
97
+ if (!email || !email.user) {
98
+ throw new BadRequestException(`Email not found`)
99
+ }
100
+ if (email.user.id !== userId) {
101
+ throw new UnauthorizedException()
102
+ }
103
+ return email.user
104
+ }
105
+
106
+ async updateUserEmail(userId: string, userEmailId: string, input: Prisma.EmailUpdateInput) {
107
+ await this.userEmailOwner(userId, userEmailId)
108
+ return this.data.email.update({ where: { id: userEmailId }, data: { ...input } })
109
+ }
110
+
111
+ async accountMarkEmailPrivate(userId: string, userEmailId: string) {
112
+ return this.updateUserEmail(userId, userEmailId, { public: false })
113
+ }
114
+
115
+ async accountMarkEmailPublic(userId: string, userEmailId: string) {
116
+ return this.updateUserEmail(userId, userEmailId, { public: true })
117
+ }
118
+
119
+ async accountMarkEmailPrimary(userId: string, userEmailId: string) {
120
+ await this.userEmailOwner(userId, userEmailId)
121
+ await this.data.email.updateMany({
122
+ where: { userId },
123
+ data: { primary: false },
124
+ })
125
+ return this.updateUserEmail(userId, userEmailId, { primary: true })
126
+ }
127
+
128
+ async accountResetPassword(userId: string) {
129
+ const emails = await this.accountEmails(userId)
130
+ const primary = emails.find((email) => email.primary)
131
+
132
+ if (!primary) {
133
+ throw new BadRequestException(`Could not find primary email for user with id ${userId}`)
134
+ }
135
+ Logger.verbose(`TODO: Implement Account Reset Password`)
136
+ return true
137
+ }
138
+
139
+ async accountUpdatePassword(userId: string, input: AccountUpdatePasswordInput) {
140
+ const user = await this.data.findUserById(userId)
141
+ if (!user || !user.password) {
142
+ throw new BadRequestException(`User not found or password not set`)
143
+ }
144
+ await validatePassword(input.currentPassword, user.password)
145
+
146
+ if (input.password !== input.verified) {
147
+ throw new BadRequestException(`Passwords do not match!`)
148
+ }
149
+ await this.data.user.update({ where: { id: userId }, data: { password: hashPassword(input.password) } })
150
+ return true
151
+ }
152
+ }
@@ -0,0 +1,9 @@
1
+ import { Field, InputType } from '@nestjs/graphql'
2
+ import { IsEmail } from 'class-validator'
3
+
4
+ @InputType()
5
+ export class AccountCreateEmailInput {
6
+ @Field()
7
+ @IsEmail()
8
+ email: string
9
+ }
@@ -0,0 +1,16 @@
1
+ import { Field, InputType } from '@nestjs/graphql'
2
+ import { MinLength } from 'class-validator'
3
+
4
+ @InputType()
5
+ export class AccountUpdatePasswordInput {
6
+ @Field()
7
+ currentPassword?: string
8
+
9
+ @Field()
10
+ @MinLength(10)
11
+ password?: string
12
+
13
+ @Field()
14
+ @MinLength(10)
15
+ verified?: string
16
+ }
@@ -0,0 +1,25 @@
1
+ import { Field, InputType } from '@nestjs/graphql'
2
+
3
+ @InputType()
4
+ export class AccountUpdateProfileInput {
5
+ @Field({ nullable: true })
6
+ firstName?: string
7
+
8
+ @Field({ nullable: true })
9
+ lastName?: string
10
+
11
+ @Field({ nullable: true })
12
+ avatarUrl?: string
13
+
14
+ @Field({ nullable: true })
15
+ bio?: string
16
+
17
+ @Field({ nullable: true })
18
+ location?: string
19
+
20
+ @Field({ nullable: true })
21
+ phone?: string
22
+
23
+ @Field({ nullable: true })
24
+ dob?: Date
25
+ }
@@ -0,0 +1 @@
1
+ export * from './lib/api-account-feature.module'
@@ -0,0 +1,9 @@
1
+ import { Module } from '@nestjs/common'
2
+ import { ApiAccountDataAccessModule } from '@<%= npmScope %>/api/account/data-access'
3
+ import { ApiAccountFeatureResolver } from './api-account-feature.resolver'
4
+
5
+ @Module({
6
+ imports: [ApiAccountDataAccessModule],
7
+ providers: [ApiAccountFeatureResolver],
8
+ })
9
+ export class ApiAccountFeatureModule {}
@@ -0,0 +1,83 @@
1
+ import { UseGuards, ValidationPipe } from '@nestjs/common'
2
+ import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'
3
+ import {
4
+ AccountCreateEmailInput,
5
+ AccountUpdatePasswordInput,
6
+ AccountUpdateProfileInput,
7
+ ApiAccountDataAccessService,
8
+ } from '@<%= npmScope %>/api/account/data-access'
9
+ import { CtxUser } from '@<%= npmScope %>/api/custom'
10
+ import { GqlAuthGuard} from '@<%= npmScope %>/api/core/data-access'
11
+ import { User, Email } from '@<%= npmScope %>/api/core/models'
12
+
13
+ @Resolver()
14
+ @UseGuards(GqlAuthGuard)
15
+ export class ApiAccountFeatureResolver {
16
+ constructor(private readonly service: ApiAccountDataAccessService) {}
17
+
18
+ @Query(() => [Email], { nullable: true })
19
+ accountEmails(@CtxUser() user: User) {
20
+ return this.service.accountEmails(user.id)
21
+ }
22
+
23
+ @Query(() => User, { nullable: true })
24
+ accountProfile(@CtxUser() user: User) {
25
+ return this.service.accountProfile(user.id)
26
+ }
27
+
28
+ @Query(() => Boolean)
29
+ accountDisplayNameAvailable(
30
+ @CtxUser() user: User,
31
+ @Args('displayName') displayName: string
32
+ ) {
33
+ return this.service.accountDisplayNameAvailable(user.id, displayName)
34
+ }
35
+
36
+ @Mutation(() => Email, { nullable: true })
37
+ accountCreateEmail(@CtxUser() user: User, @Args('input', new ValidationPipe()) input: AccountCreateEmailInput) {
38
+ return this.service.accountCreateEmail(user.id, input)
39
+ }
40
+
41
+ @Mutation(() => Email, { nullable: true })
42
+ accountDeleteEmail(@CtxUser() user: User, @Args('emailId') emailId: string) {
43
+ return this.service.accountDeleteEmail(user.id, emailId)
44
+ }
45
+
46
+ @Mutation(() => Email, { nullable: true })
47
+ accountMarkEmailPrimary(@CtxUser() user: User, @Args('emailId') emailId: string) {
48
+ return this.service.accountMarkEmailPrimary(user.id, emailId)
49
+ }
50
+
51
+ @Mutation(() => Email, { nullable: true })
52
+ accountMarkEmailPrivate(@CtxUser() user: User, @Args('emailId') emailId: string) {
53
+ return this.service.accountMarkEmailPrivate(user.id, emailId)
54
+ }
55
+
56
+ @Mutation(() => Email, { nullable: true })
57
+ accountMarkEmailPublic(@CtxUser() user: User, @Args('emailId') emailId: string) {
58
+ return this.service.accountMarkEmailPublic(user.id, emailId)
59
+ }
60
+
61
+ @Mutation(() => User, { nullable: true })
62
+ accountUpdateProfile(@CtxUser() user: User, @Args('input', new ValidationPipe()) input: AccountUpdateProfileInput) {
63
+ return this.service.accountUpdateProfile(user.id, input)
64
+ }
65
+
66
+ @Mutation(() => User)
67
+ accountUpdateDisplayName(
68
+ @CtxUser() user: User,
69
+ @Args('displayName') displayName: string
70
+ ) {
71
+ return this.service.accountUpdateDisplayName(user.id, displayName)
72
+ }
73
+
74
+ @Mutation(() => Boolean, { nullable: true })
75
+ accountResetPassword(@CtxUser() user: User) {
76
+ return this.service.accountResetPassword(user.id)
77
+ }
78
+
79
+ @Mutation(() => Boolean, { nullable: true })
80
+ accountUpdatePassword(@CtxUser() user: User, @Args('input', new ValidationPipe()) input: AccountUpdatePasswordInput) {
81
+ return this.service.accountUpdatePassword(user.id, input)
82
+ }
83
+ }
@@ -0,0 +1,71 @@
1
+ import { formatFiles, installPackagesTask, Tree } from '@nx/devkit'
2
+ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import { apiLibraryGenerator } from '@nestledjs/utils'
5
+
6
+ import generator from './generator'
7
+
8
+ vi.mock('@nestledjs/utils', async () => {
9
+ const actual = await vi.importActual('@nestledjs/utils')
10
+ return {
11
+ ...actual,
12
+ apiLibraryGenerator: vi.fn(),
13
+ }
14
+ })
15
+
16
+ vi.mock('@nx/devkit', async () => {
17
+ const actual = await vi.importActual('@nx/devkit')
18
+ return {
19
+ ...actual,
20
+ formatFiles: vi.fn(),
21
+ installPackagesTask: vi.fn(),
22
+ }
23
+ })
24
+
25
+ describe('account generator', () => {
26
+ let tree: Tree
27
+
28
+ beforeEach(() => {
29
+ tree = createTreeWithEmptyWorkspace()
30
+ })
31
+
32
+ it('should run successfully', async () => {
33
+ const callback = await generator(tree)
34
+ callback()
35
+
36
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(
37
+ tree,
38
+ { name: 'account', overwrite: false },
39
+ expect.any(String),
40
+ 'data-access',
41
+ )
42
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(
43
+ tree,
44
+ { name: 'account', overwrite: false },
45
+ expect.any(String),
46
+ 'feature',
47
+ true,
48
+ )
49
+ expect(formatFiles).toHaveBeenCalledWith(tree)
50
+ expect(installPackagesTask).toHaveBeenCalledWith(tree)
51
+ })
52
+
53
+ it('should pass overwrite flag to apiLibraryGenerator when overwrite is true', async () => {
54
+ const callback = await generator(tree, { overwrite: true })
55
+ callback()
56
+
57
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(
58
+ tree,
59
+ { name: 'account', overwrite: true },
60
+ expect.any(String),
61
+ 'data-access',
62
+ )
63
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(
64
+ tree,
65
+ { name: 'account', overwrite: true },
66
+ expect.any(String),
67
+ 'feature',
68
+ true,
69
+ )
70
+ })
71
+ })
@@ -0,0 +1,20 @@
1
+ import { formatFiles, GeneratorCallback, installPackagesTask, joinPathFragments, Tree } from '@nx/devkit'
2
+ import { apiLibraryGenerator } from '@nestledjs/utils'
3
+ import { ApiAccountGeneratorSchema } from './schema'
4
+
5
+ export default async function generateLibraries(
6
+ tree: Tree,
7
+ options: ApiAccountGeneratorSchema = {},
8
+ ): Promise<GeneratorCallback> {
9
+ const templateRootPath = joinPathFragments(__dirname, './files')
10
+ const overwrite = options.overwrite === true
11
+
12
+ await apiLibraryGenerator(tree, { name: 'account', overwrite }, templateRootPath, 'data-access')
13
+ await apiLibraryGenerator(tree, { name: 'account', overwrite }, templateRootPath, 'feature', true)
14
+
15
+ await formatFiles(tree)
16
+
17
+ return () => {
18
+ installPackagesTask(tree)
19
+ }
20
+ }
@@ -0,0 +1,3 @@
1
+ export interface ApiAccountGeneratorSchema {
2
+ overwrite?: boolean
3
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "ApiAccount",
4
+ "title": "Create an API Account Library",
5
+ "type": "object",
6
+ "properties": {
7
+ "overwrite": {
8
+ "type": "boolean",
9
+ "description": "Whether to overwrite existing libraries if they exist.",
10
+ "default": false
11
+ }
12
+ }
13
+ }