@webiny/api-core 6.6.0-alpha.0 → 6.6.0-alpha.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 (164) hide show
  1. package/ApiCoreFeature.js +6 -0
  2. package/ApiCoreFeature.js.map +1 -1
  3. package/exports/api/security/api-key.d.ts +1 -0
  4. package/exports/api/security/api-key.js +1 -0
  5. package/features/ai/Ai.d.ts +3 -1
  6. package/features/ai/Ai.js +39 -4
  7. package/features/ai/Ai.js.map +1 -1
  8. package/features/ai/AnthropicSdkFactory.js +12 -0
  9. package/features/ai/AnthropicSdkFactory.js.map +1 -1
  10. package/features/ai/OpenAiSdkFactory.js +16 -0
  11. package/features/ai/OpenAiSdkFactory.js.map +1 -1
  12. package/features/ai/TextExtractor/DefaultTextExtractor.d.ts +9 -0
  13. package/features/ai/TextExtractor/DefaultTextExtractor.js +28 -0
  14. package/features/ai/TextExtractor/DefaultTextExtractor.js.map +1 -0
  15. package/features/ai/TextExtractor/abstractions.d.ts +12 -0
  16. package/features/ai/TextExtractor/abstractions.js +5 -0
  17. package/features/ai/TextExtractor/abstractions.js.map +1 -0
  18. package/features/ai/TextExtractor/index.d.ts +3 -0
  19. package/features/ai/TextExtractor/index.js +2 -0
  20. package/features/ai/TextExtractor/parsers/docxParser.d.ts +1 -0
  21. package/features/ai/TextExtractor/parsers/docxParser.js +111 -0
  22. package/features/ai/TextExtractor/parsers/docxParser.js.map +1 -0
  23. package/features/ai/abstractions.d.ts +19 -0
  24. package/features/ai/abstractions.js.map +1 -1
  25. package/features/ai/events.d.ts +59 -0
  26. package/features/ai/events.js +41 -0
  27. package/features/ai/events.js.map +1 -0
  28. package/features/ai/feature.js +2 -0
  29. package/features/ai/feature.js.map +1 -1
  30. package/features/ai/index.d.ts +5 -1
  31. package/features/ai/index.js +2 -0
  32. package/features/dateFormatter/DefaultDateFormatter.d.ts +15 -0
  33. package/features/dateFormatter/DefaultDateFormatter.js +20 -0
  34. package/features/dateFormatter/DefaultDateFormatter.js.map +1 -0
  35. package/features/dateFormatter/abstractions.d.ts +13 -0
  36. package/features/dateFormatter/abstractions.js +5 -0
  37. package/features/dateFormatter/abstractions.js.map +1 -0
  38. package/features/dateFormatter/feature.d.ts +4 -0
  39. package/features/dateFormatter/feature.js +11 -0
  40. package/features/dateFormatter/feature.js.map +1 -0
  41. package/features/dateFormatter/index.d.ts +3 -0
  42. package/features/dateFormatter/index.js +2 -0
  43. package/features/featureFlags/decorators/FeatureFlagsWithLicenseDecorator.d.ts +13 -0
  44. package/features/featureFlags/decorators/FeatureFlagsWithLicenseDecorator.js +49 -0
  45. package/features/featureFlags/decorators/FeatureFlagsWithLicenseDecorator.js.map +1 -0
  46. package/features/security/IdentityContext/IdentityContext.d.ts +3 -3
  47. package/features/security/IdentityContext/IdentityContext.js +6 -6
  48. package/features/security/IdentityContext/IdentityContext.js.map +1 -1
  49. package/features/security/apiKeys/CreateApiKey/CreateApiKeyUseCase.js +11 -6
  50. package/features/security/apiKeys/CreateApiKey/CreateApiKeyUseCase.js.map +1 -1
  51. package/features/security/apiKeys/GetApiKey/GetApiKeyUseCase.d.ts +1 -1
  52. package/features/security/apiKeys/GetApiKey/GetApiKeyUseCase.js.map +1 -1
  53. package/features/security/apiKeys/GetApiKeyBySlug/GetApiKeyBySlugUseCase.d.ts +1 -3
  54. package/features/security/apiKeys/GetApiKeyBySlug/GetApiKeyBySlugUseCase.js.map +1 -1
  55. package/features/security/apiKeys/GetApiKeyBySlug/abstractions.d.ts +1 -1
  56. package/features/security/apiKeys/GetApiKeyBySlug/abstractions.js.map +1 -1
  57. package/features/security/apiKeys/GetApiKeyByToken/GetApiKeyByTokenUseCase.d.ts +1 -3
  58. package/features/security/apiKeys/GetApiKeyByToken/GetApiKeyByTokenUseCase.js.map +1 -1
  59. package/features/security/apiKeys/GetApiKeyByToken/abstractions.d.ts +1 -1
  60. package/features/security/apiKeys/GetApiKeyByToken/abstractions.js.map +1 -1
  61. package/features/security/apiKeys/UpdateApiKey/UpdateApiKeyUseCase.js +9 -3
  62. package/features/security/apiKeys/UpdateApiKey/UpdateApiKeyUseCase.js.map +1 -1
  63. package/features/security/apiKeys/feature.js +7 -2
  64. package/features/security/apiKeys/feature.js.map +1 -1
  65. package/features/security/apiKeys/shared/ApiKeyProvider.d.ts +3 -5
  66. package/features/security/apiKeys/shared/ApiKeyProvider.js +6 -21
  67. package/features/security/apiKeys/shared/ApiKeyProvider.js.map +1 -1
  68. package/features/security/apiKeys/shared/ApiKeysRepository.d.ts +1 -0
  69. package/features/security/apiKeys/shared/ApiKeysRepository.js +19 -4
  70. package/features/security/apiKeys/shared/ApiKeysRepository.js.map +1 -1
  71. package/features/security/apiKeys/shared/generateToken.d.ts +1 -1
  72. package/features/security/apiKeys/shared/generateToken.js.map +1 -1
  73. package/features/security/apiKeys/shared/schemas.d.ts +2 -2
  74. package/features/security/apiKeys/shared/schemas.js +2 -2
  75. package/features/security/apiKeys/shared/schemas.js.map +1 -1
  76. package/features/security/apiKeys/shared/types.d.ts +1 -1
  77. package/features/security/authorization/GroupsTeamsAuthorizer/GetPermissionsFromIdentity.d.ts +3 -3
  78. package/features/security/authorization/GroupsTeamsAuthorizer/GetPermissionsFromIdentity.js +5 -5
  79. package/features/security/authorization/GroupsTeamsAuthorizer/GetPermissionsFromIdentity.js.map +1 -1
  80. package/features/security/roles/CreateRole/CreateRoleUseCase.js +11 -6
  81. package/features/security/roles/CreateRole/CreateRoleUseCase.js.map +1 -1
  82. package/features/security/roles/CreateRole/schema.d.ts +1 -1
  83. package/features/security/roles/CreateRole/schema.js +1 -1
  84. package/features/security/roles/CreateRole/schema.js.map +1 -1
  85. package/features/security/roles/UpdateRole/UpdateRoleUseCase.js +9 -3
  86. package/features/security/roles/UpdateRole/UpdateRoleUseCase.js.map +1 -1
  87. package/features/security/roles/UpdateRole/schema.d.ts +1 -1
  88. package/features/security/roles/UpdateRole/schema.js +1 -1
  89. package/features/security/roles/UpdateRole/schema.js.map +1 -1
  90. package/features/security/shared/description.d.ts +4 -0
  91. package/features/security/shared/description.js +7 -0
  92. package/features/security/shared/description.js.map +1 -0
  93. package/features/security/shared/index.d.ts +1 -0
  94. package/features/security/shared/index.js +1 -0
  95. package/features/security/teams/CreateTeam/CreateTeamUseCase.d.ts +3 -1
  96. package/features/security/teams/CreateTeam/CreateTeamUseCase.js +20 -8
  97. package/features/security/teams/CreateTeam/CreateTeamUseCase.js.map +1 -1
  98. package/features/security/teams/CreateTeam/schema.d.ts +1 -1
  99. package/features/security/teams/CreateTeam/schema.js +1 -1
  100. package/features/security/teams/CreateTeam/schema.js.map +1 -1
  101. package/features/security/teams/UpdateTeam/UpdateTeamUseCase.d.ts +3 -1
  102. package/features/security/teams/UpdateTeam/UpdateTeamUseCase.js +20 -5
  103. package/features/security/teams/UpdateTeam/UpdateTeamUseCase.js.map +1 -1
  104. package/features/security/teams/UpdateTeam/schema.d.ts +1 -1
  105. package/features/security/teams/UpdateTeam/schema.js +1 -1
  106. package/features/security/teams/UpdateTeam/schema.js.map +1 -1
  107. package/features/security/teams/shared/resolveRoleIdentifiers.d.ts +17 -0
  108. package/features/security/teams/shared/resolveRoleIdentifiers.js +32 -0
  109. package/features/security/teams/shared/resolveRoleIdentifiers.js.map +1 -0
  110. package/features/slugify/DefaultSlugify.d.ts +8 -0
  111. package/features/slugify/DefaultSlugify.js +20 -0
  112. package/features/slugify/DefaultSlugify.js.map +1 -0
  113. package/features/slugify/abstractions.d.ts +12 -0
  114. package/features/slugify/abstractions.js +5 -0
  115. package/features/slugify/abstractions.js.map +1 -0
  116. package/features/slugify/feature.d.ts +4 -0
  117. package/features/slugify/feature.js +11 -0
  118. package/features/slugify/feature.js.map +1 -0
  119. package/features/slugify/index.d.ts +3 -0
  120. package/features/slugify/index.js +2 -0
  121. package/features/stringFormatter/DefaultStringFormatter.d.ts +17 -0
  122. package/features/stringFormatter/DefaultStringFormatter.js +19 -0
  123. package/features/stringFormatter/DefaultStringFormatter.js.map +1 -0
  124. package/features/stringFormatter/abstractions.d.ts +11 -0
  125. package/features/stringFormatter/abstractions.js +5 -0
  126. package/features/stringFormatter/abstractions.js.map +1 -0
  127. package/features/stringFormatter/feature.d.ts +4 -0
  128. package/features/stringFormatter/feature.js +13 -0
  129. package/features/stringFormatter/feature.js.map +1 -0
  130. package/features/stringFormatter/index.d.ts +3 -0
  131. package/features/stringFormatter/index.js +2 -0
  132. package/features/task/TaskDefinition/abstractions.d.ts +76 -4
  133. package/features/task/TaskDefinition/abstractions.js +2 -1
  134. package/features/task/TaskDefinition/abstractions.js.map +1 -1
  135. package/features/wcp/WcpContext/WcpContext.d.ts +6 -0
  136. package/features/wcp/WcpContext/WcpContext.js +18 -0
  137. package/features/wcp/WcpContext/WcpContext.js.map +1 -1
  138. package/features/wcp/WcpContext/decorators/WcpContextWithFeatureFlagsDecorator.d.ts +18 -0
  139. package/features/wcp/WcpContext/decorators/WcpContextWithFeatureFlagsDecorator.js +57 -25
  140. package/features/wcp/WcpContext/decorators/WcpContextWithFeatureFlagsDecorator.js.map +1 -1
  141. package/features/wcp/WcpFeature.js +2 -0
  142. package/features/wcp/WcpFeature.js.map +1 -1
  143. package/features/wcp/WcpLicenseLoader.d.ts +25 -0
  144. package/features/wcp/WcpLicenseLoader.js +58 -0
  145. package/features/wcp/WcpLicenseLoader.js.map +1 -0
  146. package/features/wcp/WcpLicenseProvider.d.ts +6 -14
  147. package/features/wcp/WcpLicenseProvider.js +3 -7
  148. package/features/wcp/WcpLicenseProvider.js.map +1 -1
  149. package/features/wcp/resolveWcpProjectEnvironment.d.ts +9 -0
  150. package/features/wcp/resolveWcpProjectEnvironment.js +23 -0
  151. package/features/wcp/resolveWcpProjectEnvironment.js.map +1 -0
  152. package/graphql/featureFlags/FeatureFlagsSchemaFactory.d.ts +9 -0
  153. package/graphql/featureFlags/FeatureFlagsSchemaFactory.js +49 -0
  154. package/graphql/featureFlags/FeatureFlagsSchemaFactory.js.map +1 -0
  155. package/graphql/wcp/WcpSchemaFactory.js +2 -0
  156. package/graphql/wcp/WcpSchemaFactory.js.map +1 -1
  157. package/package.json +18 -18
  158. package/testing/environment.d.ts +14 -0
  159. package/testing/environment.js +18 -0
  160. package/testing/environment.js.map +1 -0
  161. package/types/security.d.ts +1 -1
  162. package/features/wcp/loadWcpLicense.d.ts +0 -2
  163. package/features/wcp/loadWcpLicense.js +0 -51
  164. package/features/wcp/loadWcpLicense.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/roles/CreateRole/schema.js","sources":["../../../../../src/features/security/roles/CreateRole/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const createRoleValidation = z.object({\n name: z.string().min(3),\n slug: z.string().min(3),\n description: z.string().max(500).optional().default(\"\"),\n permissions: z.array(z.looseObject({ name: z.string() }))\n});\n"],"names":["createRoleValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,QAAQ,GAAG,OAAO,CAAC;IACpD,aAAaA,EAAE,KAAK,CAACA,EAAE,WAAW,CAAC;QAAE,MAAMA,EAAE,MAAM;IAAG;AAC1D"}
1
+ {"version":3,"file":"features/security/roles/CreateRole/schema.js","sources":["../../../../../src/features/security/roles/CreateRole/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const createRoleValidation = z.object({\n name: z.string().min(3),\n slug: z.string().min(3),\n // Nullable in the GraphQL schema - see `descriptionOnCreate` for why this is `.nullish()`.\n // `.optional().default(\"\")` did NOT cover this: Zod applies a default to `undefined` only, so\n // an explicit null still failed validation.\n description: z.string().max(500).nullish(),\n permissions: z.array(z.looseObject({ name: z.string() }))\n});\n"],"names":["createRoleValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IAIrB,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,OAAO;IACxC,aAAaA,EAAE,KAAK,CAACA,EAAE,WAAW,CAAC;QAAE,MAAMA,EAAE,MAAM;IAAG;AAC1D"}
@@ -5,6 +5,7 @@ import { IdentityContext } from "../../IdentityContext/abstractions.js";
5
5
  import { EventPublisher } from "../../../eventPublisher/index.js";
6
6
  import { updateRoleValidation } from "./schema.js";
7
7
  import { RoleAfterUpdateEvent, RoleBeforeUpdateEvent } from "./events.js";
8
+ import { descriptionOnUpdate } from "../../shared/description.js";
8
9
  import { CannotUpdatePluginRolesError, NotAuthorizedError, RoleValidationError } from "../shared/errors.js";
9
10
  class UpdateRoleUseCaseImpl {
10
11
  constructor(repository, identityContext, eventPublisher){
@@ -23,21 +24,26 @@ class UpdateRoleUseCaseImpl {
23
24
  if (existingResult.isFail()) return Result.fail(existingResult.error);
24
25
  const existingRole = existingResult.value;
25
26
  if (existingRole.plugin) return Result.fail(new CannotUpdatePluginRolesError());
27
+ const { description, ...rest } = validation.data;
28
+ const changes = {
29
+ ...rest,
30
+ ...descriptionOnUpdate(description)
31
+ };
26
32
  const updatedRole = {
27
33
  ...existingRole,
28
- ...validation.data
34
+ ...changes
29
35
  };
30
36
  await this.eventPublisher.publish(new RoleBeforeUpdateEvent({
31
37
  original: existingRole,
32
38
  updated: updatedRole,
33
- input: validation.data
39
+ input: changes
34
40
  }));
35
41
  const result = await this.repository.update(updatedRole);
36
42
  if (result.isFail()) return Result.fail(result.error);
37
43
  await this.eventPublisher.publish(new RoleAfterUpdateEvent({
38
44
  original: existingRole,
39
45
  updated: updatedRole,
40
- input: validation.data
46
+ input: changes
41
47
  }));
42
48
  return Result.ok(updatedRole);
43
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/roles/UpdateRole/UpdateRoleUseCase.js","sources":["../../../../../src/features/security/roles/UpdateRole/UpdateRoleUseCase.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { UpdateRoleUseCase as UseCase } from \"./abstractions.js\";\nimport { RolesRepository } from \"../shared/abstractions.js\";\nimport { IdentityContext } from \"../../IdentityContext/abstractions.js\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { updateRoleValidation } from \"./schema.js\";\nimport { RoleBeforeUpdateEvent, RoleAfterUpdateEvent } from \"./events.js\";\nimport type { Role, UpdateRoleInput } from \"../shared/types.js\";\nimport {\n NotAuthorizedError,\n CannotUpdatePluginRolesError,\n RoleValidationError\n} from \"../shared/errors.js\";\n\nclass UpdateRoleUseCaseImpl implements UseCase.Interface {\n private repository: RolesRepository.Interface;\n private identityContext: IdentityContext.Interface;\n private eventPublisher: EventPublisher.Interface;\n\n constructor(\n repository: RolesRepository.Interface,\n identityContext: IdentityContext.Interface,\n eventPublisher: EventPublisher.Interface\n ) {\n this.repository = repository;\n this.identityContext = identityContext;\n this.eventPublisher = eventPublisher;\n }\n\n async execute(id: string, input: UpdateRoleInput): Promise<Result<Role, UseCase.Error>> {\n const hasPermission = await this.identityContext.getPermission(\"security.role\");\n if (!hasPermission) {\n return Result.fail(new NotAuthorizedError());\n }\n\n const validation = updateRoleValidation.safeParse(input);\n if (!validation.success) {\n return Result.fail(new RoleValidationError(validation.error.issues[0].message));\n }\n\n const existingResult = await this.repository.get({ id });\n if (existingResult.isFail()) {\n return Result.fail(existingResult.error);\n }\n\n const existingRole = existingResult.value;\n\n // Check if role is created via plugin\n if (existingRole.plugin) {\n return Result.fail(new CannotUpdatePluginRolesError());\n }\n\n const updatedRole: Role = {\n ...existingRole,\n ...validation.data\n };\n\n await this.eventPublisher.publish(\n new RoleBeforeUpdateEvent({\n original: existingRole,\n updated: updatedRole,\n input: validation.data\n })\n );\n\n const result = await this.repository.update(updatedRole);\n\n if (result.isFail()) {\n return Result.fail(result.error);\n }\n\n await this.eventPublisher.publish(\n new RoleAfterUpdateEvent({\n original: existingRole,\n updated: updatedRole,\n input: validation.data\n })\n );\n\n return Result.ok(updatedRole);\n }\n}\n\nexport const UpdateRoleUseCase = UseCase.createImplementation({\n implementation: UpdateRoleUseCaseImpl,\n dependencies: [RolesRepository, IdentityContext, EventPublisher]\n});\n"],"names":["UpdateRoleUseCaseImpl","repository","identityContext","eventPublisher","id","input","hasPermission","Result","NotAuthorizedError","validation","updateRoleValidation","RoleValidationError","existingResult","existingRole","CannotUpdatePluginRolesError","updatedRole","RoleBeforeUpdateEvent","result","RoleAfterUpdateEvent","UpdateRoleUseCase","UseCase","RolesRepository","IdentityContext","EventPublisher"],"mappings":";;;;;;;;AAcA,MAAMA;IAKF,YACIC,UAAqC,EACrCC,eAA0C,EAC1CC,cAAwC,CAC1C;QACE,IAAI,CAAC,UAAU,GAAGF;QAClB,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,cAAc,GAAGC;IAC1B;IAEA,MAAM,QAAQC,EAAU,EAAEC,KAAsB,EAAwC;QACpF,MAAMC,gBAAgB,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAC/D,IAAI,CAACA,eACD,OAAOC,OAAO,IAAI,CAAC,IAAIC;QAG3B,MAAMC,aAAaC,qBAAqB,SAAS,CAACL;QAClD,IAAI,CAACI,WAAW,OAAO,EACnB,OAAOF,OAAO,IAAI,CAAC,IAAII,oBAAoBF,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO;QAGjF,MAAMG,iBAAiB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAER;QAAG;QACtD,IAAIQ,eAAe,MAAM,IACrB,OAAOL,OAAO,IAAI,CAACK,eAAe,KAAK;QAG3C,MAAMC,eAAeD,eAAe,KAAK;QAGzC,IAAIC,aAAa,MAAM,EACnB,OAAON,OAAO,IAAI,CAAC,IAAIO;QAG3B,MAAMC,cAAoB;YACtB,GAAGF,YAAY;YACf,GAAGJ,WAAW,IAAI;QACtB;QAEA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIO,sBAAsB;YACtB,UAAUH;YACV,SAASE;YACT,OAAON,WAAW,IAAI;QAC1B;QAGJ,MAAMQ,SAAS,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAACF;QAE5C,IAAIE,OAAO,MAAM,IACb,OAAOV,OAAO,IAAI,CAACU,OAAO,KAAK;QAGnC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,qBAAqB;YACrB,UAAUL;YACV,SAASE;YACT,OAAON,WAAW,IAAI;QAC1B;QAGJ,OAAOF,OAAO,EAAE,CAACQ;IACrB;AACJ;AAEO,MAAMI,sCAAoBC,kBAAAA,oBAA4B,CAAC;IAC1D,gBAAgBpB;IAChB,cAAc;QAACqB;QAAiBC;QAAiBC;KAAe;AACpE"}
1
+ {"version":3,"file":"features/security/roles/UpdateRole/UpdateRoleUseCase.js","sources":["../../../../../src/features/security/roles/UpdateRole/UpdateRoleUseCase.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { UpdateRoleUseCase as UseCase } from \"./abstractions.js\";\nimport { RolesRepository } from \"../shared/abstractions.js\";\nimport { IdentityContext } from \"../../IdentityContext/abstractions.js\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { updateRoleValidation } from \"./schema.js\";\nimport { RoleBeforeUpdateEvent, RoleAfterUpdateEvent } from \"./events.js\";\nimport type { Role, UpdateRoleInput } from \"../shared/types.js\";\nimport { descriptionOnUpdate } from \"../../shared/description.js\";\nimport {\n NotAuthorizedError,\n CannotUpdatePluginRolesError,\n RoleValidationError\n} from \"../shared/errors.js\";\n\nclass UpdateRoleUseCaseImpl implements UseCase.Interface {\n private repository: RolesRepository.Interface;\n private identityContext: IdentityContext.Interface;\n private eventPublisher: EventPublisher.Interface;\n\n constructor(\n repository: RolesRepository.Interface,\n identityContext: IdentityContext.Interface,\n eventPublisher: EventPublisher.Interface\n ) {\n this.repository = repository;\n this.identityContext = identityContext;\n this.eventPublisher = eventPublisher;\n }\n\n async execute(id: string, input: UpdateRoleInput): Promise<Result<Role, UseCase.Error>> {\n const hasPermission = await this.identityContext.getPermission(\"security.role\");\n if (!hasPermission) {\n return Result.fail(new NotAuthorizedError());\n }\n\n const validation = updateRoleValidation.safeParse(input);\n if (!validation.success) {\n return Result.fail(new RoleValidationError(validation.error.issues[0].message));\n }\n\n const existingResult = await this.repository.get({ id });\n if (existingResult.isFail()) {\n return Result.fail(existingResult.error);\n }\n\n const existingRole = existingResult.value;\n\n // Check if role is created via plugin\n if (existingRole.plugin) {\n return Result.fail(new CannotUpdatePluginRolesError());\n }\n\n // `description` is pulled out of the spread because it is the one field that accepts null.\n // Normalising it here keeps null out of both the entity and the published events, whose\n // input type declares `description?: string`.\n const { description, ...rest } = validation.data;\n\n const changes: UpdateRoleInput = {\n ...rest,\n ...descriptionOnUpdate(description)\n };\n\n const updatedRole: Role = {\n ...existingRole,\n ...changes\n };\n\n await this.eventPublisher.publish(\n new RoleBeforeUpdateEvent({\n original: existingRole,\n updated: updatedRole,\n input: changes\n })\n );\n\n const result = await this.repository.update(updatedRole);\n\n if (result.isFail()) {\n return Result.fail(result.error);\n }\n\n await this.eventPublisher.publish(\n new RoleAfterUpdateEvent({\n original: existingRole,\n updated: updatedRole,\n input: changes\n })\n );\n\n return Result.ok(updatedRole);\n }\n}\n\nexport const UpdateRoleUseCase = UseCase.createImplementation({\n implementation: UpdateRoleUseCaseImpl,\n dependencies: [RolesRepository, IdentityContext, EventPublisher]\n});\n"],"names":["UpdateRoleUseCaseImpl","repository","identityContext","eventPublisher","id","input","hasPermission","Result","NotAuthorizedError","validation","updateRoleValidation","RoleValidationError","existingResult","existingRole","CannotUpdatePluginRolesError","description","rest","changes","descriptionOnUpdate","updatedRole","RoleBeforeUpdateEvent","result","RoleAfterUpdateEvent","UpdateRoleUseCase","UseCase","RolesRepository","IdentityContext","EventPublisher"],"mappings":";;;;;;;;;AAeA,MAAMA;IAKF,YACIC,UAAqC,EACrCC,eAA0C,EAC1CC,cAAwC,CAC1C;QACE,IAAI,CAAC,UAAU,GAAGF;QAClB,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,cAAc,GAAGC;IAC1B;IAEA,MAAM,QAAQC,EAAU,EAAEC,KAAsB,EAAwC;QACpF,MAAMC,gBAAgB,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAC/D,IAAI,CAACA,eACD,OAAOC,OAAO,IAAI,CAAC,IAAIC;QAG3B,MAAMC,aAAaC,qBAAqB,SAAS,CAACL;QAClD,IAAI,CAACI,WAAW,OAAO,EACnB,OAAOF,OAAO,IAAI,CAAC,IAAII,oBAAoBF,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO;QAGjF,MAAMG,iBAAiB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAER;QAAG;QACtD,IAAIQ,eAAe,MAAM,IACrB,OAAOL,OAAO,IAAI,CAACK,eAAe,KAAK;QAG3C,MAAMC,eAAeD,eAAe,KAAK;QAGzC,IAAIC,aAAa,MAAM,EACnB,OAAON,OAAO,IAAI,CAAC,IAAIO;QAM3B,MAAM,EAAEC,WAAW,EAAE,GAAGC,MAAM,GAAGP,WAAW,IAAI;QAEhD,MAAMQ,UAA2B;YAC7B,GAAGD,IAAI;YACP,GAAGE,oBAAoBH,YAAY;QACvC;QAEA,MAAMI,cAAoB;YACtB,GAAGN,YAAY;YACf,GAAGI,OAAO;QACd;QAEA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIG,sBAAsB;YACtB,UAAUP;YACV,SAASM;YACT,OAAOF;QACX;QAGJ,MAAMI,SAAS,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAACF;QAE5C,IAAIE,OAAO,MAAM,IACb,OAAOd,OAAO,IAAI,CAACc,OAAO,KAAK;QAGnC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,qBAAqB;YACrB,UAAUT;YACV,SAASM;YACT,OAAOF;QACX;QAGJ,OAAOV,OAAO,EAAE,CAACY;IACrB;AACJ;AAEO,MAAMI,sCAAoBC,kBAAAA,oBAA4B,CAAC;IAC1D,gBAAgBxB;IAChB,cAAc;QAACyB;QAAiBC;QAAiBC;KAAe;AACpE"}
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  export declare const updateRoleValidation: z.ZodObject<{
3
3
  name: z.ZodOptional<z.ZodString>;
4
- description: z.ZodOptional<z.ZodString>;
4
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
5
5
  permissions: z.ZodOptional<z.ZodArray<z.ZodObject<{
6
6
  name: z.ZodString;
7
7
  }, z.core.$loose>>>;
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  const updateRoleValidation = z.object({
3
3
  name: z.string().min(3).optional(),
4
- description: z.string().max(500).optional(),
4
+ description: z.string().max(500).nullish(),
5
5
  permissions: z.array(z.looseObject({
6
6
  name: z.string()
7
7
  })).optional()
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/roles/UpdateRole/schema.js","sources":["../../../../../src/features/security/roles/UpdateRole/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const updateRoleValidation = z.object({\n name: z.string().min(3).optional(),\n description: z.string().max(500).optional(),\n permissions: z.array(z.looseObject({ name: z.string() })).optional()\n});\n"],"names":["updateRoleValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ;IAChC,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,QAAQ;IACzC,aAAaA,EAAE,KAAK,CAACA,EAAE,WAAW,CAAC;QAAE,MAAMA,EAAE,MAAM;IAAG,IAAI,QAAQ;AACtE"}
1
+ {"version":3,"file":"features/security/roles/UpdateRole/schema.js","sources":["../../../../../src/features/security/roles/UpdateRole/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const updateRoleValidation = z.object({\n name: z.string().min(3).optional(),\n // Nullable in the GraphQL schema - see `descriptionOnUpdate` for why this is `.nullish()` and\n // must not carry a `.transform()`.\n description: z.string().max(500).nullish(),\n permissions: z.array(z.looseObject({ name: z.string() })).optional()\n});\n"],"names":["updateRoleValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ;IAGhC,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,OAAO;IACxC,aAAaA,EAAE,KAAK,CAACA,EAAE,WAAW,CAAC;QAAE,MAAMA,EAAE,MAAM;IAAG,IAAI,QAAQ;AACtE"}
@@ -0,0 +1,4 @@
1
+ export declare const descriptionOnCreate: (value: string | null | undefined) => string;
2
+ export declare const descriptionOnUpdate: (value: string | null | undefined) => {
3
+ description?: string;
4
+ };
@@ -0,0 +1,7 @@
1
+ const descriptionOnCreate = (value)=>value ?? "";
2
+ const descriptionOnUpdate = (value)=>void 0 === value ? {} : {
3
+ description: value ?? ""
4
+ };
5
+ export { descriptionOnCreate, descriptionOnUpdate };
6
+
7
+ //# sourceMappingURL=description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/security/shared/description.js","sources":["../../../../src/features/security/shared/description.ts"],"sourcesContent":["// Roles, teams and API keys all declare `description: String` in the GraphQL schema - nullable -\n// while their domain types store a plain string and use \"\" to mean \"no description\". Clients\n// therefore legitimately send null, and the admin app does exactly that: it reads an entity saved\n// without a description and submits the edit form again unchanged.\n//\n// Zod's `.optional()` accepts only `undefined`, so those requests used to fail with\n// \"Invalid input: expected string, received null\". The input schemas use `.nullish()` instead, and\n// these two helpers map what comes out of them onto the domain type.\n//\n// Note the schemas must NOT do this mapping via `.transform()`: a transform makes the key required\n// in the parsed output rather than optional, so spreading a parsed update onto a stored entity\n// would write `description: undefined` and wipe the stored value on every partial update.\n\nexport const descriptionOnCreate = (value: string | null | undefined): string => value ?? \"\";\n\n// Returns a patch rather than a value, because on a partial update an absent `description` has to\n// leave the stored one alone, while an explicit null clears it.\nexport const descriptionOnUpdate = (value: string | null | undefined): { description?: string } =>\n value === undefined ? {} : { description: value ?? \"\" };\n"],"names":["descriptionOnCreate","value","descriptionOnUpdate","undefined"],"mappings":"AAaO,MAAMA,sBAAsB,CAACC,QAA6CA,SAAS;AAInF,MAAMC,sBAAsB,CAACD,QAChCA,AAAUE,WAAVF,QAAsB,CAAC,IAAI;QAAE,aAAaA,SAAS;IAAG"}
@@ -1,2 +1,3 @@
1
1
  export * from "./abstractions.js";
2
+ export * from "./description.js";
2
3
  export * from "./errors.js";
@@ -1,2 +1,3 @@
1
1
  export * from "./abstractions.js";
2
+ export * from "./description.js";
2
3
  export * from "./errors.js";
@@ -2,13 +2,15 @@ import { Result } from "@webiny/feature/api";
2
2
  import { EventPublisher } from "../../../../features/eventPublisher/index.js";
3
3
  import { CreateTeam } from "./abstractions.js";
4
4
  import { TeamsRepository } from "../shared/abstractions.js";
5
+ import { RolesRepository } from "../../roles/shared/abstractions.js";
5
6
  import { IdentityContext } from "../../IdentityContext/abstractions.js";
6
7
  import type { Team, CreateTeamInput } from "../shared/types.js";
7
8
  export declare class CreateTeamUseCase implements CreateTeam.Interface {
8
9
  private identityContext;
9
10
  private eventPublisher;
10
11
  private repository;
11
- constructor(identityContext: IdentityContext.Interface, eventPublisher: EventPublisher.Interface, repository: TeamsRepository.Interface);
12
+ private rolesRepository;
13
+ constructor(identityContext: IdentityContext.Interface, eventPublisher: EventPublisher.Interface, repository: TeamsRepository.Interface, rolesRepository: RolesRepository.Interface);
12
14
  execute(input: CreateTeamInput): Promise<Result<Team, CreateTeam.Error>>;
13
15
  }
14
16
  export declare const CreateTeamUseCaseImpl: import("@webiny/di").Implementation<typeof CreateTeamUseCase>;
@@ -3,15 +3,19 @@ import { Result, createImplementation } from "@webiny/feature/api";
3
3
  import { EventPublisher } from "../../../eventPublisher/index.js";
4
4
  import { CreateTeam } from "./abstractions.js";
5
5
  import { TeamsRepository } from "../shared/abstractions.js";
6
+ import { RolesRepository } from "../../roles/shared/abstractions.js";
6
7
  import { IdentityContext } from "../../IdentityContext/abstractions.js";
7
8
  import { createTeamValidation } from "./schema.js";
8
9
  import { TeamAfterCreateEvent, TeamBeforeCreateEvent } from "./events.js";
9
10
  import { NotAuthorizedError, TeamExistsError, TeamValidationError } from "../shared/errors.js";
11
+ import { descriptionOnCreate } from "../../shared/description.js";
12
+ import { resolveRoleIdentifiers } from "../shared/resolveRoleIdentifiers.js";
10
13
  class CreateTeamUseCase {
11
- constructor(identityContext, eventPublisher, repository){
14
+ constructor(identityContext, eventPublisher, repository, rolesRepository){
12
15
  this.identityContext = identityContext;
13
16
  this.eventPublisher = eventPublisher;
14
17
  this.repository = repository;
18
+ this.rolesRepository = rolesRepository;
15
19
  }
16
20
  async execute(input) {
17
21
  const hasPermission = await this.identityContext.getPermission("security.team");
@@ -24,12 +28,19 @@ class CreateTeamUseCase {
24
28
  slug: data.slug
25
29
  });
26
30
  if (existingTeamResult.isOk()) return Result.fail(new TeamExistsError(data.slug));
31
+ const roleIdsResult = await resolveRoleIdentifiers(this.rolesRepository, data.roles);
32
+ if (roleIdsResult.isFail()) return Result.fail(roleIdsResult.error);
33
+ const createInput = {
34
+ ...data,
35
+ description: descriptionOnCreate(data.description),
36
+ roles: roleIdsResult.value
37
+ };
27
38
  const team = {
28
39
  id: mdbid(),
29
- name: data.name,
30
- slug: data.slug,
31
- description: data.description,
32
- roles: data.roles,
40
+ name: createInput.name,
41
+ slug: createInput.slug,
42
+ description: descriptionOnCreate(createInput.description),
43
+ roles: createInput.roles,
33
44
  system: input.system || false,
34
45
  createdOn: new Date().toISOString(),
35
46
  createdBy: {
@@ -41,13 +52,13 @@ class CreateTeamUseCase {
41
52
  };
42
53
  await this.eventPublisher.publish(new TeamBeforeCreateEvent({
43
54
  team,
44
- input: validation.data
55
+ input: createInput
45
56
  }));
46
57
  const result = await this.repository.create(team);
47
58
  if (result.isFail()) return Result.fail(result.error);
48
59
  await this.eventPublisher.publish(new TeamAfterCreateEvent({
49
60
  team,
50
- input: validation.data
61
+ input: createInput
51
62
  }));
52
63
  return Result.ok(team);
53
64
  }
@@ -58,7 +69,8 @@ const CreateTeamUseCaseImpl = createImplementation({
58
69
  dependencies: [
59
70
  IdentityContext,
60
71
  EventPublisher,
61
- TeamsRepository
72
+ TeamsRepository,
73
+ RolesRepository
62
74
  ]
63
75
  });
64
76
  export { CreateTeamUseCase, CreateTeamUseCaseImpl };
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/teams/CreateTeam/CreateTeamUseCase.js","sources":["../../../../../src/features/security/teams/CreateTeam/CreateTeamUseCase.ts"],"sourcesContent":["import { mdbid } from \"@webiny/utils\";\nimport { createImplementation } from \"@webiny/feature/api\";\nimport { Result } from \"@webiny/feature/api\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { CreateTeam } from \"./abstractions.js\";\nimport { TeamsRepository } from \"../shared/abstractions.js\";\nimport { IdentityContext } from \"../../IdentityContext/abstractions.js\";\nimport { createTeamValidation } from \"./schema.js\";\nimport { TeamBeforeCreateEvent, TeamAfterCreateEvent } from \"./events.js\";\nimport type { Team, CreateTeamInput } from \"../shared/types.js\";\nimport { NotAuthorizedError, TeamExistsError, TeamValidationError } from \"../shared/errors.js\";\n\nexport class CreateTeamUseCase implements CreateTeam.Interface {\n constructor(\n private identityContext: IdentityContext.Interface,\n private eventPublisher: EventPublisher.Interface,\n private repository: TeamsRepository.Interface\n ) {}\n\n async execute(input: CreateTeamInput): Promise<Result<Team, CreateTeam.Error>> {\n const hasPermission = await this.identityContext.getPermission(\"security.team\");\n\n if (!hasPermission) {\n return Result.fail(new NotAuthorizedError());\n }\n\n const validation = createTeamValidation.safeParse(input);\n if (!validation.success) {\n return Result.fail(new TeamValidationError(validation.error.issues[0].message));\n }\n\n const identity = this.identityContext.getIdentity();\n const data = validation.data;\n\n // Check if team with same slug already exists\n const existingTeamResult = await this.repository.get({ slug: data.slug });\n if (existingTeamResult.isOk()) {\n return Result.fail(new TeamExistsError(data.slug));\n }\n\n const team: Team = {\n id: mdbid(),\n name: data.name,\n slug: data.slug,\n description: data.description,\n roles: data.roles,\n system: input.system || false,\n createdOn: new Date().toISOString(),\n createdBy: {\n id: identity.id,\n displayName: identity.displayName,\n type: identity.type\n },\n plugin: false\n };\n\n await this.eventPublisher.publish(\n new TeamBeforeCreateEvent({ team, input: validation.data })\n );\n\n const result = await this.repository.create(team);\n\n if (result.isFail()) {\n return Result.fail(result.error);\n }\n\n await this.eventPublisher.publish(\n new TeamAfterCreateEvent({ team, input: validation.data })\n );\n\n return Result.ok(team);\n }\n}\n\nexport const CreateTeamUseCaseImpl = createImplementation({\n abstraction: CreateTeam,\n implementation: CreateTeamUseCase,\n dependencies: [IdentityContext, EventPublisher, TeamsRepository]\n});\n"],"names":["CreateTeamUseCase","identityContext","eventPublisher","repository","input","hasPermission","Result","NotAuthorizedError","validation","createTeamValidation","TeamValidationError","identity","data","existingTeamResult","TeamExistsError","team","mdbid","Date","TeamBeforeCreateEvent","result","TeamAfterCreateEvent","CreateTeamUseCaseImpl","createImplementation","CreateTeam","IdentityContext","EventPublisher","TeamsRepository"],"mappings":";;;;;;;;;AAYO,MAAMA;IACT,YACYC,eAA0C,EAC1CC,cAAwC,EACxCC,UAAqC,CAC/C;aAHUF,eAAe,GAAfA;aACAC,cAAc,GAAdA;aACAC,UAAU,GAAVA;IACT;IAEH,MAAM,QAAQC,KAAsB,EAA2C;QAC3E,MAAMC,gBAAgB,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAE/D,IAAI,CAACA,eACD,OAAOC,OAAO,IAAI,CAAC,IAAIC;QAG3B,MAAMC,aAAaC,qBAAqB,SAAS,CAACL;QAClD,IAAI,CAACI,WAAW,OAAO,EACnB,OAAOF,OAAO,IAAI,CAAC,IAAII,oBAAoBF,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO;QAGjF,MAAMG,WAAW,IAAI,CAAC,eAAe,CAAC,WAAW;QACjD,MAAMC,OAAOJ,WAAW,IAAI;QAG5B,MAAMK,qBAAqB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAMD,KAAK,IAAI;QAAC;QACvE,IAAIC,mBAAmB,IAAI,IACvB,OAAOP,OAAO,IAAI,CAAC,IAAIQ,gBAAgBF,KAAK,IAAI;QAGpD,MAAMG,OAAa;YACf,IAAIC;YACJ,MAAMJ,KAAK,IAAI;YACf,MAAMA,KAAK,IAAI;YACf,aAAaA,KAAK,WAAW;YAC7B,OAAOA,KAAK,KAAK;YACjB,QAAQR,MAAM,MAAM,IAAI;YACxB,WAAW,IAAIa,OAAO,WAAW;YACjC,WAAW;gBACP,IAAIN,SAAS,EAAE;gBACf,aAAaA,SAAS,WAAW;gBACjC,MAAMA,SAAS,IAAI;YACvB;YACA,QAAQ;QACZ;QAEA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIO,sBAAsB;YAAEH;YAAM,OAAOP,WAAW,IAAI;QAAC;QAG7D,MAAMW,SAAS,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAACJ;QAE5C,IAAII,OAAO,MAAM,IACb,OAAOb,OAAO,IAAI,CAACa,OAAO,KAAK;QAGnC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,qBAAqB;YAAEL;YAAM,OAAOP,WAAW,IAAI;QAAC;QAG5D,OAAOF,OAAO,EAAE,CAACS;IACrB;AACJ;AAEO,MAAMM,wBAAwBC,qBAAqB;IACtD,aAAaC;IACb,gBAAgBvB;IAChB,cAAc;QAACwB;QAAiBC;QAAgBC;KAAgB;AACpE"}
1
+ {"version":3,"file":"features/security/teams/CreateTeam/CreateTeamUseCase.js","sources":["../../../../../src/features/security/teams/CreateTeam/CreateTeamUseCase.ts"],"sourcesContent":["import { mdbid } from \"@webiny/utils\";\nimport { createImplementation } from \"@webiny/feature/api\";\nimport { Result } from \"@webiny/feature/api\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { CreateTeam } from \"./abstractions.js\";\nimport { TeamsRepository } from \"../shared/abstractions.js\";\nimport { RolesRepository } from \"../../roles/shared/abstractions.js\";\nimport { IdentityContext } from \"../../IdentityContext/abstractions.js\";\nimport { createTeamValidation } from \"./schema.js\";\nimport { TeamBeforeCreateEvent, TeamAfterCreateEvent } from \"./events.js\";\nimport type { Team, CreateTeamInput } from \"../shared/types.js\";\nimport { NotAuthorizedError, TeamExistsError, TeamValidationError } from \"../shared/errors.js\";\nimport { descriptionOnCreate } from \"../../shared/description.js\";\nimport { resolveRoleIdentifiers } from \"../shared/resolveRoleIdentifiers.js\";\n\nexport class CreateTeamUseCase implements CreateTeam.Interface {\n constructor(\n private identityContext: IdentityContext.Interface,\n private eventPublisher: EventPublisher.Interface,\n private repository: TeamsRepository.Interface,\n private rolesRepository: RolesRepository.Interface\n ) {}\n\n async execute(input: CreateTeamInput): Promise<Result<Team, CreateTeam.Error>> {\n const hasPermission = await this.identityContext.getPermission(\"security.team\");\n\n if (!hasPermission) {\n return Result.fail(new NotAuthorizedError());\n }\n\n const validation = createTeamValidation.safeParse(input);\n if (!validation.success) {\n return Result.fail(new TeamValidationError(validation.error.issues[0].message));\n }\n\n const identity = this.identityContext.getIdentity();\n const data = validation.data;\n\n // Check if team with same slug already exists\n const existingTeamResult = await this.repository.get({ slug: data.slug });\n if (existingTeamResult.isOk()) {\n return Result.fail(new TeamExistsError(data.slug));\n }\n\n // A slug here would be stored verbatim and then match no role, leaving a team that grants\n // nothing. Resolved before the entity is built so the events carry the stored IDs.\n const roleIdsResult = await resolveRoleIdentifiers(this.rolesRepository, data.roles);\n\n if (roleIdsResult.isFail()) {\n return Result.fail(roleIdsResult.error);\n }\n\n // Normalised up front so that null reaches neither the entity nor the published events.\n const createInput: CreateTeamInput = {\n ...data,\n description: descriptionOnCreate(data.description),\n roles: roleIdsResult.value\n };\n\n const team: Team = {\n id: mdbid(),\n name: createInput.name,\n slug: createInput.slug,\n description: descriptionOnCreate(createInput.description),\n roles: createInput.roles,\n system: input.system || false,\n createdOn: new Date().toISOString(),\n createdBy: {\n id: identity.id,\n displayName: identity.displayName,\n type: identity.type\n },\n plugin: false\n };\n\n await this.eventPublisher.publish(new TeamBeforeCreateEvent({ team, input: createInput }));\n\n const result = await this.repository.create(team);\n\n if (result.isFail()) {\n return Result.fail(result.error);\n }\n\n await this.eventPublisher.publish(new TeamAfterCreateEvent({ team, input: createInput }));\n\n return Result.ok(team);\n }\n}\n\nexport const CreateTeamUseCaseImpl = createImplementation({\n abstraction: CreateTeam,\n implementation: CreateTeamUseCase,\n dependencies: [IdentityContext, EventPublisher, TeamsRepository, RolesRepository]\n});\n"],"names":["CreateTeamUseCase","identityContext","eventPublisher","repository","rolesRepository","input","hasPermission","Result","NotAuthorizedError","validation","createTeamValidation","TeamValidationError","identity","data","existingTeamResult","TeamExistsError","roleIdsResult","resolveRoleIdentifiers","createInput","descriptionOnCreate","team","mdbid","Date","TeamBeforeCreateEvent","result","TeamAfterCreateEvent","CreateTeamUseCaseImpl","createImplementation","CreateTeam","IdentityContext","EventPublisher","TeamsRepository","RolesRepository"],"mappings":";;;;;;;;;;;;AAeO,MAAMA;IACT,YACYC,eAA0C,EAC1CC,cAAwC,EACxCC,UAAqC,EACrCC,eAA0C,CACpD;aAJUH,eAAe,GAAfA;aACAC,cAAc,GAAdA;aACAC,UAAU,GAAVA;aACAC,eAAe,GAAfA;IACT;IAEH,MAAM,QAAQC,KAAsB,EAA2C;QAC3E,MAAMC,gBAAgB,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAE/D,IAAI,CAACA,eACD,OAAOC,OAAO,IAAI,CAAC,IAAIC;QAG3B,MAAMC,aAAaC,qBAAqB,SAAS,CAACL;QAClD,IAAI,CAACI,WAAW,OAAO,EACnB,OAAOF,OAAO,IAAI,CAAC,IAAII,oBAAoBF,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO;QAGjF,MAAMG,WAAW,IAAI,CAAC,eAAe,CAAC,WAAW;QACjD,MAAMC,OAAOJ,WAAW,IAAI;QAG5B,MAAMK,qBAAqB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAMD,KAAK,IAAI;QAAC;QACvE,IAAIC,mBAAmB,IAAI,IACvB,OAAOP,OAAO,IAAI,CAAC,IAAIQ,gBAAgBF,KAAK,IAAI;QAKpD,MAAMG,gBAAgB,MAAMC,uBAAuB,IAAI,CAAC,eAAe,EAAEJ,KAAK,KAAK;QAEnF,IAAIG,cAAc,MAAM,IACpB,OAAOT,OAAO,IAAI,CAACS,cAAc,KAAK;QAI1C,MAAME,cAA+B;YACjC,GAAGL,IAAI;YACP,aAAaM,oBAAoBN,KAAK,WAAW;YACjD,OAAOG,cAAc,KAAK;QAC9B;QAEA,MAAMI,OAAa;YACf,IAAIC;YACJ,MAAMH,YAAY,IAAI;YACtB,MAAMA,YAAY,IAAI;YACtB,aAAaC,oBAAoBD,YAAY,WAAW;YACxD,OAAOA,YAAY,KAAK;YACxB,QAAQb,MAAM,MAAM,IAAI;YACxB,WAAW,IAAIiB,OAAO,WAAW;YACjC,WAAW;gBACP,IAAIV,SAAS,EAAE;gBACf,aAAaA,SAAS,WAAW;gBACjC,MAAMA,SAAS,IAAI;YACvB;YACA,QAAQ;QACZ;QAEA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAIW,sBAAsB;YAAEH;YAAM,OAAOF;QAAY;QAEvF,MAAMM,SAAS,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAACJ;QAE5C,IAAII,OAAO,MAAM,IACb,OAAOjB,OAAO,IAAI,CAACiB,OAAO,KAAK;QAGnC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAIC,qBAAqB;YAAEL;YAAM,OAAOF;QAAY;QAEtF,OAAOX,OAAO,EAAE,CAACa;IACrB;AACJ;AAEO,MAAMM,wBAAwBC,qBAAqB;IACtD,aAAaC;IACb,gBAAgB5B;IAChB,cAAc;QAAC6B;QAAiBC;QAAgBC;QAAiBC;KAAgB;AACrF"}
@@ -2,6 +2,6 @@ import { z } from "zod";
2
2
  export declare const createTeamValidation: z.ZodObject<{
3
3
  name: z.ZodString;
4
4
  slug: z.ZodString;
5
- description: z.ZodDefault<z.ZodOptional<z.ZodString>>;
5
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
6
6
  roles: z.ZodArray<z.ZodString>;
7
7
  }, z.core.$strip>;
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  const createTeamValidation = z.object({
3
3
  name: z.string().min(3),
4
4
  slug: z.string().min(3),
5
- description: z.string().max(500).optional().default(""),
5
+ description: z.string().max(500).nullish(),
6
6
  roles: z.array(z.string())
7
7
  });
8
8
  export { createTeamValidation };
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/teams/CreateTeam/schema.js","sources":["../../../../../src/features/security/teams/CreateTeam/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const createTeamValidation = z.object({\n name: z.string().min(3),\n slug: z.string().min(3),\n description: z.string().max(500).optional().default(\"\"),\n roles: z.array(z.string())\n});\n"],"names":["createTeamValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,QAAQ,GAAG,OAAO,CAAC;IACpD,OAAOA,EAAE,KAAK,CAACA,EAAE,MAAM;AAC3B"}
1
+ {"version":3,"file":"features/security/teams/CreateTeam/schema.js","sources":["../../../../../src/features/security/teams/CreateTeam/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const createTeamValidation = z.object({\n name: z.string().min(3),\n slug: z.string().min(3),\n // Nullable in the GraphQL schema - see `descriptionOnCreate` for why this is `.nullish()`.\n description: z.string().max(500).nullish(),\n roles: z.array(z.string())\n});\n"],"names":["createTeamValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IACrB,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC;IAErB,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,OAAO;IACxC,OAAOA,EAAE,KAAK,CAACA,EAAE,MAAM;AAC3B"}
@@ -1,6 +1,7 @@
1
1
  import { Result } from "@webiny/feature/api";
2
2
  import { UpdateTeam } from "./abstractions.js";
3
3
  import { TeamsRepository } from "../shared/abstractions.js";
4
+ import { RolesRepository } from "../../roles/shared/abstractions.js";
4
5
  import { IdentityContext } from "../../IdentityContext/abstractions.js";
5
6
  import { EventPublisher } from "../../../../features/eventPublisher/index.js";
6
7
  import type { Team, UpdateTeamInput } from "../shared/types.js";
@@ -8,7 +9,8 @@ export declare class UpdateTeamUseCase {
8
9
  private repository;
9
10
  private identityContext;
10
11
  private eventPublisher;
11
- constructor(repository: TeamsRepository.Interface, identityContext: IdentityContext.Interface, eventPublisher: EventPublisher.Interface);
12
+ private rolesRepository;
13
+ constructor(repository: TeamsRepository.Interface, identityContext: IdentityContext.Interface, eventPublisher: EventPublisher.Interface, rolesRepository: RolesRepository.Interface);
12
14
  execute(id: string, input: UpdateTeamInput): Promise<Result<Team, UpdateTeam.Error>>;
13
15
  }
14
16
  export declare const UpdateTeamUseCaseImpl: import("@webiny/di").Implementation<typeof UpdateTeamUseCase>;
@@ -1,16 +1,20 @@
1
1
  import { Result, createImplementation } from "@webiny/feature/api";
2
2
  import { UpdateTeam } from "./abstractions.js";
3
3
  import { TeamsRepository } from "../shared/abstractions.js";
4
+ import { RolesRepository } from "../../roles/shared/abstractions.js";
4
5
  import { IdentityContext } from "../../IdentityContext/abstractions.js";
5
6
  import { EventPublisher } from "../../../eventPublisher/index.js";
6
7
  import { updateTeamValidation } from "./schema.js";
7
8
  import { TeamAfterUpdateEvent, TeamBeforeUpdateEvent } from "./events.js";
9
+ import { descriptionOnUpdate } from "../../shared/description.js";
10
+ import { resolveRoleIdentifiers } from "../shared/resolveRoleIdentifiers.js";
8
11
  import { CannotUpdatePluginTeamsError, CannotUpdateSystemTeamsError, NotAuthorizedError, TeamValidationError } from "../shared/errors.js";
9
12
  class UpdateTeamUseCase {
10
- constructor(repository, identityContext, eventPublisher){
13
+ constructor(repository, identityContext, eventPublisher, rolesRepository){
11
14
  this.repository = repository;
12
15
  this.identityContext = identityContext;
13
16
  this.eventPublisher = eventPublisher;
17
+ this.rolesRepository = rolesRepository;
14
18
  }
15
19
  async execute(id, input) {
16
20
  const hasPermission = await this.identityContext.getPermission("security.team");
@@ -24,21 +28,31 @@ class UpdateTeamUseCase {
24
28
  const existingTeam = existingResult.value;
25
29
  if (existingTeam.system) return Result.fail(new CannotUpdateSystemTeamsError());
26
30
  if (existingTeam.plugin) return Result.fail(new CannotUpdatePluginTeamsError());
31
+ const { description, ...rest } = validation.data;
32
+ const changes = {
33
+ ...rest,
34
+ ...descriptionOnUpdate(description)
35
+ };
36
+ if (changes.roles) {
37
+ const roleIdsResult = await resolveRoleIdentifiers(this.rolesRepository, changes.roles);
38
+ if (roleIdsResult.isFail()) return Result.fail(roleIdsResult.error);
39
+ changes.roles = roleIdsResult.value;
40
+ }
27
41
  const updatedTeam = {
28
42
  ...existingTeam,
29
- ...validation.data
43
+ ...changes
30
44
  };
31
45
  await this.eventPublisher.publish(new TeamBeforeUpdateEvent({
32
46
  original: existingTeam,
33
47
  updated: updatedTeam,
34
- input: validation.data
48
+ input: changes
35
49
  }));
36
50
  const result = await this.repository.update(updatedTeam);
37
51
  if (result.isFail()) return Result.fail(result.error);
38
52
  await this.eventPublisher.publish(new TeamAfterUpdateEvent({
39
53
  original: existingTeam,
40
54
  updated: updatedTeam,
41
- input: validation.data
55
+ input: changes
42
56
  }));
43
57
  return Result.ok(updatedTeam);
44
58
  }
@@ -49,7 +63,8 @@ const UpdateTeamUseCaseImpl = createImplementation({
49
63
  dependencies: [
50
64
  TeamsRepository,
51
65
  IdentityContext,
52
- EventPublisher
66
+ EventPublisher,
67
+ RolesRepository
53
68
  ]
54
69
  });
55
70
  export { UpdateTeamUseCase, UpdateTeamUseCaseImpl };
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/teams/UpdateTeam/UpdateTeamUseCase.js","sources":["../../../../../src/features/security/teams/UpdateTeam/UpdateTeamUseCase.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/feature/api\";\nimport { Result } from \"@webiny/feature/api\";\nimport { UpdateTeam } from \"./abstractions.js\";\nimport { TeamsRepository } from \"../shared/abstractions.js\";\nimport { IdentityContext } from \"../../IdentityContext/abstractions.js\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { updateTeamValidation } from \"./schema.js\";\nimport { TeamBeforeUpdateEvent, TeamAfterUpdateEvent } from \"./events.js\";\nimport type { Team, UpdateTeamInput } from \"../shared/types.js\";\nimport {\n NotAuthorizedError,\n CannotUpdatePluginTeamsError,\n CannotUpdateSystemTeamsError,\n TeamValidationError\n} from \"../shared/errors.js\";\n\nexport class UpdateTeamUseCase {\n private repository: TeamsRepository.Interface;\n private identityContext: IdentityContext.Interface;\n private eventPublisher: EventPublisher.Interface;\n\n constructor(\n repository: TeamsRepository.Interface,\n identityContext: IdentityContext.Interface,\n eventPublisher: EventPublisher.Interface\n ) {\n this.repository = repository;\n this.identityContext = identityContext;\n this.eventPublisher = eventPublisher;\n }\n\n async execute(id: string, input: UpdateTeamInput): Promise<Result<Team, UpdateTeam.Error>> {\n const hasPermission = await this.identityContext.getPermission(\"security.team\");\n if (!hasPermission) {\n return Result.fail(new NotAuthorizedError());\n }\n\n const validation = updateTeamValidation.safeParse(input);\n if (!validation.success) {\n return Result.fail(new TeamValidationError(validation.error.issues[0].message));\n }\n\n const existingResult = await this.repository.get({ id });\n if (existingResult.isFail()) {\n return Result.fail(existingResult.error);\n }\n\n const existingTeam = existingResult.value;\n\n // Check if team is system team\n if (existingTeam.system) {\n return Result.fail(new CannotUpdateSystemTeamsError());\n }\n\n // Check if team is created via plugin\n if (existingTeam.plugin) {\n return Result.fail(new CannotUpdatePluginTeamsError());\n }\n\n const updatedTeam: Team = {\n ...existingTeam,\n ...validation.data\n };\n\n await this.eventPublisher.publish(\n new TeamBeforeUpdateEvent({\n original: existingTeam,\n updated: updatedTeam,\n input: validation.data\n })\n );\n\n const result = await this.repository.update(updatedTeam);\n\n if (result.isFail()) {\n return Result.fail(result.error);\n }\n\n await this.eventPublisher.publish(\n new TeamAfterUpdateEvent({\n original: existingTeam,\n updated: updatedTeam,\n input: validation.data\n })\n );\n\n return Result.ok(updatedTeam);\n }\n}\n\nexport const UpdateTeamUseCaseImpl = createImplementation({\n abstraction: UpdateTeam,\n implementation: UpdateTeamUseCase,\n dependencies: [TeamsRepository, IdentityContext, EventPublisher]\n});\n"],"names":["UpdateTeamUseCase","repository","identityContext","eventPublisher","id","input","hasPermission","Result","NotAuthorizedError","validation","updateTeamValidation","TeamValidationError","existingResult","existingTeam","CannotUpdateSystemTeamsError","CannotUpdatePluginTeamsError","updatedTeam","TeamBeforeUpdateEvent","result","TeamAfterUpdateEvent","UpdateTeamUseCaseImpl","createImplementation","UpdateTeam","TeamsRepository","IdentityContext","EventPublisher"],"mappings":";;;;;;;;AAgBO,MAAMA;IAKT,YACIC,UAAqC,EACrCC,eAA0C,EAC1CC,cAAwC,CAC1C;QACE,IAAI,CAAC,UAAU,GAAGF;QAClB,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,cAAc,GAAGC;IAC1B;IAEA,MAAM,QAAQC,EAAU,EAAEC,KAAsB,EAA2C;QACvF,MAAMC,gBAAgB,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAC/D,IAAI,CAACA,eACD,OAAOC,OAAO,IAAI,CAAC,IAAIC;QAG3B,MAAMC,aAAaC,qBAAqB,SAAS,CAACL;QAClD,IAAI,CAACI,WAAW,OAAO,EACnB,OAAOF,OAAO,IAAI,CAAC,IAAII,oBAAoBF,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO;QAGjF,MAAMG,iBAAiB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAER;QAAG;QACtD,IAAIQ,eAAe,MAAM,IACrB,OAAOL,OAAO,IAAI,CAACK,eAAe,KAAK;QAG3C,MAAMC,eAAeD,eAAe,KAAK;QAGzC,IAAIC,aAAa,MAAM,EACnB,OAAON,OAAO,IAAI,CAAC,IAAIO;QAI3B,IAAID,aAAa,MAAM,EACnB,OAAON,OAAO,IAAI,CAAC,IAAIQ;QAG3B,MAAMC,cAAoB;YACtB,GAAGH,YAAY;YACf,GAAGJ,WAAW,IAAI;QACtB;QAEA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIQ,sBAAsB;YACtB,UAAUJ;YACV,SAASG;YACT,OAAOP,WAAW,IAAI;QAC1B;QAGJ,MAAMS,SAAS,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAACF;QAE5C,IAAIE,OAAO,MAAM,IACb,OAAOX,OAAO,IAAI,CAACW,OAAO,KAAK;QAGnC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,qBAAqB;YACrB,UAAUN;YACV,SAASG;YACT,OAAOP,WAAW,IAAI;QAC1B;QAGJ,OAAOF,OAAO,EAAE,CAACS;IACrB;AACJ;AAEO,MAAMI,wBAAwBC,qBAAqB;IACtD,aAAaC;IACb,gBAAgBtB;IAChB,cAAc;QAACuB;QAAiBC;QAAiBC;KAAe;AACpE"}
1
+ {"version":3,"file":"features/security/teams/UpdateTeam/UpdateTeamUseCase.js","sources":["../../../../../src/features/security/teams/UpdateTeam/UpdateTeamUseCase.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/feature/api\";\nimport { Result } from \"@webiny/feature/api\";\nimport { UpdateTeam } from \"./abstractions.js\";\nimport { TeamsRepository } from \"../shared/abstractions.js\";\nimport { RolesRepository } from \"../../roles/shared/abstractions.js\";\nimport { IdentityContext } from \"../../IdentityContext/abstractions.js\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { updateTeamValidation } from \"./schema.js\";\nimport { TeamBeforeUpdateEvent, TeamAfterUpdateEvent } from \"./events.js\";\nimport type { Team, UpdateTeamInput } from \"../shared/types.js\";\nimport { descriptionOnUpdate } from \"../../shared/description.js\";\nimport { resolveRoleIdentifiers } from \"../shared/resolveRoleIdentifiers.js\";\nimport {\n NotAuthorizedError,\n CannotUpdatePluginTeamsError,\n CannotUpdateSystemTeamsError,\n TeamValidationError\n} from \"../shared/errors.js\";\n\nexport class UpdateTeamUseCase {\n private repository: TeamsRepository.Interface;\n private identityContext: IdentityContext.Interface;\n private eventPublisher: EventPublisher.Interface;\n private rolesRepository: RolesRepository.Interface;\n\n constructor(\n repository: TeamsRepository.Interface,\n identityContext: IdentityContext.Interface,\n eventPublisher: EventPublisher.Interface,\n rolesRepository: RolesRepository.Interface\n ) {\n this.repository = repository;\n this.identityContext = identityContext;\n this.eventPublisher = eventPublisher;\n this.rolesRepository = rolesRepository;\n }\n\n async execute(id: string, input: UpdateTeamInput): Promise<Result<Team, UpdateTeam.Error>> {\n const hasPermission = await this.identityContext.getPermission(\"security.team\");\n if (!hasPermission) {\n return Result.fail(new NotAuthorizedError());\n }\n\n const validation = updateTeamValidation.safeParse(input);\n if (!validation.success) {\n return Result.fail(new TeamValidationError(validation.error.issues[0].message));\n }\n\n const existingResult = await this.repository.get({ id });\n if (existingResult.isFail()) {\n return Result.fail(existingResult.error);\n }\n\n const existingTeam = existingResult.value;\n\n // Check if team is system team\n if (existingTeam.system) {\n return Result.fail(new CannotUpdateSystemTeamsError());\n }\n\n // Check if team is created via plugin\n if (existingTeam.plugin) {\n return Result.fail(new CannotUpdatePluginTeamsError());\n }\n\n // `description` is pulled out of the spread because it is the one field that accepts null.\n // Normalising it here keeps null out of both the entity and the published events, whose\n // input type declares `description?: string`.\n const { description, ...rest } = validation.data;\n\n const changes: UpdateTeamInput = {\n ...rest,\n ...descriptionOnUpdate(description)\n };\n\n /*\n * Only when the update mentions roles. An update that omits them must leave the stored ones\n * alone, and resolving an absent list would cost a read and write an empty array.\n */\n if (changes.roles) {\n const roleIdsResult = await resolveRoleIdentifiers(this.rolesRepository, changes.roles);\n\n if (roleIdsResult.isFail()) {\n return Result.fail(roleIdsResult.error);\n }\n\n changes.roles = roleIdsResult.value;\n }\n\n const updatedTeam: Team = {\n ...existingTeam,\n ...changes\n };\n\n await this.eventPublisher.publish(\n new TeamBeforeUpdateEvent({\n original: existingTeam,\n updated: updatedTeam,\n input: changes\n })\n );\n\n const result = await this.repository.update(updatedTeam);\n\n if (result.isFail()) {\n return Result.fail(result.error);\n }\n\n await this.eventPublisher.publish(\n new TeamAfterUpdateEvent({\n original: existingTeam,\n updated: updatedTeam,\n input: changes\n })\n );\n\n return Result.ok(updatedTeam);\n }\n}\n\nexport const UpdateTeamUseCaseImpl = createImplementation({\n abstraction: UpdateTeam,\n implementation: UpdateTeamUseCase,\n dependencies: [TeamsRepository, IdentityContext, EventPublisher, RolesRepository]\n});\n"],"names":["UpdateTeamUseCase","repository","identityContext","eventPublisher","rolesRepository","id","input","hasPermission","Result","NotAuthorizedError","validation","updateTeamValidation","TeamValidationError","existingResult","existingTeam","CannotUpdateSystemTeamsError","CannotUpdatePluginTeamsError","description","rest","changes","descriptionOnUpdate","roleIdsResult","resolveRoleIdentifiers","updatedTeam","TeamBeforeUpdateEvent","result","TeamAfterUpdateEvent","UpdateTeamUseCaseImpl","createImplementation","UpdateTeam","TeamsRepository","IdentityContext","EventPublisher","RolesRepository"],"mappings":";;;;;;;;;;;AAmBO,MAAMA;IAMT,YACIC,UAAqC,EACrCC,eAA0C,EAC1CC,cAAwC,EACxCC,eAA0C,CAC5C;QACE,IAAI,CAAC,UAAU,GAAGH;QAClB,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,cAAc,GAAGC;QACtB,IAAI,CAAC,eAAe,GAAGC;IAC3B;IAEA,MAAM,QAAQC,EAAU,EAAEC,KAAsB,EAA2C;QACvF,MAAMC,gBAAgB,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAC/D,IAAI,CAACA,eACD,OAAOC,OAAO,IAAI,CAAC,IAAIC;QAG3B,MAAMC,aAAaC,qBAAqB,SAAS,CAACL;QAClD,IAAI,CAACI,WAAW,OAAO,EACnB,OAAOF,OAAO,IAAI,CAAC,IAAII,oBAAoBF,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO;QAGjF,MAAMG,iBAAiB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAER;QAAG;QACtD,IAAIQ,eAAe,MAAM,IACrB,OAAOL,OAAO,IAAI,CAACK,eAAe,KAAK;QAG3C,MAAMC,eAAeD,eAAe,KAAK;QAGzC,IAAIC,aAAa,MAAM,EACnB,OAAON,OAAO,IAAI,CAAC,IAAIO;QAI3B,IAAID,aAAa,MAAM,EACnB,OAAON,OAAO,IAAI,CAAC,IAAIQ;QAM3B,MAAM,EAAEC,WAAW,EAAE,GAAGC,MAAM,GAAGR,WAAW,IAAI;QAEhD,MAAMS,UAA2B;YAC7B,GAAGD,IAAI;YACP,GAAGE,oBAAoBH,YAAY;QACvC;QAMA,IAAIE,QAAQ,KAAK,EAAE;YACf,MAAME,gBAAgB,MAAMC,uBAAuB,IAAI,CAAC,eAAe,EAAEH,QAAQ,KAAK;YAEtF,IAAIE,cAAc,MAAM,IACpB,OAAOb,OAAO,IAAI,CAACa,cAAc,KAAK;YAG1CF,QAAQ,KAAK,GAAGE,cAAc,KAAK;QACvC;QAEA,MAAME,cAAoB;YACtB,GAAGT,YAAY;YACf,GAAGK,OAAO;QACd;QAEA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIK,sBAAsB;YACtB,UAAUV;YACV,SAASS;YACT,OAAOJ;QACX;QAGJ,MAAMM,SAAS,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAACF;QAE5C,IAAIE,OAAO,MAAM,IACb,OAAOjB,OAAO,IAAI,CAACiB,OAAO,KAAK;QAGnC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,qBAAqB;YACrB,UAAUZ;YACV,SAASS;YACT,OAAOJ;QACX;QAGJ,OAAOX,OAAO,EAAE,CAACe;IACrB;AACJ;AAEO,MAAMI,wBAAwBC,qBAAqB;IACtD,aAAaC;IACb,gBAAgB7B;IAChB,cAAc;QAAC8B;QAAiBC;QAAiBC;QAAgBC;KAAgB;AACrF"}
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export declare const updateTeamValidation: z.ZodObject<{
3
3
  name: z.ZodOptional<z.ZodString>;
4
- description: z.ZodOptional<z.ZodString>;
4
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
5
5
  roles: z.ZodOptional<z.ZodArray<z.ZodString>>;
6
6
  }, z.core.$strip>;
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  const updateTeamValidation = z.object({
3
3
  name: z.string().min(3).optional(),
4
- description: z.string().max(500).optional(),
4
+ description: z.string().max(500).nullish(),
5
5
  roles: z.array(z.string()).optional()
6
6
  });
7
7
  export { updateTeamValidation };
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/teams/UpdateTeam/schema.js","sources":["../../../../../src/features/security/teams/UpdateTeam/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const updateTeamValidation = z.object({\n name: z.string().min(3).optional(),\n description: z.string().max(500).optional(),\n roles: z.array(z.string()).optional()\n});\n"],"names":["updateTeamValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ;IAChC,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,QAAQ;IACzC,OAAOA,EAAE,KAAK,CAACA,EAAE,MAAM,IAAI,QAAQ;AACvC"}
1
+ {"version":3,"file":"features/security/teams/UpdateTeam/schema.js","sources":["../../../../../src/features/security/teams/UpdateTeam/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const updateTeamValidation = z.object({\n name: z.string().min(3).optional(),\n // Nullable in the GraphQL schema - see `descriptionOnUpdate` for why this is `.nullish()` and\n // must not carry a `.transform()`.\n description: z.string().max(500).nullish(),\n roles: z.array(z.string()).optional()\n});\n"],"names":["updateTeamValidation","z"],"mappings":";AAEO,MAAMA,uBAAuBC,EAAE,MAAM,CAAC;IACzC,MAAMA,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ;IAGhC,aAAaA,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,OAAO;IACxC,OAAOA,EAAE,KAAK,CAACA,EAAE,MAAM,IAAI,QAAQ;AACvC"}
@@ -0,0 +1,17 @@
1
+ import { Result } from "@webiny/feature/api";
2
+ import { RolesRepository } from "../../roles/shared/abstractions.js";
3
+ import { TeamValidationError } from "./errors.js";
4
+ /**
5
+ * Maps whatever a caller used to name a role onto the role IDs a team stores.
6
+ *
7
+ * `team.roles` holds IDs and only IDs: GetPermissionsFromIdentity resolves them with `id_in` against
8
+ * the roles repository. A slug stored here matches nothing, so the team is created, reads correctly
9
+ * in the admin UI, and grants no permissions at all. Nothing previously caught that, because the
10
+ * field is typed `string[]` and the use case wrote it through untouched.
11
+ *
12
+ * Accepting both forms rather than rejecting slugs: which identifier a given field wants is not
13
+ * guessable from the outside, and a few lines away in the same area a team is identified by SLUG
14
+ * (folder permission targets are `team:<slug>`). An identifier that is neither is a genuine mistake
15
+ * and is reported.
16
+ */
17
+ export declare const resolveRoleIdentifiers: (rolesRepository: RolesRepository.Interface, identifiers: string[]) => Promise<Result<string[], TeamValidationError>>;
@@ -0,0 +1,32 @@
1
+ import { Result } from "@webiny/feature/api";
2
+ import { TeamValidationError } from "./errors.js";
3
+ const resolveRoleIdentifiers = async (rolesRepository, identifiers)=>{
4
+ if (0 === identifiers.length) return Result.ok([]);
5
+ const rolesResult = await rolesRepository.list({});
6
+ if (rolesResult.isFail()) return Result.fail(new TeamValidationError(`Could not load roles: ${rolesResult.error}`));
7
+ const byId = new Map(rolesResult.value.map((role)=>[
8
+ role.id,
9
+ role.id
10
+ ]));
11
+ const bySlug = new Map(rolesResult.value.map((role)=>[
12
+ role.slug,
13
+ role.id
14
+ ]));
15
+ const unknown = [];
16
+ const roleIds = identifiers.map((identifier)=>{
17
+ const resolved = byId.get(identifier) ?? bySlug.get(identifier);
18
+ if (!resolved) {
19
+ unknown.push(identifier);
20
+ return identifier;
21
+ }
22
+ return resolved;
23
+ });
24
+ if (unknown.length > 0) {
25
+ const names = unknown.map((identifier)=>`"${identifier}"`).join(", ");
26
+ return Result.fail(new TeamValidationError(`Not a known role id or slug: ${names}.`));
27
+ }
28
+ return Result.ok(roleIds);
29
+ };
30
+ export { resolveRoleIdentifiers };
31
+
32
+ //# sourceMappingURL=resolveRoleIdentifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/security/teams/shared/resolveRoleIdentifiers.js","sources":["../../../../../src/features/security/teams/shared/resolveRoleIdentifiers.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { RolesRepository } from \"../../roles/shared/abstractions.js\";\nimport { TeamValidationError } from \"./errors.js\";\n\n/**\n * Maps whatever a caller used to name a role onto the role IDs a team stores.\n *\n * `team.roles` holds IDs and only IDs: GetPermissionsFromIdentity resolves them with `id_in` against\n * the roles repository. A slug stored here matches nothing, so the team is created, reads correctly\n * in the admin UI, and grants no permissions at all. Nothing previously caught that, because the\n * field is typed `string[]` and the use case wrote it through untouched.\n *\n * Accepting both forms rather than rejecting slugs: which identifier a given field wants is not\n * guessable from the outside, and a few lines away in the same area a team is identified by SLUG\n * (folder permission targets are `team:<slug>`). An identifier that is neither is a genuine mistake\n * and is reported.\n */\nexport const resolveRoleIdentifiers = async (\n rolesRepository: RolesRepository.Interface,\n identifiers: string[]\n): Promise<Result<string[], TeamValidationError>> => {\n if (identifiers.length === 0) {\n return Result.ok([]);\n }\n\n /*\n * Every role, in one read. `id_in` and `slug_in` are ANDed by the repository, so they cannot\n * express \"either of these\", and a project has few enough roles for the distinction not to\n * matter. Plugin-defined roles are included, which a filtered query would also have to cover.\n */\n const rolesResult = await rolesRepository.list({});\n\n if (rolesResult.isFail()) {\n return Result.fail(new TeamValidationError(`Could not load roles: ${rolesResult.error}`));\n }\n\n const byId = new Map(rolesResult.value.map(role => [role.id, role.id]));\n const bySlug = new Map(rolesResult.value.map(role => [role.slug, role.id]));\n\n const unknown: string[] = [];\n\n // ID first, so a slug that happens to equal another role's ID cannot shadow it.\n const roleIds = identifiers.map(identifier => {\n const resolved = byId.get(identifier) ?? bySlug.get(identifier);\n\n if (!resolved) {\n unknown.push(identifier);\n return identifier;\n }\n\n return resolved;\n });\n\n if (unknown.length > 0) {\n const names = unknown.map(identifier => `\"${identifier}\"`).join(\", \");\n return Result.fail(new TeamValidationError(`Not a known role id or slug: ${names}.`));\n }\n\n return Result.ok(roleIds);\n};\n"],"names":["resolveRoleIdentifiers","rolesRepository","identifiers","Result","rolesResult","TeamValidationError","byId","Map","role","bySlug","unknown","roleIds","identifier","resolved","names"],"mappings":";;AAiBO,MAAMA,yBAAyB,OAClCC,iBACAC;IAEA,IAAIA,AAAuB,MAAvBA,YAAY,MAAM,EAClB,OAAOC,OAAO,EAAE,CAAC,EAAE;IAQvB,MAAMC,cAAc,MAAMH,gBAAgB,IAAI,CAAC,CAAC;IAEhD,IAAIG,YAAY,MAAM,IAClB,OAAOD,OAAO,IAAI,CAAC,IAAIE,oBAAoB,CAAC,sBAAsB,EAAED,YAAY,KAAK,EAAE;IAG3F,MAAME,OAAO,IAAIC,IAAIH,YAAY,KAAK,CAAC,GAAG,CAACI,CAAAA,OAAQ;YAACA,KAAK,EAAE;YAAEA,KAAK,EAAE;SAAC;IACrE,MAAMC,SAAS,IAAIF,IAAIH,YAAY,KAAK,CAAC,GAAG,CAACI,CAAAA,OAAQ;YAACA,KAAK,IAAI;YAAEA,KAAK,EAAE;SAAC;IAEzE,MAAME,UAAoB,EAAE;IAG5B,MAAMC,UAAUT,YAAY,GAAG,CAACU,CAAAA;QAC5B,MAAMC,WAAWP,KAAK,GAAG,CAACM,eAAeH,OAAO,GAAG,CAACG;QAEpD,IAAI,CAACC,UAAU;YACXH,QAAQ,IAAI,CAACE;YACb,OAAOA;QACX;QAEA,OAAOC;IACX;IAEA,IAAIH,QAAQ,MAAM,GAAG,GAAG;QACpB,MAAMI,QAAQJ,QAAQ,GAAG,CAACE,CAAAA,aAAc,CAAC,CAAC,EAAEA,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC;QAChE,OAAOT,OAAO,IAAI,CAAC,IAAIE,oBAAoB,CAAC,6BAA6B,EAAES,MAAM,CAAC,CAAC;IACvF;IAEA,OAAOX,OAAO,EAAE,CAACQ;AACrB"}
@@ -0,0 +1,8 @@
1
+ import { Slugify } from "./abstractions.js";
2
+ declare class DefaultSlugifyImpl implements Slugify.Interface {
3
+ execute(value: string): string;
4
+ }
5
+ export declare const DefaultSlugify: typeof DefaultSlugifyImpl & {
6
+ __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").ISlugify>;
7
+ };
8
+ export {};
@@ -0,0 +1,20 @@
1
+ import slugify from "slugify";
2
+ import { Slugify } from "./abstractions.js";
3
+ const DEFAULT_OPTIONS = {
4
+ replacement: "-",
5
+ lower: true,
6
+ remove: /[*#?<>_{}[\]+~.()'"!:;@]/g,
7
+ trim: false
8
+ };
9
+ class DefaultSlugifyImpl {
10
+ execute(value) {
11
+ return slugify(value, DEFAULT_OPTIONS);
12
+ }
13
+ }
14
+ const DefaultSlugify = Slugify.createImplementation({
15
+ implementation: DefaultSlugifyImpl,
16
+ dependencies: []
17
+ });
18
+ export { DefaultSlugify };
19
+
20
+ //# sourceMappingURL=DefaultSlugify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/slugify/DefaultSlugify.js","sources":["../../../src/features/slugify/DefaultSlugify.ts"],"sourcesContent":["import baseSlugify from \"slugify\";\nimport { Slugify } from \"./abstractions.js\";\n\n/**\n * Webiny's canonical slug options. These were previously duplicated at every call site; keeping them\n * in one private place means projects can change slug generation everywhere at once by decorating\n * Slugify — not by passing per-call options, so the abstraction stays independent of this library.\n */\nconst DEFAULT_OPTIONS = {\n replacement: \"-\",\n lower: true,\n remove: /[*#?<>_{}[\\]+~.()'\"!:;@]/g,\n trim: false\n};\n\nclass DefaultSlugifyImpl implements Slugify.Interface {\n execute(value: string): string {\n return baseSlugify(value, DEFAULT_OPTIONS);\n }\n}\n\nexport const DefaultSlugify = Slugify.createImplementation({\n implementation: DefaultSlugifyImpl,\n dependencies: []\n});\n"],"names":["DEFAULT_OPTIONS","DefaultSlugifyImpl","value","baseSlugify","DefaultSlugify","Slugify"],"mappings":";;AAQA,MAAMA,kBAAkB;IACpB,aAAa;IACb,OAAO;IACP,QAAQ;IACR,MAAM;AACV;AAEA,MAAMC;IACF,QAAQC,KAAa,EAAU;QAC3B,OAAOC,QAAYD,OAAOF;IAC9B;AACJ;AAEO,MAAMI,iBAAiBC,QAAQ,oBAAoB,CAAC;IACvD,gBAAgBJ;IAChB,cAAc,EAAE;AACpB"}
@@ -0,0 +1,12 @@
1
+ export interface ISlugify {
2
+ /**
3
+ * Turns a value into a URL-friendly slug using Webiny's canonical options. This is the
4
+ * fine-grained seam behind `StringFormatter.slugify()` — decorate `Slugify` alone to change slug
5
+ * logic without touching the rest of the string formatter.
6
+ */
7
+ execute(value: string): string;
8
+ }
9
+ export declare const Slugify: import("@webiny/di").Abstraction<ISlugify>;
10
+ export declare namespace Slugify {
11
+ type Interface = ISlugify;
12
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const Slugify = createAbstraction("Slugify");
3
+ export { Slugify };
4
+
5
+ //# sourceMappingURL=abstractions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/slugify/abstractions.js","sources":["../../../src/features/slugify/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\nexport interface ISlugify {\n /**\n * Turns a value into a URL-friendly slug using Webiny's canonical options. This is the\n * fine-grained seam behind `StringFormatter.slugify()` — decorate `Slugify` alone to change slug\n * logic without touching the rest of the string formatter.\n */\n execute(value: string): string;\n}\n\nexport const Slugify = createAbstraction<ISlugify>(\"Slugify\");\n\nexport namespace Slugify {\n export type Interface = ISlugify;\n}\n"],"names":["Slugify","createAbstraction"],"mappings":";AAWO,MAAMA,UAAUC,kBAA4B"}
@@ -0,0 +1,4 @@
1
+ export declare const SlugifyFeature: {
2
+ name: string;
3
+ register(container: import("@webiny/di").Container): void;
4
+ };
@@ -0,0 +1,11 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { DefaultSlugify } from "./DefaultSlugify.js";
3
+ const SlugifyFeature = createFeature({
4
+ name: "SlugifyFeature",
5
+ register (container) {
6
+ container.register(DefaultSlugify);
7
+ }
8
+ });
9
+ export { SlugifyFeature };
10
+
11
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/slugify/feature.js","sources":["../../../src/features/slugify/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { DefaultSlugify } from \"./DefaultSlugify.js\";\n\nexport const SlugifyFeature = createFeature({\n name: \"SlugifyFeature\",\n register(container) {\n container.register(DefaultSlugify);\n }\n});\n"],"names":["SlugifyFeature","createFeature","container","DefaultSlugify"],"mappings":";;AAGO,MAAMA,iBAAiBC,cAAc;IACxC,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
@@ -0,0 +1,3 @@
1
+ export { Slugify } from "./abstractions.js";
2
+ export type { ISlugify } from "./abstractions.js";
3
+ export { SlugifyFeature } from "./feature.js";
@@ -0,0 +1,2 @@
1
+ export { Slugify } from "./abstractions.js";
2
+ export { SlugifyFeature } from "./feature.js";
@@ -0,0 +1,17 @@
1
+ import { Slugify } from "../../features/slugify/abstractions.js";
2
+ import { StringFormatter } from "./abstractions.js";
3
+ /**
4
+ * The string formatter is the consumer-facing API for string transforms. It keeps each transform's
5
+ * logic in its own fine-grained, decoratable feature (e.g. `Slugify`) and just delegates to it, so a
6
+ * project can change one transform without reimplementing the whole formatter. More methods will be
7
+ * added here over time.
8
+ */
9
+ declare class DefaultStringFormatterImpl implements StringFormatter.Interface {
10
+ private readonly slugifier;
11
+ constructor(slugifier: Slugify.Interface);
12
+ slugify(value: string): string;
13
+ }
14
+ export declare const DefaultStringFormatter: typeof DefaultStringFormatterImpl & {
15
+ __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IStringFormatter>;
16
+ };
17
+ export {};