@contember/engine-tenant-api 1.4.0-rc.4 → 1.4.0-rc.6

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 (39) hide show
  1. package/dist/src/model/authorization/PermissionActions.d.ts +5 -0
  2. package/dist/src/model/authorization/PermissionActions.d.ts.map +1 -1
  3. package/dist/src/model/authorization/PermissionActions.js +1 -0
  4. package/dist/src/model/authorization/PermissionActions.js.map +1 -1
  5. package/dist/src/model/service/PersonManager.d.ts +3 -4
  6. package/dist/src/model/service/PersonManager.d.ts.map +1 -1
  7. package/dist/src/model/service/PersonManager.js +1 -1
  8. package/dist/src/model/service/PersonManager.js.map +1 -1
  9. package/dist/src/resolvers/ResolverFactory.d.ts.map +1 -1
  10. package/dist/src/resolvers/ResolverFactory.js +1 -0
  11. package/dist/src/resolvers/ResolverFactory.js.map +1 -1
  12. package/dist/src/resolvers/mutation/person/ChangeProfileMutationResolver.d.ts +3 -2
  13. package/dist/src/resolvers/mutation/person/ChangeProfileMutationResolver.d.ts.map +1 -1
  14. package/dist/src/resolvers/mutation/person/ChangeProfileMutationResolver.js +23 -1
  15. package/dist/src/resolvers/mutation/person/ChangeProfileMutationResolver.js.map +1 -1
  16. package/dist/src/schema/index.d.ts +35 -0
  17. package/dist/src/schema/index.d.ts.map +1 -1
  18. package/dist/src/schema/tenant.graphql.d.ts.map +1 -1
  19. package/dist/src/schema/tenant.graphql.js +22 -1
  20. package/dist/src/schema/tenant.graphql.js.map +1 -1
  21. package/dist/src/tsconfig.tsbuildinfo +1 -1
  22. package/dist/tests/cases/integration/mocked/changeProfileMutation.test.d.ts +2 -0
  23. package/dist/tests/cases/integration/mocked/changeProfileMutation.test.d.ts.map +1 -0
  24. package/dist/tests/cases/integration/mocked/changeProfileMutation.test.js +36 -0
  25. package/dist/tests/cases/integration/mocked/changeProfileMutation.test.js.map +1 -0
  26. package/dist/tests/cases/integration/mocked/gql/changeProfile.d.ts +7 -0
  27. package/dist/tests/cases/integration/mocked/gql/changeProfile.d.ts.map +1 -0
  28. package/dist/tests/cases/integration/mocked/gql/changeProfile.js +17 -0
  29. package/dist/tests/cases/integration/mocked/gql/changeProfile.js.map +1 -0
  30. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +12 -12
  32. package/src/model/authorization/PermissionActions.ts +1 -0
  33. package/src/model/service/PersonManager.ts +3 -4
  34. package/src/resolvers/ResolverFactory.ts +1 -0
  35. package/src/resolvers/mutation/person/ChangeProfileMutationResolver.ts +42 -4
  36. package/src/schema/index.ts +45 -0
  37. package/src/schema/tenant.graphql.ts +22 -1
  38. package/tests/cases/integration/mocked/changeProfileMutation.test.ts +37 -0
  39. package/tests/cases/integration/mocked/gql/changeProfile.ts +14 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/engine-tenant-api",
3
- "version": "1.4.0-rc.4",
3
+ "version": "1.4.0-rc.6",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -13,16 +13,16 @@
13
13
  "test": "vitest --dir ./tests/cases --no-threads"
14
14
  },
