@webiny/api-aco 0.0.0-unstable.99666aeb00 → 0.0.0-unstable.a9593f74dd

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 (138) hide show
  1. package/apps/AcoApp.d.ts +5 -0
  2. package/apps/AcoApp.js +56 -21
  3. package/apps/AcoApp.js.map +1 -1
  4. package/apps/AcoApps.js +7 -8
  5. package/apps/AcoApps.js.map +1 -1
  6. package/apps/app.gql.js +6 -4
  7. package/apps/app.gql.js.map +1 -1
  8. package/apps/index.js +3 -1
  9. package/apps/index.js.map +1 -1
  10. package/createAcoContext.d.ts +5 -1
  11. package/createAcoContext.js +119 -25
  12. package/createAcoContext.js.map +1 -1
  13. package/createAcoGraphQL.js +6 -2
  14. package/createAcoGraphQL.js.map +1 -1
  15. package/createAcoHooks.js +9 -3
  16. package/createAcoHooks.js.map +1 -1
  17. package/createAcoModels.js +6 -19
  18. package/createAcoModels.js.map +1 -1
  19. package/createAcoStorageOperations.d.ts +0 -1
  20. package/createAcoStorageOperations.js +10 -7
  21. package/createAcoStorageOperations.js.map +1 -1
  22. package/fields/index.js +3 -1
  23. package/fields/index.js.map +1 -1
  24. package/fields/location.js +3 -1
  25. package/fields/location.js.map +1 -1
  26. package/filter/filter.crud.d.ts +3 -0
  27. package/filter/filter.crud.js +94 -0
  28. package/filter/filter.crud.js.map +1 -0
  29. package/filter/filter.gql.d.ts +3 -0
  30. package/filter/filter.gql.js +153 -0
  31. package/filter/filter.gql.js.map +1 -0
  32. package/filter/filter.model.d.ts +2 -0
  33. package/filter/filter.model.js +125 -0
  34. package/filter/filter.model.js.map +1 -0
  35. package/filter/filter.so.d.ts +3 -0
  36. package/filter/filter.so.js +98 -0
  37. package/filter/filter.so.js.map +1 -0
  38. package/filter/filter.types.d.ts +95 -0
  39. package/filter/filter.types.js +14 -0
  40. package/filter/filter.types.js.map +1 -0
  41. package/folder/folder.crud.d.ts +8 -1
  42. package/folder/folder.crud.js +194 -15
  43. package/folder/folder.crud.js.map +1 -1
  44. package/folder/folder.gql.js +85 -8
  45. package/folder/folder.gql.js.map +1 -1
  46. package/folder/folder.model.d.ts +1 -3
  47. package/folder/folder.model.js +54 -7
  48. package/folder/folder.model.js.map +1 -1
  49. package/folder/folder.so.js +38 -18
  50. package/folder/folder.so.js.map +1 -1
  51. package/folder/folder.types.d.ts +28 -2
  52. package/folder/folder.types.js +3 -1
  53. package/folder/folder.types.js.map +1 -1
  54. package/folder/onFolderBeforeDeleteAco.hook.d.ts +2 -0
  55. package/folder/onFolderBeforeDeleteAco.hook.js +67 -0
  56. package/folder/onFolderBeforeDeleteAco.hook.js.map +1 -0
  57. package/folder/onFolderBeforeDeleteFm.hook.d.ts +2 -0
  58. package/folder/onFolderBeforeDeleteFm.hook.js +49 -0
  59. package/folder/onFolderBeforeDeleteFm.hook.js.map +1 -0
  60. package/folder/onFolderBeforeDeleteHcms.hook.d.ts +2 -0
  61. package/folder/onFolderBeforeDeleteHcms.hook.js +56 -0
  62. package/folder/onFolderBeforeDeleteHcms.hook.js.map +1 -0
  63. package/index.d.ts +5 -1
  64. package/index.js +14 -4
  65. package/index.js.map +1 -1
  66. package/package.json +31 -27
  67. package/plugins/AcoAppModifierPlugin.js +4 -5
  68. package/plugins/AcoAppModifierPlugin.js.map +1 -1
  69. package/plugins/AcoAppRegisterPlugin.js +4 -5
  70. package/plugins/AcoAppRegisterPlugin.js.map +1 -1
  71. package/plugins/index.js +3 -1
  72. package/plugins/index.js.map +1 -1
  73. package/record/graphql/createAppResolvers.js +26 -19
  74. package/record/graphql/createAppResolvers.js.map +1 -1
  75. package/record/graphql/createAppSchema.js +10 -5
  76. package/record/graphql/createAppSchema.js.map +1 -1
  77. package/record/record.crud.js +3 -1
  78. package/record/record.crud.js.map +1 -1
  79. package/record/record.gql.js +3 -1
  80. package/record/record.gql.js.map +1 -1
  81. package/record/record.model.d.ts +1 -3
  82. package/record/record.model.js +7 -7
  83. package/record/record.model.js.map +1 -1
  84. package/record/record.so.js +35 -23
  85. package/record/record.so.js.map +1 -1
  86. package/record/record.types.d.ts +4 -3
  87. package/record/record.types.js +3 -1
  88. package/record/record.types.js.map +1 -1
  89. package/types.d.ts +23 -6
  90. package/types.js +25 -4
  91. package/types.js.map +1 -1
  92. package/utils/FolderLevelPermissions.d.ts +71 -0
  93. package/utils/FolderLevelPermissions.js +353 -0
  94. package/utils/FolderLevelPermissions.js.map +1 -0
  95. package/utils/acoRecordId.js +3 -1
  96. package/utils/acoRecordId.js.map +1 -1
  97. package/utils/createListSort.js +3 -1
  98. package/utils/createListSort.js.map +1 -1
  99. package/utils/createModelField.js +3 -1
  100. package/utils/createModelField.js.map +1 -1
  101. package/utils/createOperationsWrapper.js +3 -1
  102. package/utils/createOperationsWrapper.js.map +1 -1
  103. package/utils/decorators/CmsEntriesCrudDecorators.d.ts +11 -0
  104. package/utils/decorators/CmsEntriesCrudDecorators.js +232 -0
  105. package/utils/decorators/CmsEntriesCrudDecorators.js.map +1 -0
  106. package/utils/decorators/constants.d.ts +1 -0
  107. package/utils/decorators/constants.js +10 -0
  108. package/utils/decorators/constants.js.map +1 -0
  109. package/utils/decorators/where.d.ts +14 -0
  110. package/utils/decorators/where.js +52 -0
  111. package/utils/decorators/where.js.map +1 -0
  112. package/utils/ensureAuthentication.d.ts +2 -0
  113. package/utils/{checkPermissions.js → ensureAuthentication.js} +5 -3
  114. package/utils/ensureAuthentication.js.map +1 -0
  115. package/utils/getFolderAndItsAncestors.d.ts +2 -2
  116. package/utils/getFolderAndItsAncestors.js +16 -11
  117. package/utils/getFolderAndItsAncestors.js.map +1 -1
  118. package/utils/isInstallationPending.js +3 -1
  119. package/utils/isInstallationPending.js.map +1 -1
  120. package/utils/modelFactory.d.ts +1 -3
  121. package/utils/modelFactory.js +7 -10
  122. package/utils/modelFactory.js.map +1 -1
  123. package/utils/pickEntryFieldValues.d.ts +3 -0
  124. package/utils/pickEntryFieldValues.js +31 -0
  125. package/utils/pickEntryFieldValues.js.map +1 -0
  126. package/utils/resolve.js +3 -1
  127. package/utils/resolve.js.map +1 -1
  128. package/folder/onFolderBeforeDelete.hook.d.ts +0 -2
  129. package/folder/onFolderBeforeDelete.hook.js +0 -66
  130. package/folder/onFolderBeforeDelete.hook.js.map +0 -1
  131. package/utils/checkPermissions.d.ts +0 -2
  132. package/utils/checkPermissions.js.map +0 -1
  133. package/utils/fieldResolver.d.ts +0 -16
  134. package/utils/fieldResolver.js +0 -44
  135. package/utils/fieldResolver.js.map +0 -1
  136. package/utils/getFieldValues.d.ts +0 -5
  137. package/utils/getFieldValues.js +0 -16
  138. package/utils/getFieldValues.js.map +0 -1
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.createFilterOperations = void 0;
8
+ var _omit = _interopRequireDefault(require("lodash/omit"));
9
+ var _error = _interopRequireDefault(require("@webiny/error"));
10
+ var _filter = require("./filter.model");
11
+ var _createListSort = require("../utils/createListSort");
12
+ var _createOperationsWrapper = require("../utils/createOperationsWrapper");
13
+ var _pickEntryFieldValues = require("../utils/pickEntryFieldValues");
14
+ var _constants = require("@webiny/api-headless-cms/constants");
15
+ const createFilterOperations = params => {
16
+ const {
17
+ cms,
18
+ security
19
+ } = params;
20
+ const {
21
+ withModel
22
+ } = (0, _createOperationsWrapper.createOperationsWrapper)({
23
+ ...params,
24
+ modelName: _filter.FILTER_MODEL_ID
25
+ });
26
+ return {
27
+ getFilter({
28
+ id
29
+ }) {
30
+ return withModel(async model => {
31
+ const entry = await cms.getEntryById(model, id);
32
+ if (!entry) {
33
+ throw new _error.default("Could not load filter.", "GET_FILTER_ERROR", {
34
+ id
35
+ });
36
+ }
37
+ return (0, _pickEntryFieldValues.pickEntryFieldValues)(entry);
38
+ });
39
+ },
40
+ listFilters(params) {
41
+ return withModel(async model => {
42
+ const {
43
+ sort,
44
+ where
45
+ } = params;
46
+ const createdBy = security.getIdentity().id;
47
+ const [entries, meta] = await cms.listLatestEntries(model, {
48
+ ...params,
49
+ sort: (0, _createListSort.createListSort)(sort),
50
+ where: {
51
+ ...({
52
+ ...where,
53
+ createdBy
54
+ } || {})
55
+ }
56
+ });
57
+ return [entries.map(_pickEntryFieldValues.pickEntryFieldValues), meta];
58
+ });
59
+ },
60
+ createFilter({
61
+ data
62
+ }) {
63
+ return withModel(async model => {
64
+ const entry = await cms.createEntry(model, data);
65
+ return (0, _pickEntryFieldValues.pickEntryFieldValues)(entry);
66
+ });
67
+ },
68
+ updateFilter({
69
+ id,
70
+ data
71
+ }) {
72
+ return withModel(async model => {
73
+ const original = await cms.getEntryById(model, id);
74
+ const input = {
75
+ /**
76
+ * We are omitting the standard entry meta fields:
77
+ * we don't want to override them with the ones coming from the `original` entry.
78
+ */
79
+ ...(0, _omit.default)(original, _constants.ENTRY_META_FIELDS),
80
+ ...data
81
+ };
82
+ const entry = await cms.updateEntry(model, original.id, input);
83
+ return (0, _pickEntryFieldValues.pickEntryFieldValues)(entry);
84
+ });
85
+ },
86
+ deleteFilter({
87
+ id
88
+ }) {
89
+ return withModel(async model => {
90
+ await cms.deleteEntry(model, id);
91
+ return true;
92
+ });
93
+ }
94
+ };
95
+ };
96
+ exports.createFilterOperations = createFilterOperations;
97
+
98
+ //# sourceMappingURL=filter.so.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_omit","_interopRequireDefault","require","_error","_filter","_createListSort","_createOperationsWrapper","_pickEntryFieldValues","_constants","createFilterOperations","params","cms","security","withModel","createOperationsWrapper","modelName","FILTER_MODEL_ID","getFilter","id","model","entry","getEntryById","WebinyError","pickEntryFieldValues","listFilters","sort","where","createdBy","getIdentity","entries","meta","listLatestEntries","createListSort","map","createFilter","data","createEntry","updateFilter","original","input","omit","ENTRY_META_FIELDS","updateEntry","deleteFilter","deleteEntry","exports"],"sources":["filter.so.ts"],"sourcesContent":["import omit from \"lodash/omit\";\nimport WebinyError from \"@webiny/error\";\nimport { FILTER_MODEL_ID } from \"./filter.model\";\nimport { CreateAcoStorageOperationsParams } from \"~/createAcoStorageOperations\";\nimport { createListSort } from \"~/utils/createListSort\";\nimport { createOperationsWrapper } from \"~/utils/createOperationsWrapper\";\nimport { pickEntryFieldValues } from \"~/utils/pickEntryFieldValues\";\nimport { AcoFilterStorageOperations, Filter } from \"./filter.types\";\nimport { ENTRY_META_FIELDS } from \"@webiny/api-headless-cms/constants\";\n\nexport const createFilterOperations = (\n params: CreateAcoStorageOperationsParams\n): AcoFilterStorageOperations => {\n const { cms, security } = params;\n\n const { withModel } = createOperationsWrapper({\n ...params,\n modelName: FILTER_MODEL_ID\n });\n\n return {\n getFilter({ id }) {\n return withModel(async model => {\n const entry = await cms.getEntryById(model, id);\n\n if (!entry) {\n throw new WebinyError(\"Could not load filter.\", \"GET_FILTER_ERROR\", {\n id\n });\n }\n\n return pickEntryFieldValues(entry);\n });\n },\n listFilters(params) {\n return withModel(async model => {\n const { sort, where } = params;\n const createdBy = security.getIdentity().id;\n\n const [entries, meta] = await cms.listLatestEntries(model, {\n ...params,\n sort: createListSort(sort),\n where: {\n ...({ ...where, createdBy } || {})\n }\n });\n\n return [entries.map(pickEntryFieldValues<Filter>), meta];\n });\n },\n createFilter({ data }) {\n return withModel(async model => {\n const entry = await cms.createEntry(model, data);\n return pickEntryFieldValues(entry);\n });\n },\n updateFilter({ id, data }) {\n return withModel(async model => {\n const original = await cms.getEntryById(model, id);\n\n const input = {\n /**\n * We are omitting the standard entry meta fields:\n * we don't want to override them with the ones coming from the `original` entry.\n */\n ...omit(original, ENTRY_META_FIELDS),\n ...data\n };\n\n const entry = await cms.updateEntry(model, original.id, input);\n return pickEntryFieldValues(entry);\n });\n },\n deleteFilter({ id }) {\n return withModel(async model => {\n await cms.deleteEntry(model, id);\n return true;\n });\n }\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { AcoBaseFields, ListMeta, ListSort } from "../types";
2
+ import { Topic } from "@webiny/pubsub/types";
3
+ export declare enum Operation {
4
+ AND = "AND",
5
+ OR = "OR"
6
+ }
7
+ export interface GroupFilter {
8
+ field: string;
9
+ condition: string;
10
+ value: string;
11
+ }
12
+ export interface Group {
13
+ operation: Operation;
14
+ filters: GroupFilter[];
15
+ }
16
+ export interface Filter extends AcoBaseFields {
17
+ name: string;
18
+ description?: string;
19
+ namespace: string;
20
+ operation: Operation;
21
+ groups: Group[];
22
+ }
23
+ export interface ListFiltersWhere {
24
+ namespace: string;
25
+ }
26
+ export interface ListFiltersParams {
27
+ where: ListFiltersWhere;
28
+ sort?: ListSort;
29
+ limit?: number;
30
+ after?: string | null;
31
+ }
32
+ export declare type CreateFilterParams = Pick<Filter, "id" | "name" | "description" | "namespace" | "operation" | "groups">;
33
+ export interface UpdateFilterParams {
34
+ name?: string;
35
+ description?: string;
36
+ namespace?: string;
37
+ operation?: Operation;
38
+ groups?: Group[];
39
+ }
40
+ export interface DeleteFilterParams {
41
+ id: string;
42
+ }
43
+ export interface StorageOperationsGetFilterParams {
44
+ id: string;
45
+ }
46
+ export declare type StorageOperationsListFiltersParams = ListFiltersParams;
47
+ export interface StorageOperationsCreateFilterParams {
48
+ data: CreateFilterParams;
49
+ }
50
+ export interface StorageOperationsUpdateFilterParams {
51
+ id: string;
52
+ data: UpdateFilterParams;
53
+ }
54
+ export declare type StorageOperationsDeleteFilterParams = DeleteFilterParams;
55
+ export interface OnFilterBeforeCreateTopicParams {
56
+ input: CreateFilterParams;
57
+ }
58
+ export interface OnFilterAfterCreateTopicParams {
59
+ filter: Filter;
60
+ }
61
+ export interface OnFilterBeforeUpdateTopicParams {
62
+ original: Filter;
63
+ input: Record<string, any>;
64
+ }
65
+ export interface OnFilterAfterUpdateTopicParams {
66
+ original: Filter;
67
+ filter: Filter;
68
+ input: Record<string, any>;
69
+ }
70
+ export interface OnFilterBeforeDeleteTopicParams {
71
+ filter: Filter;
72
+ }
73
+ export interface OnFilterAfterDeleteTopicParams {
74
+ filter: Filter;
75
+ }
76
+ export interface AcoFilterCrud {
77
+ get(id: string): Promise<Filter>;
78
+ list(params: ListFiltersParams): Promise<[Filter[], ListMeta]>;
79
+ create(data: CreateFilterParams): Promise<Filter>;
80
+ update(id: string, data: UpdateFilterParams): Promise<Filter>;
81
+ delete(id: string): Promise<boolean>;
82
+ onFilterBeforeCreate: Topic<OnFilterBeforeCreateTopicParams>;
83
+ onFilterAfterCreate: Topic<OnFilterAfterCreateTopicParams>;
84
+ onFilterBeforeUpdate: Topic<OnFilterBeforeUpdateTopicParams>;
85
+ onFilterAfterUpdate: Topic<OnFilterAfterUpdateTopicParams>;
86
+ onFilterBeforeDelete: Topic<OnFilterBeforeDeleteTopicParams>;
87
+ onFilterAfterDelete: Topic<OnFilterAfterDeleteTopicParams>;
88
+ }
89
+ export interface AcoFilterStorageOperations {
90
+ getFilter(params: StorageOperationsGetFilterParams): Promise<Filter>;
91
+ listFilters(params: StorageOperationsListFiltersParams): Promise<[Filter[], ListMeta]>;
92
+ createFilter(params: StorageOperationsCreateFilterParams): Promise<Filter>;
93
+ updateFilter(params: StorageOperationsUpdateFilterParams): Promise<Filter>;
94
+ deleteFilter(params: StorageOperationsDeleteFilterParams): Promise<boolean>;
95
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Operation = void 0;
7
+ let Operation = /*#__PURE__*/function (Operation) {
8
+ Operation["AND"] = "AND";
9
+ Operation["OR"] = "OR";
10
+ return Operation;
11
+ }({});
12
+ exports.Operation = Operation;
13
+
14
+ //# sourceMappingURL=filter.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Operation","exports"],"sources":["filter.types.ts"],"sourcesContent":["import { AcoBaseFields, ListMeta, ListSort } from \"~/types\";\nimport { Topic } from \"@webiny/pubsub/types\";\n\nexport enum Operation {\n AND = \"AND\",\n OR = \"OR\"\n}\n\nexport interface GroupFilter {\n field: string;\n condition: string;\n value: string;\n}\n\nexport interface Group {\n operation: Operation;\n filters: GroupFilter[];\n}\n\nexport interface Filter extends AcoBaseFields {\n name: string;\n description?: string;\n namespace: string;\n operation: Operation;\n groups: Group[];\n}\n\nexport interface ListFiltersWhere {\n namespace: string;\n}\n\nexport interface ListFiltersParams {\n where: ListFiltersWhere;\n sort?: ListSort;\n limit?: number;\n after?: string | null;\n}\n\nexport type CreateFilterParams = Pick<\n Filter,\n \"id\" | \"name\" | \"description\" | \"namespace\" | \"operation\" | \"groups\"\n>;\n\nexport interface UpdateFilterParams {\n name?: string;\n description?: string;\n namespace?: string;\n operation?: Operation;\n groups?: Group[];\n}\n\nexport interface DeleteFilterParams {\n id: string;\n}\n\nexport interface StorageOperationsGetFilterParams {\n id: string;\n}\n\nexport type StorageOperationsListFiltersParams = ListFiltersParams;\n\nexport interface StorageOperationsCreateFilterParams {\n data: CreateFilterParams;\n}\n\nexport interface StorageOperationsUpdateFilterParams {\n id: string;\n data: UpdateFilterParams;\n}\n\nexport type StorageOperationsDeleteFilterParams = DeleteFilterParams;\n\nexport interface OnFilterBeforeCreateTopicParams {\n input: CreateFilterParams;\n}\n\nexport interface OnFilterAfterCreateTopicParams {\n filter: Filter;\n}\n\nexport interface OnFilterBeforeUpdateTopicParams {\n original: Filter;\n input: Record<string, any>;\n}\n\nexport interface OnFilterAfterUpdateTopicParams {\n original: Filter;\n filter: Filter;\n input: Record<string, any>;\n}\n\nexport interface OnFilterBeforeDeleteTopicParams {\n filter: Filter;\n}\n\nexport interface OnFilterAfterDeleteTopicParams {\n filter: Filter;\n}\n\nexport interface AcoFilterCrud {\n get(id: string): Promise<Filter>;\n list(params: ListFiltersParams): Promise<[Filter[], ListMeta]>;\n create(data: CreateFilterParams): Promise<Filter>;\n update(id: string, data: UpdateFilterParams): Promise<Filter>;\n delete(id: string): Promise<boolean>;\n onFilterBeforeCreate: Topic<OnFilterBeforeCreateTopicParams>;\n onFilterAfterCreate: Topic<OnFilterAfterCreateTopicParams>;\n onFilterBeforeUpdate: Topic<OnFilterBeforeUpdateTopicParams>;\n onFilterAfterUpdate: Topic<OnFilterAfterUpdateTopicParams>;\n onFilterBeforeDelete: Topic<OnFilterBeforeDeleteTopicParams>;\n onFilterAfterDelete: Topic<OnFilterAfterDeleteTopicParams>;\n}\n\nexport interface AcoFilterStorageOperations {\n getFilter(params: StorageOperationsGetFilterParams): Promise<Filter>;\n listFilters(params: StorageOperationsListFiltersParams): Promise<[Filter[], ListMeta]>;\n createFilter(params: StorageOperationsCreateFilterParams): Promise<Filter>;\n updateFilter(params: StorageOperationsUpdateFilterParams): Promise<Filter>;\n deleteFilter(params: StorageOperationsDeleteFilterParams): Promise<boolean>;\n}\n"],"mappings":";;;;;;IAGYA,SAAS,0BAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAAA,OAATA,SAAS;AAAA;AAAAC,OAAA,CAAAD,SAAA,GAAAA,SAAA"}
@@ -1,3 +1,10 @@
1
1
  import { CreateAcoParams } from "../types";
2
2
  import { AcoFolderCrud } from "./folder.types";
3
- export declare const createFolderCrudMethods: ({ storageOperations }: CreateAcoParams) => AcoFolderCrud;
3
+ import { AdminUser } from "@webiny/api-admin-users/types";
4
+ import { Team } from "@webiny/api-security/types";
5
+ interface CreateFolderCrudMethodsParams extends CreateAcoParams {
6
+ listAdminUsers: () => Promise<AdminUser[]>;
7
+ listTeams: () => Promise<Team[]>;
8
+ }
9
+ export declare const createFolderCrudMethods: ({ storageOperations, folderLevelPermissions, listAdminUsers, listTeams }: CreateFolderCrudMethodsParams) => AcoFolderCrud;
10
+ export {};
@@ -1,13 +1,21 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.createFolderCrudMethods = void 0;
7
8
  var _pubsub = require("@webiny/pubsub");
9
+ var _validation = require("@webiny/validation");
8
10
  var _getFolderAndItsAncestors = require("../utils/getFolderAndItsAncestors");
11
+ var _NotAuthorizedError = _interopRequireDefault(require("@webiny/api-security/NotAuthorizedError"));
12
+ var _error = _interopRequireDefault(require("@webiny/error"));
13
+ const FIXED_FOLDER_LISTING_LIMIT = 10_000;
9
14
  const createFolderCrudMethods = ({
10
- storageOperations
15
+ storageOperations,
16
+ folderLevelPermissions,
17
+ listAdminUsers,
18
+ listTeams
11
19
  }) => {
12
20
  // create
13
21
  const onFolderBeforeCreate = (0, _pubsub.createTopic)("aco.onFolderBeforeCreate");
@@ -29,20 +37,81 @@ const createFolderCrudMethods = ({
29
37
  onFolderBeforeDelete,
30
38
  onFolderAfterDelete,
31
39
  async get(id) {
32
- return storageOperations.getFolder({
40
+ const folder = await storageOperations.getFolder({
33
41
  id
34
42
  });
43
+ await folderLevelPermissions.ensureCanAccessFolder({
44
+ folder,
45
+ rwd: "r"
46
+ });
47
+ await folderLevelPermissions.assignFolderPermissions(folder);
48
+ return folder;
35
49
  },
36
50
  async list(params) {
37
- return storageOperations.listFolders(params);
51
+ // No matter what was the limit set in the params, initially, we always retrieve
52
+ // all folders. The limit is then applied with the filtered folders list below.
53
+ const filteredFolders = await folderLevelPermissions.listAllFoldersWithPermissions(params.where.type).then(filteredFolders => {
54
+ // If `parentId` was included in the `where` clause, we need to filter the folders.
55
+ // TODO: we might want to incorporate this into the `listAllFoldersWithPermissions` method.
56
+ if (params.where.parentId) {
57
+ // Filter by parent ID.
58
+ return filteredFolders.filter(folder => folder.parentId === params.where.parentId);
59
+ }
60
+ return filteredFolders;
61
+ });
62
+ const totalCount = filteredFolders.length;
63
+ let hasMoreItems = false;
64
+ let cursor = null;
65
+
66
+ // Apply cursor/limit params.
67
+ if (params.after) {
68
+ const afterListItemIndex = filteredFolders.findIndex(folder => folder.id === params.after);
69
+ if (afterListItemIndex >= 0) {
70
+ // Remove all items below the "after" item.
71
+ filteredFolders.splice(0, afterListItemIndex + 1);
72
+ }
73
+ }
74
+ hasMoreItems = !!params.limit && filteredFolders.length > params.limit;
75
+ if (hasMoreItems) {
76
+ cursor = filteredFolders[params.limit - 1]?.id || null;
77
+ filteredFolders.splice(params.limit);
78
+ }
79
+ return [filteredFolders, {
80
+ totalCount,
81
+ hasMoreItems,
82
+ cursor
83
+ }];
84
+ },
85
+ async listAll(params) {
86
+ return this.list({
87
+ ...params,
88
+ limit: FIXED_FOLDER_LISTING_LIMIT
89
+ });
38
90
  },
39
91
  async create(data) {
92
+ let canCreateFolder = false;
93
+ if (data.parentId) {
94
+ const parentFolder = await storageOperations.getFolder({
95
+ id: data.parentId
96
+ });
97
+ canCreateFolder = await folderLevelPermissions.canAccessFolder({
98
+ folder: parentFolder,
99
+ rwd: "w"
100
+ });
101
+ } else {
102
+ canCreateFolder = await folderLevelPermissions.canCreateFolderInRoot();
103
+ }
104
+ if (!canCreateFolder) {
105
+ throw new _NotAuthorizedError.default();
106
+ }
40
107
  await onFolderBeforeCreate.publish({
41
108
  input: data
42
109
  });
43
110
  const folder = await storageOperations.createFolder({
44
111
  data
45
112
  });
113
+ folderLevelPermissions.invalidateCache();
114
+ await folderLevelPermissions.assignFolderPermissions(folder);
46
115
  await onFolderAfterCreate.publish({
47
116
  folder
48
117
  });
@@ -52,6 +121,63 @@ const createFolderCrudMethods = ({
52
121
  const original = await storageOperations.getFolder({
53
122
  id
54
123
  });
124
+ const canUpdateFolder = await folderLevelPermissions.canAccessFolder({
125
+ folder: original,
126
+ rwd: "w"
127
+ });
128
+ if (!canUpdateFolder) {
129
+ throw new _NotAuthorizedError.default();
130
+ }
131
+
132
+ // Validate data.
133
+ if (Array.isArray(data.permissions)) {
134
+ data.permissions.forEach(permission => {
135
+ const targetIsValid = permission.target.startsWith("admin:") || permission.target.startsWith("team:");
136
+ if (!targetIsValid) {
137
+ throw new Error(`Permission target "${permission.target}" is not valid.`);
138
+ }
139
+ if (permission.inheritedFrom) {
140
+ throw new Error(`Permission "inheritedFrom" cannot be set manually.`);
141
+ }
142
+ });
143
+ }
144
+
145
+ // Parent change is not allowed if the user doesn't have access to the new parent.
146
+ if (data.parentId && data.parentId !== original.parentId) {
147
+ try {
148
+ // Getting the parent folder will throw an error if the user doesn't have access.
149
+ await this.get(data.parentId);
150
+ } catch (e) {
151
+ if (e instanceof _NotAuthorizedError.default) {
152
+ throw new _error.default(`Cannot move folder to a new parent because you don't have access to the new parent.`, "CANNOT_MOVE_FOLDER_TO_NEW_PARENT");
153
+ }
154
+
155
+ // If we didn't receive the expected error, we still want to throw it.
156
+ throw e;
157
+ }
158
+ }
159
+
160
+ // Let's prepare a custom folder permissions list, where the folder contains the updated data.
161
+ const customFoldersList = await folderLevelPermissions.listAllFolders(original.type).then(folders => {
162
+ const foldersClone = structuredClone(folders);
163
+ return foldersClone.map(folder => {
164
+ if (folder.id === id) {
165
+ Object.assign(folder, data);
166
+ }
167
+ return folder;
168
+ });
169
+ });
170
+ const stillHasAccess = await folderLevelPermissions.canAccessFolder({
171
+ folder: {
172
+ id,
173
+ type: original.type
174
+ },
175
+ rwd: "w",
176
+ foldersList: customFoldersList
177
+ });
178
+ if (!stillHasAccess) {
179
+ throw new _error.default(`Cannot continue because you would loose access to this folder.`, "CANNOT_LOOSE_FOLDER_ACCESS");
180
+ }
55
181
  await onFolderBeforeUpdate.publish({
56
182
  original,
57
183
  input: {
@@ -71,12 +197,18 @@ const createFolderCrudMethods = ({
71
197
  },
72
198
  folder
73
199
  });
200
+ folderLevelPermissions.invalidateCache();
201
+ await folderLevelPermissions.assignFolderPermissions(folder);
74
202
  return folder;
75
203
  },
76
204
  async delete(id) {
77
205
  const folder = await storageOperations.getFolder({
78
206
  id
79
207
  });
208
+ await folderLevelPermissions.ensureCanAccessFolder({
209
+ folder,
210
+ rwd: "d"
211
+ });
80
212
  await onFolderBeforeDelete.publish({
81
213
  folder
82
214
  });
@@ -88,23 +220,70 @@ const createFolderCrudMethods = ({
88
220
  });
89
221
  return true;
90
222
  },
91
- async getFolderWithAncestors(id) {
92
- const {
93
- type
94
- } = await storageOperations.getFolder({
95
- id
96
- });
97
- const [folders] = await storageOperations.listFolders({
223
+ async getAncestors(folder) {
224
+ const [folders] = await this.listAll({
98
225
  where: {
99
- type
100
- },
101
- limit: 10000
226
+ type: folder.type
227
+ }
102
228
  });
103
229
  return (0, _getFolderAndItsAncestors.getFolderAndItsAncestors)({
104
- id,
230
+ folder,
105
231
  folders
106
232
  });
233
+ },
234
+ /**
235
+ * @deprecated use `getAncestors` instead
236
+ */
237
+ async getFolderWithAncestors(id) {
238
+ const folder = await this.get(id);
239
+ return this.getAncestors(folder);
240
+ },
241
+ async listFolderLevelPermissionsTargets() {
242
+ const adminUsers = await listAdminUsers();
243
+ const teams = await listTeams();
244
+ const teamTargets = teams.map(team => ({
245
+ id: team.id,
246
+ type: "team",
247
+ target: `team:${team.id}`,
248
+ name: team.name || "",
249
+ meta: {}
250
+ }));
251
+ const adminUserTargets = adminUsers.map(user => {
252
+ let name = user.displayName;
253
+ if (!name) {
254
+ // For backwards compatibility, we also want to try concatenating first and last name.
255
+ name = [user.firstName, user.lastName].filter(Boolean).join(" ");
256
+ }
257
+
258
+ // We're doing the validation because, with non-Cognito IdPs (Okta, Auth0), the email
259
+ // field might actually contain a non-email value: `id:${IdP_Identity_ID}`. In that case,
260
+ // let's not assign anything to the `email` field.
261
+ let email = user.email;
262
+ try {
263
+ _validation.validation.validateSync(email, "email");
264
+ } catch {
265
+ email = null;
266
+ }
267
+ const image = user.avatar?.src || null;
268
+ return {
269
+ id: user.id,
270
+ type: "admin",
271
+ target: `admin:${user.id}`,
272
+ name,
273
+ meta: {
274
+ email,
275
+ image
276
+ }
277
+ };
278
+ });
279
+ const results = [...teamTargets, ...adminUserTargets];
280
+ const meta = {
281
+ totalCount: results.length
282
+ };
283
+ return [results, meta];
107
284
  }
108
285
  };
109
286
  };
110
- exports.createFolderCrudMethods = createFolderCrudMethods;
287
+ exports.createFolderCrudMethods = createFolderCrudMethods;
288
+
289
+ //# sourceMappingURL=folder.crud.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["createFolderCrudMethods","storageOperations","onFolderBeforeCreate","createTopic","onFolderAfterCreate","onFolderBeforeUpdate","onFolderAfterUpdate","onFolderBeforeDelete","onFolderAfterDelete","get","id","getFolder","list","params","listFolders","create","data","publish","input","folder","createFolder","update","original","updateFolder","delete","deleteFolder","getFolderWithAncestors","type","folders","where","limit","getFolderAndItsAncestors"],"sources":["folder.crud.ts"],"sourcesContent":["import { createTopic } from \"@webiny/pubsub\";\n\nimport { CreateAcoParams } from \"~/types\";\nimport {\n AcoFolderCrud,\n OnFolderAfterCreateTopicParams,\n OnFolderAfterDeleteTopicParams,\n OnFolderAfterUpdateTopicParams,\n OnFolderBeforeCreateTopicParams,\n OnFolderBeforeDeleteTopicParams,\n OnFolderBeforeUpdateTopicParams\n} from \"./folder.types\";\n\nimport { getFolderAndItsAncestors } from \"~/utils/getFolderAndItsAncestors\";\n\nexport const createFolderCrudMethods = ({ storageOperations }: CreateAcoParams): AcoFolderCrud => {\n // create\n const onFolderBeforeCreate = createTopic<OnFolderBeforeCreateTopicParams>(\n \"aco.onFolderBeforeCreate\"\n );\n const onFolderAfterCreate =\n createTopic<OnFolderAfterCreateTopicParams>(\"aco.onFolderAfterCreate\");\n // update\n const onFolderBeforeUpdate = createTopic<OnFolderBeforeUpdateTopicParams>(\n \"aco.onFolderBeforeUpdate\"\n );\n const onFolderAfterUpdate =\n createTopic<OnFolderAfterUpdateTopicParams>(\"aco.onFolderAfterUpdate\");\n // delete\n const onFolderBeforeDelete = createTopic<OnFolderBeforeDeleteTopicParams>(\n \"aco.onFolderBeforeDelete\"\n );\n const onFolderAfterDelete =\n createTopic<OnFolderAfterDeleteTopicParams>(\"aco.onFolderAfterDelete\");\n\n return {\n /**\n * Lifecycle events\n */\n onFolderBeforeCreate,\n onFolderAfterCreate,\n onFolderBeforeUpdate,\n onFolderAfterUpdate,\n onFolderBeforeDelete,\n onFolderAfterDelete,\n async get(id) {\n return storageOperations.getFolder({ id });\n },\n async list(params) {\n return storageOperations.listFolders(params);\n },\n async create(data) {\n await onFolderBeforeCreate.publish({ input: data });\n const folder = await storageOperations.createFolder({ data });\n await onFolderAfterCreate.publish({ folder });\n return folder;\n },\n async update(id, data) {\n const original = await storageOperations.getFolder({ id });\n await onFolderBeforeUpdate.publish({ original, input: { id, data } });\n const folder = await storageOperations.updateFolder({ id, data });\n await onFolderAfterUpdate.publish({ original, input: { id, data }, folder });\n return folder;\n },\n async delete(id: string) {\n const folder = await storageOperations.getFolder({ id });\n await onFolderBeforeDelete.publish({ folder });\n await storageOperations.deleteFolder({ id });\n await onFolderAfterDelete.publish({ folder });\n return true;\n },\n async getFolderWithAncestors(id: string) {\n const { type } = await storageOperations.getFolder({ id });\n const [folders] = await storageOperations.listFolders({\n where: {\n type\n },\n limit: 10000\n });\n return getFolderAndItsAncestors({ id, folders });\n }\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"names":["_pubsub","require","_validation","_getFolderAndItsAncestors","_NotAuthorizedError","_interopRequireDefault","_error","FIXED_FOLDER_LISTING_LIMIT","createFolderCrudMethods","storageOperations","folderLevelPermissions","listAdminUsers","listTeams","onFolderBeforeCreate","createTopic","onFolderAfterCreate","onFolderBeforeUpdate","onFolderAfterUpdate","onFolderBeforeDelete","onFolderAfterDelete","get","id","folder","getFolder","ensureCanAccessFolder","rwd","assignFolderPermissions","list","params","filteredFolders","listAllFoldersWithPermissions","where","type","then","parentId","filter","totalCount","length","hasMoreItems","cursor","after","afterListItemIndex","findIndex","splice","limit","listAll","create","data","canCreateFolder","parentFolder","canAccessFolder","canCreateFolderInRoot","NotAuthorizedError","publish","input","createFolder","invalidateCache","update","original","canUpdateFolder","Array","isArray","permissions","forEach","permission","targetIsValid","target","startsWith","Error","inheritedFrom","e","WError","customFoldersList","listAllFolders","folders","foldersClone","structuredClone","map","Object","assign","stillHasAccess","foldersList","updateFolder","delete","deleteFolder","getAncestors","getFolderAndItsAncestors","getFolderWithAncestors","listFolderLevelPermissionsTargets","adminUsers","teams","teamTargets","team","name","meta","adminUserTargets","user","displayName","firstName","lastName","Boolean","join","email","validation","validateSync","image","avatar","src","results","exports"],"sources":["folder.crud.ts"],"sourcesContent":["import { createTopic } from \"@webiny/pubsub\";\nimport { validation } from \"@webiny/validation\";\nimport { CreateAcoParams, Folder } from \"~/types\";\nimport {\n AcoFolderCrud,\n OnFolderAfterCreateTopicParams,\n OnFolderAfterDeleteTopicParams,\n OnFolderAfterUpdateTopicParams,\n OnFolderBeforeCreateTopicParams,\n OnFolderBeforeDeleteTopicParams,\n OnFolderBeforeUpdateTopicParams\n} from \"./folder.types\";\n\nimport { getFolderAndItsAncestors } from \"~/utils/getFolderAndItsAncestors\";\nimport NotAuthorizedError from \"@webiny/api-security/NotAuthorizedError\";\nimport { AdminUser } from \"@webiny/api-admin-users/types\";\nimport { Team } from \"@webiny/api-security/types\";\nimport WError from \"@webiny/error\";\n\nconst FIXED_FOLDER_LISTING_LIMIT = 10_000;\n\ninterface CreateFolderCrudMethodsParams extends CreateAcoParams {\n listAdminUsers: () => Promise<AdminUser[]>;\n listTeams: () => Promise<Team[]>;\n}\n\nexport const createFolderCrudMethods = ({\n storageOperations,\n folderLevelPermissions,\n listAdminUsers,\n listTeams\n}: CreateFolderCrudMethodsParams): AcoFolderCrud => {\n // create\n const onFolderBeforeCreate = createTopic<OnFolderBeforeCreateTopicParams>(\n \"aco.onFolderBeforeCreate\"\n );\n const onFolderAfterCreate =\n createTopic<OnFolderAfterCreateTopicParams>(\"aco.onFolderAfterCreate\");\n // update\n const onFolderBeforeUpdate = createTopic<OnFolderBeforeUpdateTopicParams>(\n \"aco.onFolderBeforeUpdate\"\n );\n const onFolderAfterUpdate =\n createTopic<OnFolderAfterUpdateTopicParams>(\"aco.onFolderAfterUpdate\");\n // delete\n const onFolderBeforeDelete = createTopic<OnFolderBeforeDeleteTopicParams>(\n \"aco.onFolderBeforeDelete\"\n );\n const onFolderAfterDelete =\n createTopic<OnFolderAfterDeleteTopicParams>(\"aco.onFolderAfterDelete\");\n\n return {\n /**\n * Lifecycle events\n */\n onFolderBeforeCreate,\n onFolderAfterCreate,\n onFolderBeforeUpdate,\n onFolderAfterUpdate,\n onFolderBeforeDelete,\n onFolderAfterDelete,\n\n async get(id) {\n const folder = await storageOperations.getFolder({ id });\n\n await folderLevelPermissions.ensureCanAccessFolder({\n folder,\n rwd: \"r\"\n });\n\n await folderLevelPermissions.assignFolderPermissions(folder);\n return folder;\n },\n async list(params) {\n // No matter what was the limit set in the params, initially, we always retrieve\n // all folders. The limit is then applied with the filtered folders list below.\n const filteredFolders = await folderLevelPermissions\n .listAllFoldersWithPermissions(params.where.type)\n .then(filteredFolders => {\n // If `parentId` was included in the `where` clause, we need to filter the folders.\n // TODO: we might want to incorporate this into the `listAllFoldersWithPermissions` method.\n if (params.where.parentId) {\n // Filter by parent ID.\n return filteredFolders.filter(\n folder => folder.parentId === params.where.parentId\n );\n }\n return filteredFolders;\n });\n\n const totalCount = filteredFolders.length;\n let hasMoreItems = false;\n let cursor: string | null = null;\n\n // Apply cursor/limit params.\n if (params.after) {\n const afterListItemIndex = filteredFolders.findIndex(\n folder => folder.id === params.after\n );\n if (afterListItemIndex >= 0) {\n // Remove all items below the \"after\" item.\n filteredFolders.splice(0, afterListItemIndex + 1);\n }\n }\n\n hasMoreItems = !!params.limit && filteredFolders.length > params.limit;\n\n if (hasMoreItems) {\n cursor = filteredFolders[params.limit! - 1]?.id || null;\n filteredFolders.splice(params.limit!);\n }\n\n return [filteredFolders, { totalCount, hasMoreItems, cursor }];\n },\n\n async listAll(params) {\n return this.list({ ...params, limit: FIXED_FOLDER_LISTING_LIMIT });\n },\n\n async create(data) {\n let canCreateFolder = false;\n if (data.parentId) {\n const parentFolder = await storageOperations.getFolder({ id: data.parentId });\n canCreateFolder = await folderLevelPermissions.canAccessFolder({\n folder: parentFolder,\n rwd: \"w\"\n });\n } else {\n canCreateFolder = await folderLevelPermissions.canCreateFolderInRoot();\n }\n\n if (!canCreateFolder) {\n throw new NotAuthorizedError();\n }\n\n await onFolderBeforeCreate.publish({ input: data });\n const folder = await storageOperations.createFolder({ data });\n\n folderLevelPermissions.invalidateCache();\n await folderLevelPermissions.assignFolderPermissions(folder);\n\n await onFolderAfterCreate.publish({ folder });\n\n return folder;\n },\n\n async update(id, data) {\n const original = await storageOperations.getFolder({ id });\n\n const canUpdateFolder = await folderLevelPermissions.canAccessFolder({\n folder: original,\n rwd: \"w\"\n });\n\n if (!canUpdateFolder) {\n throw new NotAuthorizedError();\n }\n\n // Validate data.\n if (Array.isArray(data.permissions)) {\n data.permissions.forEach(permission => {\n const targetIsValid =\n permission.target.startsWith(\"admin:\") ||\n permission.target.startsWith(\"team:\");\n if (!targetIsValid) {\n throw new Error(`Permission target \"${permission.target}\" is not valid.`);\n }\n\n if (permission.inheritedFrom) {\n throw new Error(`Permission \"inheritedFrom\" cannot be set manually.`);\n }\n });\n }\n\n // Parent change is not allowed if the user doesn't have access to the new parent.\n if (data.parentId && data.parentId !== original.parentId) {\n try {\n // Getting the parent folder will throw an error if the user doesn't have access.\n await this.get(data.parentId);\n } catch (e) {\n if (e instanceof NotAuthorizedError) {\n throw new WError(\n `Cannot move folder to a new parent because you don't have access to the new parent.`,\n \"CANNOT_MOVE_FOLDER_TO_NEW_PARENT\"\n );\n }\n\n // If we didn't receive the expected error, we still want to throw it.\n throw e;\n }\n }\n\n // Let's prepare a custom folder permissions list, where the folder contains the updated data.\n const customFoldersList = await folderLevelPermissions\n .listAllFolders(original.type)\n .then(folders => {\n const foldersClone = structuredClone<Folder[]>(folders);\n return foldersClone.map(folder => {\n if (folder.id === id) {\n Object.assign(folder, data);\n }\n return folder;\n });\n });\n\n const stillHasAccess = await folderLevelPermissions.canAccessFolder({\n folder: { id, type: original.type },\n rwd: \"w\",\n foldersList: customFoldersList\n });\n\n if (!stillHasAccess) {\n throw new WError(\n `Cannot continue because you would loose access to this folder.`,\n \"CANNOT_LOOSE_FOLDER_ACCESS\"\n );\n }\n\n await onFolderBeforeUpdate.publish({ original, input: { id, data } });\n const folder = await storageOperations.updateFolder({ id, data });\n await onFolderAfterUpdate.publish({ original, input: { id, data }, folder });\n\n folderLevelPermissions.invalidateCache();\n await folderLevelPermissions.assignFolderPermissions(folder);\n return folder;\n },\n\n async delete(id: string) {\n const folder = await storageOperations.getFolder({ id });\n\n await folderLevelPermissions.ensureCanAccessFolder({\n folder,\n rwd: \"d\"\n });\n\n await onFolderBeforeDelete.publish({ folder });\n await storageOperations.deleteFolder({ id });\n await onFolderAfterDelete.publish({ folder });\n return true;\n },\n\n async getAncestors(folder: Folder) {\n const [folders] = await this.listAll({ where: { type: folder.type } });\n return getFolderAndItsAncestors({ folder, folders });\n },\n\n /**\n * @deprecated use `getAncestors` instead\n */\n async getFolderWithAncestors(id: string) {\n const folder = await this.get(id);\n return this.getAncestors(folder);\n },\n\n async listFolderLevelPermissionsTargets() {\n const adminUsers = await listAdminUsers();\n const teams = await listTeams();\n\n const teamTargets = teams.map(team => ({\n id: team.id,\n type: \"team\",\n target: `team:${team.id}`,\n name: team.name || \"\",\n meta: {}\n }));\n\n const adminUserTargets = adminUsers.map(user => {\n let name = user.displayName;\n if (!name) {\n // For backwards compatibility, we also want to try concatenating first and last name.\n name = [user.firstName, user.lastName].filter(Boolean).join(\" \");\n }\n\n // We're doing the validation because, with non-Cognito IdPs (Okta, Auth0), the email\n // field might actually contain a non-email value: `id:${IdP_Identity_ID}`. In that case,\n // let's not assign anything to the `email` field.\n let email: string | null = user.email;\n try {\n validation.validateSync(email, \"email\");\n } catch {\n email = null;\n }\n\n const image = user.avatar?.src || null;\n\n return {\n id: user.id,\n type: \"admin\",\n target: `admin:${user.id}`,\n name,\n meta: {\n email,\n image\n }\n };\n });\n\n const results = [...teamTargets, ...adminUserTargets];\n const meta = { totalCount: results.length };\n\n return [results, meta];\n }\n };\n};\n"],"mappings":"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"}