@unchainedshop/api 3.0.0-rc9 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/lib/api-index.d.ts +3 -2
  2. package/lib/api-index.d.ts.map +1 -1
  3. package/lib/api-index.js +3 -2
  4. package/lib/api-index.js.map +1 -1
  5. package/lib/context.d.ts +17 -21
  6. package/lib/context.d.ts.map +1 -1
  7. package/lib/context.js +2 -2
  8. package/lib/context.js.map +1 -1
  9. package/lib/errors.d.ts +1 -0
  10. package/lib/errors.d.ts.map +1 -1
  11. package/lib/errors.js +1 -0
  12. package/lib/errors.js.map +1 -1
  13. package/lib/express/createBulkImportMiddleware.d.ts.map +1 -1
  14. package/lib/express/createBulkImportMiddleware.js +3 -3
  15. package/lib/express/createBulkImportMiddleware.js.map +1 -1
  16. package/lib/express/createERCMetadataMiddleware.d.ts.map +1 -1
  17. package/lib/express/createERCMetadataMiddleware.js +9 -8
  18. package/lib/express/createERCMetadataMiddleware.js.map +1 -1
  19. package/lib/express/index.d.ts +2 -0
  20. package/lib/express/index.d.ts.map +1 -1
  21. package/lib/express/index.js +11 -2
  22. package/lib/express/index.js.map +1 -1
  23. package/lib/fastify/ercMetadataHandler.d.ts.map +1 -1
  24. package/lib/fastify/ercMetadataHandler.js +2 -1
  25. package/lib/fastify/ercMetadataHandler.js.map +1 -1
  26. package/lib/fastify/index.d.ts +4 -1
  27. package/lib/fastify/index.d.ts.map +1 -1
  28. package/lib/fastify/index.js +47 -15
  29. package/lib/fastify/index.js.map +1 -1
  30. package/lib/loaders/index.d.ts +103 -59
  31. package/lib/loaders/index.d.ts.map +1 -1
  32. package/lib/loaders/index.js +173 -111
  33. package/lib/loaders/index.js.map +1 -1
  34. package/lib/locale-context.d.ts +1 -6
  35. package/lib/locale-context.d.ts.map +1 -1
  36. package/lib/locale-context.js +2 -4
  37. package/lib/locale-context.js.map +1 -1
  38. package/lib/resolvers/mutations/accounts/createUser.js +1 -1
  39. package/lib/resolvers/mutations/accounts/createUser.js.map +1 -1
  40. package/lib/resolvers/mutations/accounts/impersonate.d.ts +9 -0
  41. package/lib/resolvers/mutations/accounts/impersonate.d.ts.map +1 -0
  42. package/lib/resolvers/mutations/accounts/impersonate.js +17 -0
  43. package/lib/resolvers/mutations/accounts/impersonate.js.map +1 -0
  44. package/lib/resolvers/mutations/accounts/loginAsGuest.js +1 -1
  45. package/lib/resolvers/mutations/accounts/loginAsGuest.js.map +1 -1
  46. package/lib/resolvers/mutations/accounts/loginWithPassword.js +1 -1
  47. package/lib/resolvers/mutations/accounts/loginWithPassword.js.map +1 -1
  48. package/lib/resolvers/mutations/accounts/loginWithWebAuthn.js +1 -1
  49. package/lib/resolvers/mutations/accounts/loginWithWebAuthn.js.map +1 -1
  50. package/lib/resolvers/mutations/accounts/resetPassword.js +1 -1
  51. package/lib/resolvers/mutations/accounts/resetPassword.js.map +1 -1
  52. package/lib/resolvers/mutations/accounts/stopImpersonation.d.ts +6 -0
  53. package/lib/resolvers/mutations/accounts/stopImpersonation.d.ts.map +1 -0
  54. package/lib/resolvers/mutations/accounts/stopImpersonation.js +14 -0
  55. package/lib/resolvers/mutations/accounts/stopImpersonation.js.map +1 -0
  56. package/lib/resolvers/mutations/accounts/verifyEmail.js +1 -1
  57. package/lib/resolvers/mutations/accounts/verifyEmail.js.map +1 -1
  58. package/lib/resolvers/mutations/assortments/removeAssortmentProduct.js +1 -1
  59. package/lib/resolvers/mutations/assortments/removeAssortmentProduct.js.map +1 -1
  60. package/lib/resolvers/mutations/index.d.ts +2 -0
  61. package/lib/resolvers/mutations/index.d.ts.map +1 -1
  62. package/lib/resolvers/mutations/index.js +5 -1
  63. package/lib/resolvers/mutations/index.js.map +1 -1
  64. package/lib/resolvers/mutations/products/removeProductReviewVote.d.ts +1 -1
  65. package/lib/resolvers/mutations/products/removeProductReviewVote.d.ts.map +1 -1
  66. package/lib/resolvers/mutations/users/addPushSubscription.d.ts.map +1 -1
  67. package/lib/resolvers/mutations/users/addPushSubscription.js +2 -1
  68. package/lib/resolvers/mutations/users/addPushSubscription.js.map +1 -1
  69. package/lib/resolvers/mutations/users/heartbeat.d.ts +1 -1
  70. package/lib/resolvers/mutations/users/heartbeat.d.ts.map +1 -1
  71. package/lib/resolvers/mutations/users/heartbeat.js +2 -2
  72. package/lib/resolvers/mutations/users/heartbeat.js.map +1 -1
  73. package/lib/resolvers/mutations/users/removePushSubscription.d.ts.map +1 -1
  74. package/lib/resolvers/mutations/users/removePushSubscription.js +2 -1
  75. package/lib/resolvers/mutations/users/removePushSubscription.js.map +1 -1
  76. package/lib/resolvers/queries/index.d.ts +3 -0
  77. package/lib/resolvers/queries/index.d.ts.map +1 -1
  78. package/lib/resolvers/queries/index.js +4 -0
  79. package/lib/resolvers/queries/index.js.map +1 -1
  80. package/lib/resolvers/queries/shopInfo.d.ts +0 -1
  81. package/lib/resolvers/queries/shopInfo.d.ts.map +1 -1
  82. package/lib/resolvers/queries/shopInfo.js +10 -9
  83. package/lib/resolvers/queries/shopInfo.js.map +1 -1
  84. package/lib/resolvers/queries/users/impersonator.d.ts +3 -0
  85. package/lib/resolvers/queries/users/impersonator.d.ts.map +1 -0
  86. package/lib/resolvers/queries/users/impersonator.js +8 -0
  87. package/lib/resolvers/queries/users/impersonator.js.map +1 -0
  88. package/lib/resolvers/queries/warehousing/tokens.d.ts +2 -1
  89. package/lib/resolvers/queries/warehousing/tokens.d.ts.map +1 -1
  90. package/lib/resolvers/queries/warehousing/tokens.js +2 -2
  91. package/lib/resolvers/queries/warehousing/tokens.js.map +1 -1
  92. package/lib/resolvers/queries/warehousing/tokensCount.d.ts +5 -0
  93. package/lib/resolvers/queries/warehousing/tokensCount.d.ts.map +1 -0
  94. package/lib/resolvers/queries/warehousing/tokensCount.js +6 -0
  95. package/lib/resolvers/queries/warehousing/tokensCount.js.map +1 -0
  96. package/lib/resolvers/type/assortment/assortment-path-link-types.d.ts +7 -9
  97. package/lib/resolvers/type/assortment/assortment-path-link-types.d.ts.map +1 -1
  98. package/lib/resolvers/type/assortment/assortment-path-link-types.js +9 -7
  99. package/lib/resolvers/type/assortment/assortment-path-link-types.js.map +1 -1
  100. package/lib/resolvers/type/assortment/assortment-types.d.ts +1 -1
  101. package/lib/resolvers/type/assortment/assortment-types.d.ts.map +1 -1
  102. package/lib/resolvers/type/assortment/assortment-types.js +9 -2
  103. package/lib/resolvers/type/assortment/assortment-types.js.map +1 -1
  104. package/lib/resolvers/type/index.d.ts +14 -8
  105. package/lib/resolvers/type/index.d.ts.map +1 -1
  106. package/lib/resolvers/type/product/product-bundle-types.d.ts +1 -1
  107. package/lib/resolvers/type/product/product-bundle-types.d.ts.map +1 -1
  108. package/lib/resolvers/type/product/product-configurable-types.d.ts +1 -1
  109. package/lib/resolvers/type/product/product-configurable-types.d.ts.map +1 -1
  110. package/lib/resolvers/type/product/product-plan-types.d.ts +1 -1
  111. package/lib/resolvers/type/product/product-plan-types.d.ts.map +1 -1
  112. package/lib/resolvers/type/product/product-simple-types.d.ts +1 -1
  113. package/lib/resolvers/type/product/product-simple-types.d.ts.map +1 -1
  114. package/lib/resolvers/type/product/product-tokenized-types.d.ts +1 -1
  115. package/lib/resolvers/type/product/product-tokenized-types.d.ts.map +1 -1
  116. package/lib/resolvers/type/product/product-types.d.ts +1 -1
  117. package/lib/resolvers/type/product/product-types.d.ts.map +1 -1
  118. package/lib/resolvers/type/product/product-types.js +9 -2
  119. package/lib/resolvers/type/product/product-types.js.map +1 -1
  120. package/lib/resolvers/type/product/product-variation-assignment-vector.d.ts.map +1 -1
  121. package/lib/resolvers/type/product/product-variation-assignment-vector.js +4 -1
  122. package/lib/resolvers/type/product/product-variation-assignment-vector.js.map +1 -1
  123. package/lib/resolvers/type/product/product-variation-types.d.ts.map +1 -1
  124. package/lib/resolvers/type/product/product-variation-types.js +5 -2
  125. package/lib/resolvers/type/product/product-variation-types.js.map +1 -1
  126. package/lib/roles/all.d.ts.map +1 -1
  127. package/lib/roles/all.js +2 -0
  128. package/lib/roles/all.js.map +1 -1
  129. package/lib/roles/index.d.ts.map +1 -1
  130. package/lib/roles/index.js +2 -0
  131. package/lib/roles/index.js.map +1 -1
  132. package/lib/schema/mutation.d.ts.map +1 -1
  133. package/lib/schema/mutation.js +10 -0
  134. package/lib/schema/mutation.js.map +1 -1
  135. package/lib/schema/query.d.ts.map +1 -1
  136. package/lib/schema/query.js +11 -1
  137. package/lib/schema/query.js.map +1 -1
  138. package/lib/schema/types/shop.d.ts.map +1 -1
  139. package/lib/schema/types/shop.js +3 -2
  140. package/lib/schema/types/shop.js.map +1 -1
  141. package/lib/schema/types/user.js +1 -1
  142. package/package.json +20 -20
  143. package/src/api-index.ts +5 -4
  144. package/src/context.ts +32 -17
  145. package/src/errors.ts +5 -0
  146. package/src/express/createBulkImportMiddleware.ts +3 -3
  147. package/src/express/createERCMetadataMiddleware.ts +10 -8
  148. package/src/express/index.ts +29 -13
  149. package/src/fastify/ercMetadataHandler.ts +3 -1
  150. package/src/fastify/index.ts +67 -23
  151. package/src/loaders/index.ts +273 -228
  152. package/src/locale-context.ts +1 -10
  153. package/src/resolvers/mutations/accounts/createUser.ts +1 -1
  154. package/src/resolvers/mutations/accounts/impersonate.ts +23 -0
  155. package/src/resolvers/mutations/accounts/loginAsGuest.ts +1 -1
  156. package/src/resolvers/mutations/accounts/loginWithPassword.ts +1 -1
  157. package/src/resolvers/mutations/accounts/loginWithWebAuthn.ts +1 -1
  158. package/src/resolvers/mutations/accounts/resetPassword.ts +1 -1
  159. package/src/resolvers/mutations/accounts/stopImpersonation.ts +19 -0
  160. package/src/resolvers/mutations/accounts/verifyEmail.ts +1 -1
  161. package/src/resolvers/mutations/assortments/removeAssortmentProduct.ts +1 -1
  162. package/src/resolvers/mutations/index.ts +6 -1
  163. package/src/resolvers/mutations/users/addPushSubscription.ts +2 -1
  164. package/src/resolvers/mutations/users/heartbeat.ts +2 -2
  165. package/src/resolvers/mutations/users/removePushSubscription.ts +3 -1
  166. package/src/resolvers/queries/index.ts +4 -0
  167. package/src/resolvers/queries/shopInfo.ts +9 -9
  168. package/src/resolvers/queries/users/impersonator.ts +13 -0
  169. package/src/resolvers/queries/warehousing/tokens.ts +3 -1
  170. package/src/resolvers/queries/warehousing/tokensCount.ts +12 -0
  171. package/src/resolvers/type/assortment/assortment-path-link-types.ts +14 -19
  172. package/src/resolvers/type/assortment/assortment-types.ts +17 -5
  173. package/src/resolvers/type/product/product-types.ts +17 -5
  174. package/src/resolvers/type/product/product-variation-assignment-vector.ts +4 -1
  175. package/src/resolvers/type/product/product-variation-types.ts +5 -2
  176. package/src/roles/all.ts +2 -0
  177. package/src/roles/index.ts +2 -0
  178. package/src/schema/mutation.ts +10 -0
  179. package/src/schema/query.ts +11 -1
  180. package/src/schema/types/shop.ts +3 -2
  181. package/src/schema/types/user.ts +1 -1
