@scayle/storefront-core 7.28.1 → 7.28.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @scayle/storefront-core
2
2
 
3
+ ## 7.28.2
4
+
5
+ ### Patch Changes
6
+
7
+ - `fetchUser` and `refreshUser` are now proper RPC methods
8
+ - Fix parsing error from authentication service
9
+ - Use a common API client for interacting with Checkout Customer API
10
+
3
11
  ## 7.28.1
4
12
 
5
13
  ### Patch Changes
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.CustomerAPIClient = void 0;
7
+ var _fetch = require("../utils/fetch.cjs");
8
+ var _httpStatus = require("../constants/httpStatus.cjs");
9
+ class CustomerAPIClient {
10
+ headers;
11
+ baseURL;
12
+ constructor(options) {
13
+ const {
14
+ accessToken,
15
+ accessHeader,
16
+ baseUrl
17
+ } = options;
18
+ this.baseURL = baseUrl;
19
+ this.headers = {
20
+ Authorization: `Bearer ${accessToken}`,
21
+ Accept: "application/json",
22
+ "Content-Type": "application/json",
23
+ ...(accessHeader ? {
24
+ "X-Access-Header": accessHeader
25
+ } : {})
26
+ };
27
+ }
28
+ async handleResponse(response) {
29
+ if (!response.ok) {
30
+ throw new _fetch.FetchError(response);
31
+ }
32
+ return await response.json();
33
+ }
34
+ /**
35
+ * Get the addresses for the current customer
36
+ */
37
+ async getAddresses(shopId) {
38
+ return await fetch(`${this.baseURL}/api/oauth/customer/addresses`, {
39
+ headers: {
40
+ ...this.headers,
41
+ "X-Shop-Id": shopId.toString()
42
+ }
43
+ }).then(response => this.handleResponse(response));
44
+ }
45
+ /**
46
+ * Returns customer data and latest orders.
47
+ * When not logged in will return undefined.
48
+ * @see https://scayle.dev/api/oauth/latest/fetch-authorized-payload
49
+ */
50
+ async getMe(shopId) {
51
+ const response = await fetch(`${this.baseURL}/api/oauth/me`, {
52
+ headers: {
53
+ ...this.headers,
54
+ "X-Shop-Id": shopId.toString()
55
+ }
56
+ });
57
+ if (response.status === _httpStatus.HttpStatusCode.FORBIDDEN) {
58
+ return void 0;
59
+ }
60
+ return this.handleResponse(response);
61
+ }
62
+ /**
63
+ * Fetch a customer's order
64
+ * @see https://scayle.dev/api/oauth/latest/fetch-order-by-id
65
+ */
66
+ async getOrder(shopId, orderId) {
67
+ return await fetch(`${this.baseURL}/api/oauth/customer/order/${orderId}`, {
68
+ headers: {
69
+ ...this.headers,
70
+ "X-Shop-Id": shopId.toString()
71
+ }
72
+ }).then(response => this.handleResponse(response));
73
+ }
74
+ /**
75
+ * Update the customer's contact details
76
+ *
77
+ * @see https://scayle.dev/api/oauth/latest/put-customer-contact
78
+ */
79
+ async updateContactInfo(shopId, {
80
+ email,
81
+ phone
82
+ }) {
83
+ return await fetch(`${this.baseURL}/api/oauth/customer/contact`, {
84
+ method: "PUT",
85
+ headers: {
86
+ ...this.headers,
87
+ "X-Shop-Id": shopId.toString()
88
+ },
89
+ body: JSON.stringify({
90
+ email,
91
+ phone
92
+ })
93
+ }).then(response => this.handleResponse(response));
94
+ }
95
+ /**
96
+ * Update the customer's personal details
97
+ */
98
+ async updatePersonalInfo(shopId, payload) {
99
+ return await fetch(`${this.baseURL}/api/oauth/customer/personal`, {
100
+ method: "PUT",
101
+ headers: {
102
+ ...this.headers,
103
+ "X-Shop-Id": shopId.toString()
104
+ },
105
+ body: JSON.stringify(payload)
106
+ }).then(response => this.handleResponse(response));
107
+ }
108
+ /**
109
+ * Update the customer's password
110
+ *
111
+ * @see https://scayle.dev/api/oauth/latest/put-customer-password
112
+ */
113
+ async updatePassword(shopId, {
114
+ password,
115
+ newPassword
116
+ }) {
117
+ return await fetch(`${this.baseURL}/api/oauth/customer/password`, {
118
+ method: "PUT",
119
+ headers: {
120
+ ...this.headers,
121
+ "X-Shop-Id": shopId.toString()
122
+ },
123
+ body: JSON.stringify({
124
+ password,
125
+ newPassword
126
+ })
127
+ }).then(response => this.handleResponse(response));
128
+ }
129
+ }
130
+ exports.CustomerAPIClient = CustomerAPIClient;
@@ -0,0 +1,89 @@
1
+ import type { ShopUserAddress, Order, ShopUser, Gender } from '../types';
2
+ interface CustomerAPIOptions {
3
+ /**
4
+ * The OAuth Access Token
5
+ */
6
+ accessToken: string;
7
+ /**
8
+ * The OAuth Refresh Token
9
+ */
10
+ refreshToken: string;
11
+ accessHeader: string;
12
+ /**
13
+ * The Checkout Host
14
+ */
15
+ baseUrl: string;
16
+ }
17
+ interface PaginatedResponse<EntityType> {
18
+ entities: EntityType[];
19
+ pagination: {
20
+ current: number;
21
+ first: number;
22
+ last: number;
23
+ next: number;
24
+ page: number;
25
+ perPage: number;
26
+ prev: number;
27
+ total: number;
28
+ };
29
+ }
30
+ interface ContactData {
31
+ email: string;
32
+ phone: string;
33
+ }
34
+ interface PersonalData {
35
+ firstName: string;
36
+ lastName: string;
37
+ birthDate: string;
38
+ gender?: Gender;
39
+ title?: string;
40
+ }
41
+ interface PasswordUpdate {
42
+ password: string;
43
+ newPassword: string;
44
+ }
45
+ /**
46
+ * An API client for interacting with the Checkout Customer API
47
+ *
48
+ * Should be initialized with the token set acquired from the Auth API
49
+ *
50
+ * @see https://scayle.dev/api/oauth/latest
51
+ */
52
+ export declare class CustomerAPIClient {
53
+ headers: HeadersInit;
54
+ baseURL: string;
55
+ constructor(options: CustomerAPIOptions);
56
+ handleResponse<BodyType>(response: Response): Promise<BodyType>;
57
+ /**
58
+ * Get the addresses for the current customer
59
+ */
60
+ getAddresses(shopId: number): Promise<PaginatedResponse<ShopUserAddress>>;
61
+ /**
62
+ * Returns customer data and latest orders.
63
+ * When not logged in will return undefined.
64
+ * @see https://scayle.dev/api/oauth/latest/fetch-authorized-payload
65
+ */
66
+ getMe(shopId: number): Promise<ShopUser | undefined>;
67
+ /**
68
+ * Fetch a customer's order
69
+ * @see https://scayle.dev/api/oauth/latest/fetch-order-by-id
70
+ */
71
+ getOrder(shopId: number, orderId: number): Promise<Order>;
72
+ /**
73
+ * Update the customer's contact details
74
+ *
75
+ * @see https://scayle.dev/api/oauth/latest/put-customer-contact
76
+ */
77
+ updateContactInfo(shopId: number, { email, phone }: ContactData): Promise<ShopUser>;
78
+ /**
79
+ * Update the customer's personal details
80
+ */
81
+ updatePersonalInfo(shopId: number, payload: PersonalData): Promise<ShopUser>;
82
+ /**
83
+ * Update the customer's password
84
+ *
85
+ * @see https://scayle.dev/api/oauth/latest/put-customer-password
86
+ */
87
+ updatePassword(shopId: number, { password, newPassword }: PasswordUpdate): Promise<ShopUser>;
88
+ }
89
+ export {};
@@ -0,0 +1,110 @@
1
+ import { FetchError } from "../utils/fetch.mjs";
2
+ import { HttpStatusCode } from "../constants/httpStatus.mjs";
3
+ export class CustomerAPIClient {
4
+ headers;
5
+ baseURL;
6
+ constructor(options) {
7
+ const { accessToken, accessHeader, baseUrl } = options;
8
+ this.baseURL = baseUrl;
9
+ this.headers = {
10
+ Authorization: `Bearer ${accessToken}`,
11
+ Accept: "application/json",
12
+ "Content-Type": "application/json",
13
+ ...accessHeader ? { "X-Access-Header": accessHeader } : {}
14
+ };
15
+ }
16
+ async handleResponse(response) {
17
+ if (!response.ok) {
18
+ throw new FetchError(response);
19
+ }
20
+ return await response.json();
21
+ }
22
+ /**
23
+ * Get the addresses for the current customer
24
+ */
25
+ async getAddresses(shopId) {
26
+ return await fetch(`${this.baseURL}/api/oauth/customer/addresses`, {
27
+ headers: {
28
+ ...this.headers,
29
+ "X-Shop-Id": shopId.toString()
30
+ }
31
+ }).then(
32
+ (response) => this.handleResponse(response)
33
+ );
34
+ }
35
+ /**
36
+ * Returns customer data and latest orders.
37
+ * When not logged in will return undefined.
38
+ * @see https://scayle.dev/api/oauth/latest/fetch-authorized-payload
39
+ */
40
+ async getMe(shopId) {
41
+ const response = await fetch(`${this.baseURL}/api/oauth/me`, {
42
+ headers: {
43
+ ...this.headers,
44
+ "X-Shop-Id": shopId.toString()
45
+ }
46
+ });
47
+ if (response.status === HttpStatusCode.FORBIDDEN) {
48
+ return void 0;
49
+ }
50
+ return this.handleResponse(response);
51
+ }
52
+ /**
53
+ * Fetch a customer's order
54
+ * @see https://scayle.dev/api/oauth/latest/fetch-order-by-id
55
+ */
56
+ async getOrder(shopId, orderId) {
57
+ return await fetch(`${this.baseURL}/api/oauth/customer/order/${orderId}`, {
58
+ headers: {
59
+ ...this.headers,
60
+ "X-Shop-Id": shopId.toString()
61
+ }
62
+ }).then((response) => this.handleResponse(response));
63
+ }
64
+ /**
65
+ * Update the customer's contact details
66
+ *
67
+ * @see https://scayle.dev/api/oauth/latest/put-customer-contact
68
+ */
69
+ async updateContactInfo(shopId, { email, phone }) {
70
+ return await fetch(`${this.baseURL}/api/oauth/customer/contact`, {
71
+ method: "PUT",
72
+ headers: {
73
+ ...this.headers,
74
+ "X-Shop-Id": shopId.toString()
75
+ },
76
+ body: JSON.stringify({
77
+ email,
78
+ phone
79
+ })
80
+ }).then((response) => this.handleResponse(response));
81
+ }
82
+ /**
83
+ * Update the customer's personal details
84
+ */
85
+ async updatePersonalInfo(shopId, payload) {
86
+ return await fetch(`${this.baseURL}/api/oauth/customer/personal`, {
87
+ method: "PUT",
88
+ headers: {
89
+ ...this.headers,
90
+ "X-Shop-Id": shopId.toString()
91
+ },
92
+ body: JSON.stringify(payload)
93
+ }).then((response) => this.handleResponse(response));
94
+ }
95
+ /**
96
+ * Update the customer's password
97
+ *
98
+ * @see https://scayle.dev/api/oauth/latest/put-customer-password
99
+ */
100
+ async updatePassword(shopId, { password, newPassword }) {
101
+ return await fetch(`${this.baseURL}/api/oauth/customer/password`, {
102
+ method: "PUT",
103
+ headers: {
104
+ ...this.headers,
105
+ "X-Shop-Id": shopId.toString()
106
+ },
107
+ body: JSON.stringify({ password, newPassword })
108
+ }).then((response) => this.handleResponse(response));
109
+ }
110
+ }
@@ -20,6 +20,11 @@ async function oauthResponseHandler(response) {
20
20
  }