15
15
  "dependencies": {
16
- "@contember/authorization": "1.4.0-rc.4",
17
- "@contember/config-loader": "1.4.0-rc.4",
18
- "@contember/database": "1.4.0-rc.4",
19
- "@contember/database-migrations": "1.4.0-rc.4",
20
- "@contember/dic": "1.4.0-rc.4",
21
- "@contember/graphql-utils": "1.4.0-rc.4",
22
- "@contember/logger": "1.4.0-rc.4",
23
- "@contember/schema": "1.4.0-rc.4",
24
- "@contember/schema-utils": "1.4.0-rc.4",
25
- "@contember/typesafe": "1.4.0-rc.4",
16
+ "@contember/authorization": "1.4.0-rc.6",
17
+ "@contember/config-loader": "1.4.0-rc.6",
18
+ "@contember/database": "1.4.0-rc.6",
19
+ "@contember/database-migrations": "1.4.0-rc.6",
20
+ "@contember/dic": "1.4.0-rc.6",
21
+ "@contember/graphql-utils": "1.4.0-rc.6",
22
+ "@contember/logger": "1.4.0-rc.6",
23
+ "@contember/schema": "1.4.0-rc.6",
24
+ "@contember/schema-utils": "1.4.0-rc.6",
25
+ "@contember/typesafe": "1.4.0-rc.6",
26
26
  "@graphql-tools/utils": "^10.0.8",
27
27
  "graphql-tag": "^2.12.6",
28
28
  "jose": "^5.1.1",
@@ -32,7 +32,7 @@
32
32
  "otpauth": "^9.1.5"
33
33
  },