@@ -0,0 +1,23 @@
1
+ import { log } from '@unchainedshop/logger';
2
+ import { ImpersonatingAdminUserError, UserNotFoundError } from '../../../errors.js';
3
+ import { Context } from '../../../context.js';
4
+
5
+ const impersonate = async (root, { userId }, context: Context) => {
6
+ log(`mutation impersonate ${userId}`, { userId: context.userId });
7
+
8
+ const userToImpersonate = await context.modules.users.findUserById(userId);
9
+
10
+ if (!userToImpersonate) {
11
+ throw new UserNotFoundError({ userId });
12
+ }
13
+
14
+ if ((userToImpersonate.roles || []).includes('admin')) {
15
+ throw new ImpersonatingAdminUserError({ impersonatedUserId: userId, userId: context.userId });
16
+ }
17
+
18
+ return context.login(userToImpersonate, {
19
+ impersonator: context.user,
20
+ });
21
+ };
22
+
23
+ export default impersonate;
@@ -22,7 +22,7 @@ export default async function loginAsGuest(root: never, _: any, context: Context
22
22
  user = await context.modules.users.updateHeartbeat(user._id, {
23
23
  remoteAddress: context.remoteAddress,
24
24
  remotePort: context.remotePort,
25
- userAgent: context.userAgent,
25
+ userAgent: context.getHeader('user-agent'),
26
26
  locale: context.localeContext.baseName,
27
27
  countryCode: context.countryContext,
28
28
  });
@@ -35,7 +35,7 @@ export default async function loginWithPassword(
35
35
  user = await context.modules.users.updateHeartbeat(user._id, {
36
36
  remoteAddress: context.remoteAddress,
37
37
  remotePort: context.remotePort,
38
- userAgent: context.userAgent,
38
+ userAgent: context.getHeader('user-agent'),
39
39
  locale: context.localeContext.baseName,
40
40
  countryCode: context.countryContext,
41
41
  });
@@ -28,7 +28,7 @@ export default async function loginWithWebAuthn(
28
28
  user = await context.modules.users.updateHeartbeat(user._id, {
29
29
  remoteAddress: context.remoteAddress,
30
30
  remotePort: context.remotePort,
31
- userAgent: context.userAgent,
31
+ userAgent: context.getHeader('user-agent'),
32
32
  locale: context.localeContext.baseName,
33
33
  countryCode: context.countryContext,
34
34
  });
@@ -22,7 +22,7 @@ export default async function resetPassword(
22
22
  user = await context.modules.users.updateHeartbeat(user._id, {
23
23
  remoteAddress: context.remoteAddress,
24
24
  remotePort: context.remotePort,
25
- userAgent: context.userAgent,
25
+ userAgent: context.getHeader('user-agent'),
26
26
  locale: context.localeContext.baseName,
27
27
  countryCode: context.countryContext,
28
28
  });
@@ -0,0 +1,19 @@
1
+ import { log } from '@unchainedshop/logger';
2
+ import { Context } from '../../../context.js';
3
+ import { UserNotFoundError } from '../../../errors.js';
4
+
5
+ export default async function stopImpersonation(root: never, _, context: Context) {
6
+ const { userId, modules, impersonatorId } = context;
7
+
8
+ log(`mutation stopImpersonation for ${userId}`);
9
+
10
+ if (!impersonatorId) return null;
11
+
12
+ const impersonator = await modules.users.findUserById(impersonatorId);
13
+
14
+ if (!impersonator) throw new UserNotFoundError({ userId: impersonatorId });
15
+
16
+ await context.logout();
17
+
18
+ return context.login(impersonator);
19
+ }
@@ -18,7 +18,7 @@ export default async function verifyEmail(root: never, { token }: { token: any }
18
18
  const user = await context.modules.users.updateHeartbeat(unverifiedToken.userId, {
19
19
  remoteAddress: context.remoteAddress,
20
20
  remotePort: context.remotePort,
21
- userAgent: context.userAgent,
21
+ userAgent: context.getHeader('user-agent'),
22
22
  locale: context.localeContext.baseName,
23
23
  countryCode: context.countryContext,
24
24
  });
@@ -13,7 +13,7 @@ export default async function removeAssortmentProduct(
13
13
 
14
14
  if (!assortmentProductId) throw new InvalidIdError({ assortmentProductId });
15
15
 
16
- const assortmentProduct = await modules.assortments.products.findProduct({
16
+ const assortmentProduct = await modules.assortments.products.findAssortmentProduct({
17
17
  assortmentProductId,
18
18
  });
19
19
  if (!assortmentProduct) throw new AssortmentProductNotFoundError({ assortmentProductId });
@@ -4,6 +4,8 @@ import loginWithPassword from './accounts/loginWithPassword.js';
4
4
  import loginWithWebAuthn from './accounts/loginWithWebAuthn.js';
5
5
  import loginAsGuest from './accounts/loginAsGuest.js';
6
6
  import logout from './accounts/logout.js';
7
+ import impersonate from './accounts/impersonate.js';
8
+ import stopImpersonation from './accounts/stopImpersonation.js';
7
9
  import changePassword from './accounts/changePassword.js';
8
10
  import createUser from './accounts/createUser.js';
9
11
  import verifyEmail from './accounts/verifyEmail.js';
@@ -155,6 +157,9 @@ import removeUserProductReviews from './users/removeUserProductReviews.js';
155
157
  export default {
156
158
  logout: acl(actions.logout)(logout),
157
159
  loginAsGuest: acl(actions.loginAsGuest)(loginAsGuest),
160
+ impersonate: acl(actions.impersonate)(impersonate),
161
+ stopImpersonation: acl(actions.stopImpersonation)(stopImpersonation),
162
+
158
163
  createWebAuthnCredentialCreationOptions: acl(actions.useWebAuthn)(
159
164
  createWebAuthnCredentialCreationOptions,
160
165
  ),
@@ -313,5 +318,5 @@ export default {
313
318
  signPaymentProviderForCheckout: acl(actions.registerPaymentCredentials)(
314
319
  signPaymentProviderForCheckout,
315
320
  ),
316
- removeUserProductReviews,
321
+ removeUserProductReviews, // TODO!
317
322
  };
@@ -6,7 +6,8 @@ const addPushSubscription = async (
6
6
  { subscription, unsubscribeFromOtherUsers = false },
7
7
  context: Context,
8
8
  ) => {
9
- const { modules, userId, userAgent } = context;
9
+ const { modules, userId } = context;
10
+ const userAgent = context.getHeader('user-agent');
10
11
  log(`mutation addPushSubscription ${userAgent}`, { userId });
11
12
 
12
13
  await modules.users.addPushSubscription(userId, subscription, {
@@ -5,7 +5,7 @@ import { UserNotFoundError } from '../../../errors.js';
5
5
  export default async function heartbeat(
6
6
  root: never,
7
7
  _: any,
8
- { countryContext, localeContext, modules, remoteAddress, remotePort, userAgent, userId }: Context,
8
+ { countryContext, localeContext, modules, remoteAddress, remotePort, userId, getHeader }: Context,
9
9
  ) {
10
10
  log(`mutation heartbeat ${remoteAddress}`, { userId });
11
11
 
@@ -20,7 +20,7 @@ export default async function heartbeat(
20
20
  locale: localeContext.baseName,
21
21
  remoteAddress,
22
22
  remotePort,
23
- userAgent,
23
+ userAgent: getHeader('user-agent'),
24
24
  });
25
25
 
26
26
  return user;
@@ -2,7 +2,9 @@ import { Context } from '../../../context.js';
2
2
  import { log } from '@unchainedshop/logger';
3
3
 
4
4
  const removePushSubscription = async (_, { p256dh }, context: Context) => {
5
- const { modules, userId, userAgent } = context;
5
+ const { modules, userId } = context;
6
+ const userAgent = context.getHeader('user-agent');
7
+
6
8
  log(`mutation removePushSubscription ${userId} ${userAgent}`, { userId });
7
9
 
8
10
  await modules.users.removePushSubscription(userId, p256dh);
@@ -63,6 +63,7 @@ import warehousingProviders from './warehousing/warehousingProviders.js';
63
63
  import warehousingProvidersCount from './warehousing/warehousingProvidersCount.js';
64
64
  import token from './warehousing/token.js';
65
65
  import tokens from './warehousing/tokens.js';
66
+ import tokensCount from './warehousing/tokensCount.js';
66
67
  import work from './worker/work.js';
67
68
  import workQueue from './worker/workQueue.js';
68
69
  import workStatistics from './worker/workStatistics.js';
@@ -71,9 +72,11 @@ import validateResetPasswordToken from './users/validateResetPasswordToken.js';
71
72
  import validateVerifyEmailToken from './users/validateVerifyEmailToken.js';
72
73
  import eventStatistics from './events/eventStatistics.js';
73
74
  import orderStatistics from './orders/orderStatistics.js';
75
+ import impersonator from './users/impersonator.js';
74
76
 
75
77
  export default {
76
78
  me,
79
+ impersonator,
77
80
  user: acl(actions.viewUser)(user),
78
81
  users: acl(actions.viewUsers)(users),
79
82
  usersCount: acl(actions.viewUsers)(usersCount),
@@ -104,6 +107,7 @@ export default {
104
107
  warehousingInterfaces: acl(actions.viewWarehousingInterfaces)(warehousingInterfaces),
105
108
  token: acl(actions.viewToken)(token),
106
109
  tokens: acl(actions.viewTokens)(tokens),
110
+ tokensCount: acl(actions.viewTokens)(tokensCount),
107
111
  translatedProductTexts: acl(actions.viewTranslations)(translatedProductTexts),
108
112
  translatedProductMediaTexts: acl(actions.viewTranslations)(translatedProductMediaTexts),
109
113
  translatedProductVariationTexts: acl(actions.viewTranslations)(translatedProductVariationTexts),
@@ -7,7 +7,6 @@ export default function shopInfo(
7
7
  context: Context,
8
8
  ): {
9
9
  version?: string;
10
- externalLinks: () => Array<string>;
11
10
  adminUiConfig?: Record<string, any>;
12
11
  vapidPublicKey?: string;
13
12
  } {
@@ -16,16 +15,17 @@ export default function shopInfo(
16
15
 
17
16
  return {
18
17
  version: context.version,
19
- externalLinks: () => {
20
- try {
21
- const parsed = JSON.parse(process.env.EXTERNAL_LINKS);
22
- return parsed;
23
- } catch {
24
- return [];
25
- }
26
- },
27
18
  adminUiConfig: {
28
19
  customProperties: adminUiConfig?.customProperties ?? [],
20
+ singleSignOnURL: adminUiConfig?.singleSignOnURL,
21
+ externalLinks: () => {
22
+ try {
23
+ const parsed = JSON.parse(process.env.EXTERNAL_LINKS);
24
+ return parsed;
25
+ } catch {
26
+ return [];
27
+ }
28
+ },
29
29
  },
30
30
  vapidPublicKey: process.env?.PUSH_NOTIFICATION_PUBLIC_KEY,
31
31
  };
@@ -0,0 +1,13 @@
1
+ import { log } from '@unchainedshop/logger';
2
+ import { Context } from '../../../context.js';
3
+
4
+ export default async function impersonator(
5
+ root: never,
6
+ params: any,
7
+ { userId, remoteAddress, modules, impersonatorId }: Context,
8
+ ) {
9
+ log(`query impersonator ${remoteAddress}`, { userId });
10
+
11
+ if (!impersonatorId) return null;
12
+ return modules.users.findUserById(impersonatorId);
13
+ }
@@ -4,15 +4,17 @@ import { Context } from '../../../context.js';
4
4
  export default async function tokens(
5
5
  root: never,
6
6
  {
7
+ queryString = null,
7
8
  limit = 10,
8
9
  offset = 0,
9
10
  }: {
10
11
  limit: number;
11
12
  offset: number;
13
+ queryString?: string;
12
14
  },
13
15
  { modules, userId }: Context,
14
16
  ) {
15
17
  log(`query tokens`, { userId });
16
18
 
17
- return modules.warehousing.findTokens({}, { limit, skip: offset });
19
+ return modules.warehousing.findTokens({ queryString }, { limit, skip: offset });
18
20
  }
@@ -0,0 +1,12 @@
1
+ import { log } from '@unchainedshop/logger';
2
+ import { Context } from '../../../context.js';
3
+
4
+ export default async function tokensCount(
5
+ root: never,
6
+ { queryString = null },
7
+ { modules, userId }: Context,
8
+ ) {
9
+ log(`query tokensCount`, { userId });
10
+
11
+ return modules.warehousing.tokensCount({ queryString });
12
+ }
@@ -1,28 +1,23 @@
1
+ import { AssortmentLink } from '@unchainedshop/core-assortments';
1
2
  import { Context } from '../../../context.js';
2
- import {
3
- AssortmentPathLink as AssortmentPathLinkType,
4
- AssortmentLink as AssortmentLinkType,
5
- AssortmentText,
6
- } from '@unchainedshop/core-assortments';
7
3
 
8
- type HelperType<P, T> = (assortmentPathLink: AssortmentPathLinkType, params: P, context: Context) => T;
9
-
10
- export interface AssortmentPathLinkHelperTypes {
11
- link: HelperType<never, Promise<AssortmentLinkType>>;
12
- assortmentTexts: HelperType<{ forceLocale?: string }, Promise<AssortmentText>>;
13
- }
4
+ export const AssortmentPathLink = {
5
+ link(linkObj) {
6
+ if (linkObj._id) return linkObj;
7
+ return null;
8
+ },
14
9
 
15
- export const AssortmentPathLink: AssortmentPathLinkHelperTypes = {
16
- link: async ({ assortmentId, childAssortmentId }, _, { loaders }) => {
17
- return loaders.assortmentLinkLoader.load({
18
- parentAssortmentId: assortmentId,
19
- childAssortmentId,
20
- });
10
+ assortmentId(linkObj) {
11
+ return linkObj.childAssortmentId;
21
12
  },
22
13
 
23
- assortmentTexts: async ({ assortmentId }, params, { loaders, localeContext }) => {
14
+ assortmentTexts: async (
15
+ { childAssortmentId }: AssortmentLink,
16
+ params: { forceLocale?: string },
17
+ { loaders, localeContext }: Context,
18
+ ) => {
24
19
  const text = await loaders.assortmentTextLoader.load({
25
- assortmentId,
20
+ assortmentId: childAssortmentId,
26
21
  locale: params.forceLocale || localeContext.baseName,
27
22
  });
28
23
  return text;
@@ -3,10 +3,22 @@ import { Assortment } from '@unchainedshop/core-assortments';
3
3
  import { SearchFilterQuery } from '@unchainedshop/core-filters';
4
4
 
5
5
  export const AssortmentTypes = {
6
- assortmentPaths: (obj: Assortment, _, { modules }: Context) => {
7
- return modules.assortments.breadcrumbs({
8
- assortmentId: obj._id,
9
- });
6
+ assortmentPaths(obj: Assortment, _, { modules, loaders }: Context) {
7
+ return modules.assortments.breadcrumbs(
8
+ {
9
+ assortmentId: obj._id,
10
+ },
11
+ {
12
+ resolveAssortmentProducts: async (productId) =>
13
+ loaders.assortmentProductsLoader.load({
14
+ productId,
15
+ }),
16
+ resolveAssortmentLinks: async (childAssortmentId) =>
17
+ loaders.assortmentLinksLoader.load({
18
+ childAssortmentId,
19
+ }),
20
+ },
21
+ );
10
22
  },
11
23
 
12
24
  children: async (
@@ -79,7 +91,7 @@ export const AssortmentTypes = {
79
91
 
80
92
  async productAssignments(obj: Assortment, _, { modules }: Context) {
81
93
  // TODO: Loader & move default sort to core module
82
- return modules.assortments.products.findProducts(
94
+ return modules.assortments.products.findAssortmentProducts(
83
95
  {
84
96
  assortmentId: obj._id,
85
97
  },
@@ -18,15 +18,27 @@ export const Product = {
18
18
  params: {
19
19
  forceLocale?: string;
20
20
  },
21
- { modules }: Context,
21
+ { modules, loaders }: Context,
22
22
  ): Promise<
23
23
  Array<{
24
24
  links: Array<AssortmentPathLink>;
25
25
  }>
26
26
  > {
27
- return modules.assortments.breadcrumbs({
28
- productId: product._id,
29
- });
27
+ return modules.assortments.breadcrumbs(
28
+ {
29
+ productId: product._id,
30
+ },
31
+ {
32
+ resolveAssortmentProducts: async (productId) =>
33
+ loaders.assortmentProductsLoader.load({
34
+ productId,
35
+ }),
36
+ resolveAssortmentLinks: async (childAssortmentId) =>
37
+ loaders.assortmentLinksLoader.load({
38
+ childAssortmentId,
39
+ }),
40
+ },
41
+ );
30
42
  },
31
43
 
32
44
  // TODO: Use a loader!
@@ -103,7 +115,7 @@ export const Product = {
103
115
 
104
116
  if (!assortmentIds.length) return [];
105
117
 
106
- const productIds = await modules.assortments.products.findProductSiblings({
118
+ const productIds = await modules.assortments.products.findSiblings({
107
119
  productId,
108
120
  assortmentIds,
109
121
  });
@@ -22,7 +22,10 @@ export const ProductVariationAssignmentVector: ProductVariationAssignmentVectorH
22
22
  key: obj.key,
23
23
  productId: obj.product._id,
24
24
  });
25
- return modules.products.variations.option(productVariation, obj.value);
25
+ return {
26
+ _id: productVariation._id,
27
+ productVariationOption: obj.value,
28
+ };
26
29
  },
27
30
 
28
31
  variation: (obj, _, { modules }) => {
@@ -21,8 +21,11 @@ export interface ProductVariationHelperTypes {
21
21
  texts: HelperType<{ forceLocale?: string }, Promise<ProductVariationText>>;
22
22
  }
23
23
  export const ProductVariation: ProductVariationHelperTypes = {
24
- options: (obj, _, { modules }) => {
25
- return (obj.options || []).map((option) => modules.products.variations.option(obj, option));
24
+ options: (obj) => {
25
+ return (obj.options || []).map((option) => ({
26
+ _id: obj._id,
27
+ productVariationOption: option,
28
+ }));
26
29
  },
27
30
 
28
31
  texts: async (obj, { forceLocale }, { modules, localeContext }) => {
package/src/roles/all.ts CHANGED
@@ -100,6 +100,7 @@ export const all = (role, actions) => {
100
100
  role.allow(actions.viewTokens, () => false);
101
101
  role.allow(actions.viewStatistics, () => false);
102
102
  role.allow(actions.uploadUserAvatar, () => false);
103
+ role.allow(actions.impersonate, () => false);
103
104
 
104
105
  // special case: when doing a login mutation, the user is not logged in technically yet,
105
106
  // but should be able to see user data of the user that is about to be logged in
@@ -139,6 +140,7 @@ export const all = (role, actions) => {
139
140
  role.allow(actions.viewFilter, () => true);
140
141
  role.allow(actions.viewTranslations, () => true);
141
142
  role.allow(actions.logout, () => true);
143
+ role.allow(actions.stopImpersonation, () => true);
142
144
  role.allow(actions.loginAsGuest, () => true);
143
145
  role.allow(actions.loginWithPassword, () => true);
144
146
  role.allow(actions.loginWithWebAuthn, () => true);
@@ -12,6 +12,8 @@ const roles = {
12
12
  const allRoles = roles;
13
13
 
14
14
  const actions: Record<string, string> = [
15
+ 'impersonate',
16
+ 'stopImpersonation',
15
17
  'viewEvent',
16
18
  'viewEvents',
17
19
  'viewUserRoles',
@@ -57,6 +57,16 @@ export default [
57
57
  """
58
58
  logout: SuccessResponse
59
59
 
60
+ """
61
+ Impersonate a user
62
+ """
63
+ impersonate(userId: ID!): LoginMethodResponse!
64
+
65
+ """
66
+ End customer impersonated user session and resume the impersonator session
67
+ """
68
+ stopImpersonation: LoginMethodResponse
69
+
60
70
  """
61
71
  Marks the user's email address as verified. Logs the user in afterwards.
62
72
  """
@@ -6,6 +6,11 @@ export default [
6
6
  """
7
7
  me: User
8
8
 
9
+ """
10
+ User impersonating currently logged in user
11
+ """
12
+ impersonator: User
13
+
9
14
  """
10
15
  Get list of users, by default sorted by creation date (ascending) unless a queryString is set
11
16
  """
@@ -211,7 +216,12 @@ export default [
211
216
  """
212
217
  Get all tokens
213
218
  """
214
- tokens(limit: Int = 10, offset: Int = 0): [Token!]!
219
+ tokens(queryString: String, limit: Int = 10, offset: Int = 0): [Token!]!
220
+
221
+ """
222
+ Returns total tokens
223
+ """
224
+ tokensCount(queryString: String): Int!
215
225
 
216
226
  """
217
227
  Returns total number of payment providers, optionally filtered by type
@@ -10,7 +10,7 @@ export default [
10
10
  """
11
11
  SELF
12
12
  }
13
- type Link @cacheControl(maxAge: 180) {
13
+ type AdminUiLink @cacheControl(maxAge: 180) {
14
14
  href: String
15
15
  title: String
16
16
  target: ExternalLinkTarget
@@ -23,6 +23,8 @@ export default [
23
23
 
24
24
  type AdminUiConfig {
25
25
  customProperties: [AdminUiConfigCustomEntityInterface!]!
26
+ externalLinks: [AdminUiLink!]!
27
+ singleSignOnURL: String
26
28
  }
27
29
 
28
30
  type Shop @cacheControl(maxAge: 180) {
@@ -31,7 +33,6 @@ export default [
31
33
  country: Country
32
34
  version: String
33
35
  userRoles: [String!]!
34
- externalLinks: [Link]!
35
36
  adminUiConfig: AdminUiConfig!
36
37
  vapidPublicKey: String
37
38
  }
@@ -45,7 +45,7 @@ export default [
45
45
  type UserLoginTracker {
46
46
  timestamp: Timestamp!
47
47
  remoteAddress: String
48
- remotePort: String
48
+ remotePort: Int
49
49
  userAgent: String
50
50
  locale: Locale
51
51
  countryCode: String