21
21
  return data;
22
22
  }
23
+ function emptyOAuthResponseHandler(response) {
24
+ if (!response.ok) {
25
+ throw new _fetch.FetchError(response);
26
+ }
27
+ }
23
28
  class OAuthClient {
24
29
  headers;
25
30
  baseURL;
@@ -82,7 +87,7 @@ class OAuthClient {
82
87
  method: "POST",
83
88
  headers: this.headers,
84
89
  body: JSON.stringify(payload)
85
- }).then(oauthResponseHandler);
90
+ }).then(emptyOAuthResponseHandler);
86
91
  }
87
92
  /**
88
93
  * Update password by hash
@@ -113,10 +118,10 @@ class OAuthClient {
113
118
  async validateToken(accessToken) {
114
119
  await fetch(`${this.baseURL}/oauth/token/validate`, {
115
120
  headers: {
116
- ...headers,
121
+ ...this.headers,
117
122
  Authorization: `Bearer ${accessToken}`
118
123
  }
119
- }).then(oauthResponseHandler);
124
+ }).then(emptyOAuthResponseHandler);
120
125
  }
121
126
  /**
122
127
  * Revoke an access token
@@ -127,10 +132,10 @@ class OAuthClient {
127
132
  await fetch(`${this.baseURL}/oauth/tokens/${decodedAccessToken.jti}`, {
128
133
  method: "DELETE",
129
134
  headers: {
130
- ...headers,
135
+ ...this.headers,
131
136
  Authorization: `Bearer ${accessToken}`
132
137
  }
133
- }).then(oauthResponseHandler);
138
+ }).then(emptyOAuthResponseHandler);
134
139
  }
135
140
  }
136
141
  exports.OAuthClient = OAuthClient;
@@ -14,6 +14,11 @@ async function oauthResponseHandler(response) {
14
14
  }
15
15
  return data;
16
16
  }
17
+ function emptyOAuthResponseHandler(response) {
18
+ if (!response.ok) {
19
+ throw new FetchError(response);
20
+ }
21
+ }
17
22
  export class OAuthClient {
18
23
  headers;
19
24
  baseURL;
@@ -72,7 +77,7 @@ export class OAuthClient {
72
77
  method: "POST",
73
78
  headers: this.headers,
74
79
  body: JSON.stringify(payload)
75
- }).then(oauthResponseHandler);
80
+ }).then(emptyOAuthResponseHandler);
76
81
  }
77
82
  /**
78
83
  * Update password by hash
@@ -103,10 +108,10 @@ export class OAuthClient {
103
108
  async validateToken(accessToken) {
104
109
  await fetch(`${this.baseURL}/oauth/token/validate`, {
105
110
  headers: {
106
- ...headers,
111
+ ...this.headers,
107
112
  Authorization: `Bearer ${accessToken}`
108
113
  }
109
- }).then(oauthResponseHandler);
114
+ }).then(emptyOAuthResponseHandler);
110
115
  }
111
116
  /**
112
117
  * Revoke an access token
@@ -117,9 +122,9 @@ export class OAuthClient {
117
122
  await fetch(`${this.baseURL}/oauth/tokens/${decodedAccessToken.jti}`, {
118
123
  method: "DELETE",
119
124
  headers: {
120
- ...headers,
125
+ ...this.headers,
121
126
  Authorization: `Bearer ${accessToken}`
122
127
  }
123
- }).then(oauthResponseHandler);
128
+ }).then(emptyOAuthResponseHandler);
124
129
  }
125
130
  }
@@ -33,9 +33,7 @@ export class UnstorageCache {
33
33
  }
34
34
  async set(key, value, ttl, tags = []) {
35
35
  const _value = await this.serialize(value);
36
- await this.storage.setItem(this.getKey(key), _value, {
37
- ttl
38
- });
36
+ await this.storage.setItem(this.getKey(key), _value, { ttl });
39
37
  await Promise.all(
40
38
  tags.map((tag) => this.addKeyToTag(tag, this.getKey(key)))
41
39
  );
@@ -4,13 +4,13 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getOrderById = void 0;
7
+ var _fetch = require("../../../utils/fetch.cjs");
8
+ var _customer = require("../../../api/customer.cjs");
9
+ var _httpStatus = require("../../../constants/httpStatus.cjs");
7
10
  const getOrderById = exports.getOrderById = async function getOrderById2({
8
11
  orderId
9
12
  }, context) {
10
13
  const accessToken = context.accessToken;
11
- const shopId = context.shopId;
12
- const accessHeader = context.checkout.accessHeader;
13
- const checkoutUrl = context.checkout.url;
14
14
  if (!orderId || isNaN(orderId)) {
15
15
  throw new Error("No order-id provided");
16
16
  }
@@ -21,29 +21,21 @@ const getOrderById = exports.getOrderById = async function getOrderById2({
21
21
  if (!isOrderInUserOrderlist) {
22
22
  throw new Error("Order not belonging to current user");
23
23
  }
24
- if (!accessToken) {
25
- throw new Error("No access token");
26
- }
27
- const orderUrl = `${checkoutUrl}/api/customer/order/${orderId}`;
24
+ const client = new _customer.CustomerAPIClient({
25
+ accessToken: context.accessToken,
26
+ refreshToken: context.refreshToken,
27
+ accessHeader: context.checkout.accessHeader,
28
+ baseUrl: context.checkout.url
29
+ });
28
30
  try {
29
- const response = await fetch(orderUrl, {
30
- headers: {
31
- Authorization: `Bearer ${accessToken}`,
32
- "X-Shop-Id": shopId.toString(),
33
- Accept: "application/json",
34
- ...(accessHeader ? {
35
- "X-Access-Header": accessHeader
36
- } : {})
31
+ return await client.getOrder(context.shopId, orderId);
32
+ } catch (error) {
33
+ if (error instanceof _fetch.FetchError) {
34
+ if (error.response.status === _httpStatus.HttpStatusCode.NOT_FOUND) {
35
+ throw new Error("Order not found");
37
36
  }
38
- });
39
- if (response.status === 404) {
40
- throw new Error("Order not found");
41
- }
42
- if (response.status === 200) {
43
- return await response.json();
37
+ throw new Error(`Unknown response status: ${error.response.status}`);
44
38
  }
45
- throw new Error(`Unknown response status: ${response.status}`);
46
- } catch (error) {
47
39
  context.log.error("Error while fetching order details", {
48
40
  error
49
41
  });
@@ -1,8 +1,8 @@
1
+ import { FetchError } from "../../../utils/fetch.mjs";
2
+ import { CustomerAPIClient } from "../../../api/customer.mjs";
3
+ import { HttpStatusCode } from "../../../constants/httpStatus.mjs";
1
4
  export const getOrderById = async function getOrderById2({ orderId }, context) {
2
5
  const accessToken = context.accessToken;
3
- const shopId = context.shopId;
4
- const accessHeader = context.checkout.accessHeader;
5
- const checkoutUrl = context.checkout.url;
6
6
  if (!orderId || isNaN(orderId)) {
7
7
  throw new Error("No order-id provided");
8
8
  }
@@ -15,27 +15,21 @@ export const getOrderById = async function getOrderById2({ orderId }, context) {
15
15
  if (!isOrderInUserOrderlist) {
16
16
  throw new Error("Order not belonging to current user");
17
17
  }
18
- if (!accessToken) {
19
- throw new Error("No access token");
20
- }
21
- const orderUrl = `${checkoutUrl}/api/customer/order/${orderId}`;
18
+ const client = new CustomerAPIClient({
19
+ accessToken: context.accessToken,
20
+ refreshToken: context.refreshToken,
21
+ accessHeader: context.checkout.accessHeader,
22
+ baseUrl: context.checkout.url
23
+ });
22
24
  try {
23
- const response = await fetch(orderUrl, {
24
- headers: {
25
- Authorization: `Bearer ${accessToken}`,
26
- "X-Shop-Id": shopId.toString(),
27
- Accept: "application/json",
28
- ...accessHeader ? { "X-Access-Header": accessHeader } : {}
25
+ return await client.getOrder(context.shopId, orderId);
26
+ } catch (error) {
27
+ if (error instanceof FetchError) {
28
+ if (error.response.status === HttpStatusCode.NOT_FOUND) {
29
+ throw new Error("Order not found");
29
30
  }
30
- });
31
- if (response.status === 404) {
32
- throw new Error("Order not found");
33
- }
34
- if (response.status === 200) {
35
- return await response.json();
31
+ throw new Error(`Unknown response status: ${error.response.status}`);
36
32
  }
37
- throw new Error(`Unknown response status: ${response.status}`);
38
- } catch (error) {
39
33
  context.log.error("Error while fetching order details", { error });
40
34
  throw error;
41
35
  }
@@ -4,10 +4,11 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.updateShopUser = exports.updatePassword = void 0;
7
+ var _customer = require("../../../api/customer.cjs");
8
+ var _fetch = require("../../../utils/fetch.cjs");
9
+ var _httpStatus = require("../../../constants/httpStatus.cjs");
7
10
  const updateShopUser = exports.updateShopUser = async function updateShopUser2(payload, context) {
8
11
  const shopId = context.shopId;
9
- const accessHeader = context.checkout.accessHeader;
10
- const checkoutUrl = context.checkout.url;
11
12
  const updatedUser = {
12
13
  email: payload.email,
13
14
  phone: payload.phone,
@@ -26,101 +27,69 @@ const updateShopUser = exports.updateShopUser = async function updateShopUser2(p
26
27
  ...context.user,
27
28
  ...updatedUser
28
29
  };
29
- const contactUrl = `${checkoutUrl}/api/customer/contact`;
30
- const personalUrl = `${checkoutUrl}/api/customer/personal`;
31
- const headers = {
32
- Authorization: `Bearer ${context.accessToken}`,
33
- "X-Shop-Id": shopId.toString(),
34
- "Content-Type": "application/json",
35
- ...(accessHeader ? {
36
- "X-Access-Header": accessHeader
37
- } : {})
38
- };
30
+ const client = new _customer.CustomerAPIClient({
31
+ accessToken: context.accessToken,
32
+ refreshToken: context.refreshToken,
33
+ accessHeader: context.checkout.accessHeader,
34
+ baseUrl: context.checkout.url
35
+ });
39
36
  try {
40
- const contactResponse = await fetch(contactUrl, {
41
- method: "PATCH",
42
- headers,
43
- body: JSON.stringify({
44
- email: user?.email,
45
- phone: user?.phone
37
+ await Promise.all([client.updateContactInfo(shopId, {
38
+ email: user.email,
39
+ phone: user.phone
40
+ }), client.updatePersonalInfo(shopId, {
41
+ firstName: user?.firstName,
42
+ lastName: user?.lastName,
43
+ birthDate: user?.birthDate,
44
+ ...(user?.gender && {
45
+ gender: user?.gender
46
+ }),
47
+ ...(user?.title && {
48
+ title: user?.title
46
49
  })
47
- });
48
- if (contactResponse.status === 404) {
49
- throw new Error("Failed to update user's contact information");
50
- }
51
- if (contactResponse.status === 200) {
52
- const personalResponse = await fetch(personalUrl, {
53
- method: "PATCH",
54
- headers,
55
- body: JSON.stringify({
56
- firstName: user?.firstName,
57
- lastName: user?.lastName,
58
- birthDate: user?.birthDate,
59
- ...(user?.gender && {
60
- gender: user?.gender
61
- }),
62
- ...(user?.title && {
63
- title: user?.title
64
- })
65
- })
66
- });
67
- if (personalResponse.status === 404) {
68
- throw new Error("Failed to update user's personal information");
69
- }
70
- if (personalResponse.status === 200) {
71
- context.updateUser(user);
72
- return {
73
- user
74
- };
75
- }
76
- }
50
+ })]);
51
+ context.updateUser(user);
52
+ return {
53
+ user
54
+ };
77
55
  } catch (error) {
78
56
  context.log.error("Error while updating user information", error);
79
57
  throw error;
80
58
  }
81
- return {
82
- user
83
- };
84
59
  };
85
60
  const updatePassword = exports.updatePassword = async function updatePassword2({
86
61
  oldPassword,
87
62
  newPassword
88
63
  }, context) {
89
64
  const shopUser = context.user;
65
+ const client = new _customer.CustomerAPIClient({
66
+ accessToken: context.accessToken,
67
+ refreshToken: context.refreshToken,
68
+ accessHeader: context.checkout.accessHeader,
69
+ baseUrl: context.checkout.url
70
+ });
90
71
  try {
91
- const apiResponse = await fetch(`${context.checkout.url}/api/oauth/customer/password`, {
92
- headers: {
93
- Authorization: `Bearer ${context.accessToken}`,
94
- "X-Shop-Id": context.shopId.toString(),
95
- "Content-Type": "application/json",
96
- ...(context.checkout.accessHeader ? {
97
- "X-Access-Header": context.checkout.accessHeader
98
- } : {})
99
- },
100
- body: JSON.stringify({
101
- password: oldPassword,
102
- newPassword
103
- })
72
+ const user = client.updatePassword(context.shopId, {
73
+ password: oldPassword,
74
+ newPassword
104
75
  });
105
- if (apiResponse.status === 200) {
106
- if (shopUser?.id) {
107
- await context.destroySessionsForUserId(shopUser.id, [context.sessionId]);
108
- }
109
- return {
110
- user: await apiResponse.json()
111
- };
112
- } else if (apiResponse.status === 401) {
76
+ if (shopUser?.id) {
77
+ await context.destroySessionsForUserId(shopUser.id, [context.sessionId]);
78
+ }
79
+ return {
80
+ user
81
+ };
82
+ } catch (error) {
83
+ if (!(error instanceof _fetch.FetchError)) {
84
+ context.log.error("Error while updating user's password", error);
85
+ throw new Error(error);
86
+ }
87
+ if (error.response.status === _httpStatus.HttpStatusCode.UNAUTHORIZED) {
113
88
  throw new Error("401 - Failed to update user's password - Unauthorized request");
114
- } else if (apiResponse.status === 403) {
89
+ } else if (error.response.status === _httpStatus.HttpStatusCode.FORBIDDEN) {
115
90
  throw new Error("403 - Failed to update user's password - Invalid auth");
116
- } else if (apiResponse.status === 404) {
91
+ } else if (error.response.status === _httpStatus.HttpStatusCode.NOT_FOUND) {
117
92
  throw new Error("404 - Failed to update user's password - User not found");
118
93
  }
119
- } catch (error) {
120
- context.log.error("Error while updating user's password", error);
121
- throw new Error(error);
122
94
  }
123
- return {
124
- user: shopUser
125
- };
126
95
  };
@@ -1,7 +1,8 @@
1
+ import { CustomerAPIClient } from "../../../api/customer.mjs";
2
+ import { FetchError } from "../../../utils/fetch.mjs";
3
+ import { HttpStatusCode } from "../../../constants/httpStatus.mjs";
1
4
  export const updateShopUser = async function updateShopUser2(payload, context) {
2
5
  const shopId = context.shopId;
3
- const accessHeader = context.checkout.accessHeader;
4
- const checkoutUrl = context.checkout.url;
5
6
  const updatedUser = {
6
7
  email: payload.email,
7
8
  phone: payload.phone,
@@ -20,84 +21,63 @@ export const updateShopUser = async function updateShopUser2(payload, context) {
20
21
  ...context.user,
21
22
  ...updatedUser
22
23
  };
23
- const contactUrl = `${checkoutUrl}/api/customer/contact`;
24
- const personalUrl = `${checkoutUrl}/api/customer/personal`;
25
- const headers = {
26
- Authorization: `Bearer ${context.accessToken}`,
27
- "X-Shop-Id": shopId.toString(),
28
- "Content-Type": "application/json",
29
- ...accessHeader ? { "X-Access-Header": accessHeader } : {}
30
- };
24
+ const client = new CustomerAPIClient({
25
+ accessToken: context.accessToken,
26
+ refreshToken: context.refreshToken,
27
+ accessHeader: context.checkout.accessHeader,
28
+ baseUrl: context.checkout.url
29
+ });
31
30
  try {
32
- const contactResponse = await fetch(contactUrl, {
33
- method: "PATCH",
34
- headers,
35
- body: JSON.stringify({
36
- email: user?.email,
37
- phone: user?.phone
31
+ await Promise.all([
32
+ client.updateContactInfo(shopId, {
33
+ email: user.email,
34
+ phone: user.phone
35
+ }),
36
+ client.updatePersonalInfo(shopId, {
37
+ firstName: user?.firstName,
38
+ lastName: user?.lastName,
39
+ birthDate: user?.birthDate,
40
+ ...user?.gender && { gender: user?.gender },
41
+ ...user?.title && { title: user?.title }
38
42
  })
39
- });
40
- if (contactResponse.status === 404) {
41
- throw new Error("Failed to update user's contact information");
42
- }
43
- if (contactResponse.status === 200) {
44
- const personalResponse = await fetch(personalUrl, {
45
- method: "PATCH",
46
- headers,
47
- body: JSON.stringify({
48
- firstName: user?.firstName,
49
- lastName: user?.lastName,
50
- birthDate: user?.birthDate,
51
- ...user?.gender && { gender: user?.gender },
52
- ...user?.title && { title: user?.title }
53
- })
54
- });
55
- if (personalResponse.status === 404) {
56
- throw new Error("Failed to update user's personal information");
57
- }
58
- if (personalResponse.status === 200) {
59
- context.updateUser(user);
60
- return { user };
61
- }
62
- }
43
+ ]);
44
+ context.updateUser(user);
45
+ return { user };
63
46
  } catch (error) {
64
47
  context.log.error("Error while updating user information", error);
65
48
  throw error;
66
49
  }
67
- return { user };
68
50
  };
69
51
  export const updatePassword = async function updatePassword2({ oldPassword, newPassword }, context) {
70
52
  const shopUser = context.user;
53
+ const client = new CustomerAPIClient({
54
+ accessToken: context.accessToken,
55
+ refreshToken: context.refreshToken,
56
+ accessHeader: context.checkout.accessHeader,
57
+ baseUrl: context.checkout.url
58
+ });
71
59
  try {
72
- const apiResponse = await fetch(
73
- `${context.checkout.url}/api/oauth/customer/password`,
74
- {
75
- headers: {
76
- Authorization: `Bearer ${context.accessToken}`,
77
- "X-Shop-Id": context.shopId.toString(),
78
- "Content-Type": "application/json",
79
- ...context.checkout.accessHeader ? { "X-Access-Header": context.checkout.accessHeader } : {}
80
- },
81
- body: JSON.stringify({ password: oldPassword, newPassword })
82
- }
83
- );
84
- if (apiResponse.status === 200) {
85
- if (shopUser?.id) {
86
- await context.destroySessionsForUserId(shopUser.id, [context.sessionId]);
87
- }
88
- return { user: await apiResponse.json() };
89
- } else if (apiResponse.status === 401) {
60
+ const user = client.updatePassword(context.shopId, {
61
+ password: oldPassword,
62
+ newPassword
63
+ });
64
+ if (shopUser?.id) {
65
+ await context.destroySessionsForUserId(shopUser.id, [context.sessionId]);
66
+ }
67
+ return { user };
68
+ } catch (error) {
69
+ if (!(error instanceof FetchError)) {
70
+ context.log.error("Error while updating user's password", error);
71
+ throw new Error(error);
72
+ }
73
+ if (error.response.status === HttpStatusCode.UNAUTHORIZED) {
90
74
  throw new Error(
91
75
  "401 - Failed to update user's password - Unauthorized request"
92
76
  );
93
- } else if (apiResponse.status === 403) {
77
+ } else if (error.response.status === HttpStatusCode.FORBIDDEN) {
94
78
  throw new Error("403 - Failed to update user's password - Invalid auth");
95
- } else if (apiResponse.status === 404) {
79
+ } else if (error.response.status === HttpStatusCode.NOT_FOUND) {
96
80
  throw new Error("404 - Failed to update user's password - User not found");
97
81
  }
98
- } catch (error) {
99
- context.log.error("Error while updating user's password", error);
100
- throw new Error(error);
101
82
  }
102
- return { user: shopUser };
103
83
  };
@@ -4,23 +4,14 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getShopUserAddresses = void 0;
7
- var _fetch = require("../../../utils/fetch.cjs");
7
+ var _customer = require("../../../api/customer.cjs");
8
8
  const getShopUserAddresses = exports.getShopUserAddresses = async function getShopUserAddresses2(context) {
9
9
  const shopId = context.shopId;
10
- const accessHeader = context.checkout.accessHeader;
11
- const checkoutUrl = context.checkout.url;
12
- const response = await fetch(`${checkoutUrl}/api/oauth/customer/addresses`, {
13
- headers: {
14
- Authorization: `Bearer ${context.accessToken}`,
15
- "X-Shop-Id": shopId.toString(),
16
- Accept: "application/json",
17
- ...(accessHeader && {
18
- "X-Access-Header": accessHeader
19
- })
20
- }
10
+ const client = new _customer.CustomerAPIClient({
11
+ accessToken: context.accessToken,
12
+ refreshToken: context.refreshToken,
13
+ accessHeader: context.checkout.accessHeader,
14
+ baseUrl: context.checkout.url
21
15
  });
22
- if (!response.ok) {
23
- throw new _fetch.FetchError(response);
24
- }
25
- return (await response.json()).entities;
16
+ return (await client.getAddresses(shopId)).entities;
26
17
  };
@@ -1,22 +1,12 @@
1
- import { FetchError } from "../../../utils/fetch.mjs";
1
+ import { CustomerAPIClient } from "../../../api/customer.mjs";
2
2
  const getShopUserAddresses = async function getShopUserAddresses2(context) {
3
3
  const shopId = context.shopId;
4
- const accessHeader = context.checkout.accessHeader;
5
- const checkoutUrl = context.checkout.url;
6
- const response = await fetch(
7
- `${checkoutUrl}/api/oauth/customer/addresses`,
8
- {
9
- headers: {
10
- Authorization: `Bearer ${context.accessToken}`,
11
- "X-Shop-Id": shopId.toString(),
12
- Accept: "application/json",
13
- ...accessHeader && { "X-Access-Header": accessHeader }
14
- }
15
- }
16
- );
17
- if (!response.ok) {
18
- throw new FetchError(response);
19
- }
20
- return (await response.json()).entities;
4
+ const client = new CustomerAPIClient({
5
+ accessToken: context.accessToken,
6
+ refreshToken: context.refreshToken,
7
+ accessHeader: context.checkout.accessHeader,
8
+ baseUrl: context.checkout.url
9
+ });
10
+ return (await client.getAddresses(shopId)).entities;
21
11
  };
22
12
  export { getShopUserAddresses };
@@ -27,15 +27,10 @@ function getOAuthClient(context) {
27
27
  });
28
28
  }
29
29
  const saveUserOnSession = async (accessToken, context) => {
30
- const checkoutUrl = context.checkout.url;
31
- const shopId = context.shopId;
32
30
  const user = await (0, _user2.fetchUser)({
33
31
  accessToken,
34
32
  callback: ""
35
- }, {
36
- checkoutUrl,
37
- shopId
38
- });
33
+ }, context);
39
34
  context.updateUser(user);
40
35
  };
41
36
  async function postLogin(context, tokens) {
@@ -16,15 +16,7 @@ function getOAuthClient(context) {
16
16
  return new OAuthClient({ clientId, clientSecret, apiHost });
17
17
  }
18
18
  const saveUserOnSession = async (accessToken, context) => {
19
- const checkoutUrl = context.checkout.url;
20
- const shopId = context.shopId;
21
- const user = await fetchUser(
22
- { accessToken, callback: "" },
23
- {
24
- checkoutUrl,
25
- shopId
26
- }
27
- );
19
+ const user = await fetchUser({ accessToken, callback: "" }, context);
28
20
  context.updateUser(user);
29
21
  };
30
22
  async function postLogin(context, tokens) {
@@ -4,36 +4,26 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.refreshUser = exports.getUser = exports.fetchUser = void 0;
7
- var _fetch = require("../../utils/fetch.cjs");
8
- var _httpStatus = require("../../constants/httpStatus.cjs");
7
+ var _customer = require("../../api/customer.cjs");
9
8
  const getUser = exports.getUser = function getUser2(context) {
10
9
  return {
11
10
  user: context.user
12
11
  };
13
12
  };
14
- const fetchUser = exports.fetchUser = async function fetchUser2(payload, options) {
13
+ const fetchUser = exports.fetchUser = async function fetchUser2(payload, context) {
15
14
  const {
16
15
  accessToken
17
16
  } = payload;
18
17
  const {
19
- checkoutUrl,
20
- shopId,
21
- accessHeader
22
- } = options;
23
- const response = await fetch(`${checkoutUrl}/api/oauth/me`, {
24
- headers: {
25
- Authorization: `Bearer ${accessToken}`,
26
- "X-Shop-Id": shopId.toString(),
27
- "Content-Type": "application/json",
28
- ...(accessHeader && {
29
- "X-Access-Header": accessHeader
30
- })
31
- }
18
+ shopId
19
+ } = context;
20
+ const client = new _customer.CustomerAPIClient({
21
+ accessToken: context.accessToken,
22
+ refreshToken: context.refreshToken,
23
+ accessHeader: context.checkout.accessHeader,
24
+ baseUrl: context.checkout.url
32
25
  });
33
- if (!response.ok) {
34
- throw new _fetch.FetchError(response);
35
- }
36
- const user = await response.json();
26
+ const user = await client.getMe(shopId);
37
27
  return {
38
28
  ...user,
39
29
  authentication: {
@@ -48,29 +38,16 @@ const fetchUser = exports.fetchUser = async function fetchUser2(payload, options
48
38
  const refreshUser = exports.refreshUser = async function refreshUser2(context) {
49
39
  const {
50
40
  accessToken,
51
- checkout,
52
41
  shopId
53
42
  } = context;
54
- const validateStatus = status => {
55
- const {
56
- OK,
57
- MULTIPLE_CHOICES,
58
- FORBIDDEN
59
- } = _httpStatus.HttpStatusCode;
60
- return status >= OK && status < MULTIPLE_CHOICES || status === FORBIDDEN;
61
- };
62
- const response = await fetch(`${checkout.url}/api/oauth/me`, {
63
- headers: {
64
- Authorization: `Bearer ${accessToken}`,
65
- "X-Shop-Id": shopId.toString(),
66
- "Content-Type": "application/json"
67
- }
43
+ const client = new _customer.CustomerAPIClient({
44
+ accessToken: context.accessToken,
45
+ refreshToken: context.refreshToken,
46
+ accessHeader: context.checkout.accessHeader,
47
+ baseUrl: context.checkout.url
68
48
  });
69
- if (!response.ok || !validateStatus(response.status)) {
70
- throw new _fetch.FetchError(response);
71
- }
72
- const user = await response.json();
73
- if (user.id) {
49
+ const user = await client.getMe(shopId);
50
+ if (user?.id) {
74
51
  context.updateUser({
75
52
  ...user,
76
53
  authentication: {
@@ -1,20 +1,15 @@
1
- import { AuthConfig, RpcContext, RpcHandler, ShopUser } from '../../types';
1
+ import { AuthConfig, RpcHandler, ShopUser } from '../../types';
2
2
  declare const getUser: RpcHandler<{
3
3
  user: ShopUser | undefined;
4
4
  }>;
5
- declare const fetchUser: (payload: AuthConfig, options: {
6
- checkoutUrl: string;
7
- shopId: number;
8
- accessHeader?: string | undefined;
9
- }) => Promise<ShopUser>;
5
+ declare const fetchUser: RpcHandler<AuthConfig, ShopUser>;
10
6
  /**
11
- * This function adds a way to force fetch the user from checkout. In case the user is not logged in any longer the session will be destroyed and the returned user will be undefined.
7
+ * This function adds a way to force fetch the user from checkout.
8
+ * In case the user is not logged in any longer the session will be destroyed and the returned user will be undefined.
12
9
  * @param context
13
10
  * @returns ShopUser
14
11
  */