34
34
  "devDependencies": {
35
- "@contember/database-tester": "1.4.0-rc.4",
35
+ "@contember/database-tester": "1.4.0-rc.6",
36
36
  "@graphql-codegen/cli": "^5.0.0",
37
37
  "@graphql-codegen/typescript": "^4.0.1",
38
38
  "@graphql-codegen/typescript-operations": "^4.0.1",
@@ -24,6 +24,7 @@ namespace PermissionActions {
24
24
  export const PERSON_SIGN_UP = (roles?: readonly string[]) => Authorizator.createAction(Resources.person, 'signUp', { roles })
25
25
  export const PERSON_SIGN_OUT = Authorizator.createAction(Resources.person, 'signOut')
26
26
  export const PERSON_SETUP_OTP = Authorizator.createAction(Resources.person, 'setupOtp')
27
+ export const PERSON_CHANGE_PROFILE = Authorizator.createAction(Resources.person, 'changeProfile')
27
28
  export const PERSON_CHANGE_MY_PROFILE = Authorizator.createAction(Resources.person, 'changeMyProfile')
28
29
  export const PERSON_CHANGE_PASSWORD = Authorizator.createAction(Resources.person, 'changePassword')
29
30
  export const PERSON_CHANGE_MY_PASSWORD = Authorizator.createAction(Resources.person, 'changeMyPassword')
@@ -1,9 +1,8 @@
1
- import { ChangeMyProfileErrorCode } from '../../schema'
2
1
  import { ChangeProfileCommand, ChangeProfileData } from '../commands/person/ChangeProfileCommand'
3
2
  import { Response, ResponseError, ResponseOk } from '../utils/Response'
4
3
  import { PersonQuery, PersonRow } from '../queries'
5
4
  import { DatabaseContext } from '../utils'
6
- import { EmailValidator } from './EmailValidator'
5
+ import { EmailValidator, EmailValidatorError } from './EmailValidator'
7
6
 
8
7
  class PersonManager {
9
8
  constructor(
@@ -17,7 +16,7 @@ class PersonManager {
17
16
  )
18
17
  }
19
18
 
20
- async changeMyProfile(dbContext: DatabaseContext, person: PersonRow, data: ChangeProfileData): Promise<PersonManager.ProfileChangeResponse> {
19
+ async changeProfile(dbContext: DatabaseContext, person: PersonRow, data: ChangeProfileData): Promise<PersonManager.ProfileChangeResponse> {
21
20
  if (data.email !== undefined && person.email !== data.email) {
22
21
  const validationError = await this.emailValidator.validateEmail(dbContext, data.email)
23
22
  if (validationError !== null) {
@@ -31,7 +30,7 @@ class PersonManager {
31
30
  }
32
31
 
33
32
  namespace PersonManager {
34
- export type ProfileChangeResponse = Response<null, ChangeMyProfileErrorCode>
33
+ export type ProfileChangeResponse = Response<null, EmailValidatorError>
35
34
  }
36
35
 
37
36
  export { PersonManager }
@@ -112,6 +112,7 @@ class ResolverFactory {
112
112
  createSessionToken: this.resolvers.signInMutationResolver.createSessionToken.bind(this.resolvers.signInMutationResolver),
113
113
  signOut: this.resolvers.signOutMutationResolver.signOut.bind(this.resolvers.signOutMutationResolver),
114
114
 
115
+ changeProfile: this.resolvers.changeProfileMutationResolver.changeProfile.bind(this.resolvers.changeProfileMutationResolver),
115
116
  changeMyProfile: this.resolvers.changeProfileMutationResolver.changeMyProfile.bind(this.resolvers.changeProfileMutationResolver),
116
117
  changePassword: this.resolvers.changePasswordMutationResolver.changePassword.bind(this.resolvers.changePasswordMutationResolver),
117
118
  changeMyPassword: this.resolvers.changePasswordMutationResolver.changeMyPassword.bind(this.resolvers.changePasswordMutationResolver),
@@ -1,14 +1,52 @@
1
- import { ChangeMyProfileResponse, MutationChangeMyProfileArgs, MutationResolvers } from '../../../schema'
1
+ import {
2
+ ChangeMyProfileResponse,
3
+ ChangeProfileResponse,
4
+ MutationChangeMyProfileArgs,
5
+ MutationChangeProfileArgs,
6
+ MutationResolvers,
7
+ } from '../../../schema'
2
8
  import { GraphQLResolveInfo } from 'graphql'
3
9
  import { TenantResolverContext } from '../../TenantResolverContext'
4
- import { PermissionActions, PersonManager, PersonQuery } from '../../../model'
10
+ import { IdentityScope, PermissionActions, PersonManager, PersonQuery } from '../../../model'
5
11
  import { createErrorResponse } from '../../errorUtils'
6
12
 
7
- export class ChangeProfileMutationResolver implements MutationResolvers {
13
+ export class ChangeProfileMutationResolver implements Pick<MutationResolvers, 'changeMyProfile' | 'changeProfile'> {
8
14
  constructor(
9
15
  private readonly personManager: PersonManager,
10
16
  ) { }
11
17
 
18
+ async changeProfile(
19
+ parent: unknown,
20
+ args: MutationChangeProfileArgs,
21
+ context: TenantResolverContext,
22
+ info: GraphQLResolveInfo,
23
+ ): Promise<ChangeProfileResponse> {
24
+ const person = await context.db.queryHandler.fetch(PersonQuery.byId(args.personId))
25
+ if (!person) {
26
+ return createErrorResponse('PERSON_NOT_FOUND', 'Person not found')
27
+ }
28
+ if (args.email === null) {
29
+ return createErrorResponse('INVALID_EMAIL_FORMAT', 'E-mail address cannot be null.')
30
+ }
31
+
32
+ await context.requireAccess({
33
+ scope: new IdentityScope(person.identity_id),
34
+ action: PermissionActions.PERSON_CHANGE_PROFILE,
35
+ message: 'You are not allowed to change a profile',
36
+ })
37
+ const result = await this.personManager.changeProfile(context.db, person, {
38
+ email: args.email,
39
+ name: args.name === '' ? null : args.name,
40
+ })
41
+
42
+ if (!result.ok) {
43
+ return createErrorResponse(result.error, result.errorMessage)
44
+ }
45
+
46
+ return { ok: true }
47
+ }
48
+
49
+
12
50
  async changeMyProfile(
13
51
  parent: unknown,
14
52
  args: MutationChangeMyProfileArgs,
@@ -28,7 +66,7 @@ export class ChangeProfileMutationResolver implements MutationResolvers {
28
66
  message: 'You are not allowed to change profile',
29
67
  })
30
68
 
31
- const result = await this.personManager.changeMyProfile(context.db, person, {
69
+ const result = await this.personManager.changeProfile(context.db, person, {
32
70
  email: args.email,
33
71
  name: args.name === '' ? null : args.name,
34
72
  })
@@ -171,6 +171,23 @@ export type ChangePasswordResponse = {
171
171
  readonly ok: Scalars['Boolean']['output']
172
172
  }
173
173
 
174
+ export type ChangeProfileError = {
175
+ readonly __typename?: 'ChangeProfileError'
176
+ readonly code: ChangeProfileErrorCode
177
+ readonly developerMessage: Scalars['String']['output']
178
+ }
179
+
180
+ export type ChangeProfileErrorCode =
181
+ | 'EMAIL_ALREADY_EXISTS'
182
+ | 'INVALID_EMAIL_FORMAT'
183
+ | 'PERSON_NOT_FOUND'
184
+
185
+ export type ChangeProfileResponse = {
186
+ readonly __typename?: 'ChangeProfileResponse'
187
+ readonly error?: Maybe<ChangeProfileError>
188
+ readonly ok: Scalars['Boolean']['output']
189
+ }
190
+
174
191
  export type CheckResetPasswordTokenCode =
175
192
  | 'REQUEST_NOT_FOUND'
176
193
  | 'TOKEN_EXPIRED'
@@ -587,6 +604,7 @@ export type Mutation = {
587
604
  readonly changeMyPassword?: Maybe<ChangeMyPasswordResponse>
588
605
  readonly changeMyProfile?: Maybe<ChangeMyProfileResponse>
589
606
  readonly changePassword?: Maybe<ChangePasswordResponse>
607
+ readonly changeProfile?: Maybe<ChangeProfileResponse>
590
608
  readonly confirmOtp?: Maybe<ConfirmOtpResponse>
591
609
  readonly createApiKey?: Maybe<CreateApiKeyResponse>
592
610
  readonly createGlobalApiKey?: Maybe<CreateApiKeyResponse>
@@ -668,6 +686,13 @@ export type MutationChangePasswordArgs = {
668
686
  }
669
687
 
670
688
 
689
+ export type MutationChangeProfileArgs = {
690
+ email?: InputMaybe<Scalars['String']['input']>
691
+ name?: InputMaybe<Scalars['String']['input']>
692
+ personId: Scalars['String']['input']
693
+ }
694
+
695
+
671
696
  export type MutationConfirmOtpArgs = {
672
697
  otpToken: Scalars['String']['input']
673
698
  }
@@ -1352,6 +1377,9 @@ export type ResolversTypes = {
1352
1377
  ChangePasswordError: ResolverTypeWrapper<ChangePasswordError>
1353
1378
  ChangePasswordErrorCode: ChangePasswordErrorCode
1354
1379
  ChangePasswordResponse: ResolverTypeWrapper<ChangePasswordResponse>
1380
+ ChangeProfileError: ResolverTypeWrapper<ChangeProfileError>
1381
+ ChangeProfileErrorCode: ChangeProfileErrorCode
1382
+ ChangeProfileResponse: ResolverTypeWrapper<ChangeProfileResponse>
1355
1383
  CheckResetPasswordTokenCode: CheckResetPasswordTokenCode
1356
1384
  CheckResetPasswordTokenResult: ResolverTypeWrapper<CheckResetPasswordTokenResult>
1357
1385
  CommonSignInResult: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['CommonSignInResult']>
@@ -1500,6 +1528,8 @@ export type ResolversParentTypes = {
1500
1528
  ChangeMyProfileResponse: ChangeMyProfileResponse
1501
1529
  ChangePasswordError: ChangePasswordError
1502
1530
  ChangePasswordResponse: ChangePasswordResponse
1531
+ ChangeProfileError: ChangeProfileError
1532
+ ChangeProfileResponse: ChangeProfileResponse
1503
1533
  CheckResetPasswordTokenResult: CheckResetPasswordTokenResult
1504
1534
  CommonSignInResult: ResolversInterfaceTypes<ResolversParentTypes>['CommonSignInResult']
1505
1535
  ConfirmOtpError: ConfirmOtpError
@@ -1709,6 +1739,18 @@ export type ChangePasswordResponseResolvers<ContextType = any, ParentType extend
1709
1739
  __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
1710
1740
  }
1711
1741
 
1742
+ export type ChangeProfileErrorResolvers<ContextType = any, ParentType extends ResolversParentTypes['ChangeProfileError'] = ResolversParentTypes['ChangeProfileError']> = {
1743
+ code?: Resolver<ResolversTypes['ChangeProfileErrorCode'], ParentType, ContextType>
1744
+ developerMessage?: Resolver<ResolversTypes['String'], ParentType, ContextType>
1745
+ __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
1746
+ }
1747
+
1748
+ export type ChangeProfileResponseResolvers<ContextType = any, ParentType extends ResolversParentTypes['ChangeProfileResponse'] = ResolversParentTypes['ChangeProfileResponse']> = {
1749
+ error?: Resolver<Maybe<ResolversTypes['ChangeProfileError']>, ParentType, ContextType>
1750
+ ok?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>
1751
+ __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
1752
+ }
1753
+
1712
1754
  export type CheckResetPasswordTokenResultResolvers<ContextType = any, ParentType extends ResolversParentTypes['CheckResetPasswordTokenResult'] = ResolversParentTypes['CheckResetPasswordTokenResult']> = {
1713
1755
  code?: Resolver<ResolversTypes['CheckResetPasswordTokenCode'], ParentType, ContextType>
1714
1756
  __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
@@ -1994,6 +2036,7 @@ export type MutationResolvers<ContextType = any, ParentType extends ResolversPar
1994
2036
  changeMyPassword?: Resolver<Maybe<ResolversTypes['ChangeMyPasswordResponse']>, ParentType, ContextType, RequireFields<MutationChangeMyPasswordArgs, 'currentPassword' | 'newPassword'>>
1995
2037
  changeMyProfile?: Resolver<Maybe<ResolversTypes['ChangeMyProfileResponse']>, ParentType, ContextType, Partial<MutationChangeMyProfileArgs>>
1996
2038
  changePassword?: Resolver<Maybe<ResolversTypes['ChangePasswordResponse']>, ParentType, ContextType, RequireFields<MutationChangePasswordArgs, 'password' | 'personId'>>
2039
+ changeProfile?: Resolver<Maybe<ResolversTypes['ChangeProfileResponse']>, ParentType, ContextType, RequireFields<MutationChangeProfileArgs, 'personId'>>
1997
2040
  confirmOtp?: Resolver<Maybe<ResolversTypes['ConfirmOtpResponse']>, ParentType, ContextType, RequireFields<MutationConfirmOtpArgs, 'otpToken'>>
1998
2041
  createApiKey?: Resolver<Maybe<ResolversTypes['CreateApiKeyResponse']>, ParentType, ContextType, RequireFields<MutationCreateApiKeyArgs, 'description' | 'memberships' | 'projectSlug'>>
1999
2042
  createGlobalApiKey?: Resolver<Maybe<ResolversTypes['CreateApiKeyResponse']>, ParentType, ContextType, RequireFields<MutationCreateGlobalApiKeyArgs, 'description'>>
@@ -2311,6 +2354,8 @@ export type Resolvers<ContextType = any> = {
2311
2354
  ChangeMyProfileResponse?: ChangeMyProfileResponseResolvers<ContextType>
2312
2355
  ChangePasswordError?: ChangePasswordErrorResolvers<ContextType>
2313
2356
  ChangePasswordResponse?: ChangePasswordResponseResolvers<ContextType>
2357
+ ChangeProfileError?: ChangeProfileErrorResolvers<ContextType>
2358
+ ChangeProfileResponse?: ChangeProfileResponseResolvers<ContextType>
2314
2359
  CheckResetPasswordTokenResult?: CheckResetPasswordTokenResultResolvers<ContextType>
2315
2360
  CommonSignInResult?: CommonSignInResultResolvers<ContextType>
2316
2361
  ConfirmOtpError?: ConfirmOtpErrorResolvers<ContextType>
@@ -27,6 +27,7 @@ const schema: DocumentNode = gql`
27
27
  signIn(email: String!, password: String!, expiration: Int, otpToken: String): SignInResponse
28
28
  createSessionToken(email: String, personId: String, expiration: Int): CreateSessionTokenResponse
29
29
  signOut(all: Boolean): SignOutResponse
30
+ changeProfile(personId: String!, email: String, name: String): ChangeProfileResponse
30
31
  changeMyProfile(email: String, name: String): ChangeMyProfileResponse
31
32
  changePassword(personId: String!, password: String!): ChangePasswordResponse
32
33
  changeMyPassword(currentPassword: String!, newPassword: String!): ChangeMyPasswordResponse
@@ -206,6 +207,24 @@ const schema: DocumentNode = gql`
206
207
  }
207
208
 
208
209
  # === changeProfile ===
210
+
211
+ type ChangeProfileResponse {
212
+ ok: Boolean!
213
+ error: ChangeProfileError
214
+ }
215
+
216
+ type ChangeProfileError {
217
+ code: ChangeProfileErrorCode!
218
+ developerMessage: String!
219
+ }
220
+
221
+ enum ChangeProfileErrorCode {
222
+ PERSON_NOT_FOUND
223
+ INVALID_EMAIL_FORMAT
224
+ EMAIL_ALREADY_EXISTS
225
+ }
226
+
227
+ # === changeMyProfile ===
209
228
 
210
229
  type ChangeMyProfileResponse {
211
230
  ok: Boolean!
@@ -221,7 +240,7 @@ const schema: DocumentNode = gql`
221
240
  NOT_A_PERSON
222
241
  INVALID_EMAIL_FORMAT
223
242
  EMAIL_ALREADY_EXISTS
224
- }
243
+ }
225
244
 
226
245
  # === changePassword ===
227
246
 
@@ -243,6 +262,8 @@ const schema: DocumentNode = gql`
243
262
  }
244
263
 
245
264
 
265
+ # === changeMyPassword ===
266
+
246
267
  type ChangeMyPasswordResponse {
247
268
  ok: Boolean!
248
269
  error: ChangeMyPasswordError
@@ -0,0 +1,37 @@
1
+ import { executeTenantTest } from '../../../src/testTenant'
2
+ import { testUuid } from '../../../src/testUuid'
3
+ import { test } from 'vitest'
4
+ import { changeProfileMutation } from './gql/changeProfile'
5
+ import { updatePersonProfileNameAndEmailSql } from './sql/updatePesonNameSql'
6
+ import { authenticatedIdentityId } from '../../../src/testTenantDb'
7
+ import { getPersonByIdentity } from './sql/getPersonByIdentity'
8
+ import { getPersonByEmailSql } from './sql/getPersonByEmailSql'
9
+ import { getPersonByIdSql } from './sql/getPersonByIdSql'
10
+
11
+ test('changes my name and email', async () => {
12
+
13
+ const personId = testUuid(1)
14
+ const identityId = authenticatedIdentityId
15
+ const email = 'jane@doe.com'
16
+ const name = 'Jane Doe'
17
+ await executeTenantTest({
18
+ query: changeProfileMutation({ personId, email, name }),
19
+ executes: [
20
+ getPersonByIdSql({
21
+ personId,
22
+ response: { personId, email: 'john.doe@example.com', name: 'John Doe', roles: [], password: '123456', identityId },
23
+ }),
24
+ getPersonByEmailSql({ email, response: null }),
25
+ updatePersonProfileNameAndEmailSql({ personId, email, name }),
26
+ ],
27
+ return: {
28
+ data: {
29
+ changeProfile: {
30
+ ok: true,
31
+ error: null,
32
+ },
33
+ },
34
+ },
35
+ })
36
+ })
37
+
@@ -0,0 +1,14 @@
1
+ import { GraphQLTestQuery } from './types'
2
+ import { GQL } from '../../../../src/tags'
3
+
4
+ export const changeProfileMutation = (variables: { personId: string; email?: string; name?: string }): GraphQLTestQuery => ({
5
+ query: GQL`mutation($personId: String!, $email: String, $name: String) {
6
+ changeProfile(personId: $personId, email: $email, name: $name) {
7
+ ok
8
+ error {
9
+ code
10
+ }
11
+ }
12
+ }`,
13
+ variables,
14
+ })