15
- declare const refreshUser: (context: RpcContext) => Promise<{
16
- user: ShopUser;
17
- } | {
18
- user: undefined;
12
+ declare const refreshUser: RpcHandler<{
13
+ user: ShopUser | undefined;
19
14
  }>;
20
15
  export { getUser, fetchUser, refreshUser };
@@ -1,25 +1,19 @@
1
- import { FetchError } from "../../utils/fetch.mjs";
2
- import { HttpStatusCode } from "../../constants/httpStatus.mjs";
1
+ import { CustomerAPIClient } from "../../api/customer.mjs";
3
2
  const getUser = function getUser2(context) {
4
3
  return {
5
4
  user: context.user
6
5
  };
7
6
  };
8
- const fetchUser = async function fetchUser2(payload, options) {
7
+ const fetchUser = async function fetchUser2(payload, context) {
9
8
  const { accessToken } = payload;
10
- const { checkoutUrl, shopId, accessHeader } = options;
11
- const response = await fetch(`${checkoutUrl}/api/oauth/me`, {
12
- headers: {
13
- Authorization: `Bearer ${accessToken}`,
14
- "X-Shop-Id": shopId.toString(),
15
- "Content-Type": "application/json",
16
- ...accessHeader && { "X-Access-Header": accessHeader }
17
- }
9
+ const { shopId } = context;
10
+ const client = new CustomerAPIClient({
11
+ accessToken: context.accessToken,
12
+ refreshToken: context.refreshToken,
13
+ accessHeader: context.checkout.accessHeader,
14
+ baseUrl: context.checkout.url
18
15
  });
19
- if (!response.ok) {
20
- throw new FetchError(response);
21
- }
22
- const user = await response.json();
16
+ const user = await client.getMe(shopId);
23
17
  return {
24
18
  ...user,
25
19
  authentication: {
@@ -30,23 +24,15 @@ const fetchUser = async function fetchUser2(payload, options) {
30
24
  };
31
25
  };
32
26
  const refreshUser = async function refreshUser2(context) {
33
- const { accessToken, checkout, shopId } = context;
34
- const validateStatus = (status) => {
35
- const { OK, MULTIPLE_CHOICES, FORBIDDEN } = HttpStatusCode;
36
- return status >= OK && status < MULTIPLE_CHOICES || status === FORBIDDEN;
37
- };
38
- const response = await fetch(`${checkout.url}/api/oauth/me`, {
39
- headers: {
40
- Authorization: `Bearer ${accessToken}`,
41
- "X-Shop-Id": shopId.toString(),
42
- "Content-Type": "application/json"
43
- }
27
+ const { accessToken, shopId } = context;
28
+ const client = new CustomerAPIClient({
29
+ accessToken: context.accessToken,
30
+ refreshToken: context.refreshToken,
31
+ accessHeader: context.checkout.accessHeader,
32
+ baseUrl: context.checkout.url
44
33
  });
45
- if (!response.ok || !validateStatus(response.status)) {
46
- throw new FetchError(response);
47
- }
48
- const user = await response.json();
49
- if (user.id) {
34
+ const user = await client.getMe(shopId);
35
+ if (user?.id) {
50
36
  context.updateUser({
51
37
  ...user,
52
38
  authentication: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-core",
3
- "version": "7.28.1",
3
+ "version": "7.28.2",
4
4
  "description": "Collection of essential utilities to work with the Storefront API",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -69,20 +69,20 @@
69
69
  "@scayle/eslint-config-storefront": "3.2.5",
70
70
  "@scayle/prettier-config-storefront": "2.0.2",
71
71
  "@types/crypto-js": "4.2.1",
72
- "@types/jest": "29.5.10",
73
- "@types/node": "20.10.0",
72
+ "@types/jest": "29.5.11",
73
+ "@types/node": "20.10.4",
74
74
  "@types/webpack-env": "1.18.4",
75
75
  "unbuild": "2.0.0",
76
- "eslint": "8.54.0",
76
+ "eslint": "8.55.0",
77
77
  "eslint-formatter-gitlab": "5.1.0",
78
78
  "jest": "29.7.0",
79
79
  "jest-junit": "16.0.0",
80
80
  "prettier": "3.0.0",
81
- "publint": "0.2.5",
81
+ "publint": "0.2.6",
82
82
  "rimraf": "5.0.5",
83
83
  "ts-jest": "29.1.1",
84
84
  "ts-node": "10.9.1",
85
- "typescript": "5.3.2",
85
+ "typescript": "5.3.3",
86
86
  "unstorage": "1.10.1"
87
87
  },
88
88
  "optionalDependencies": {