@shopware/api-client 0.5.0 → 1.0.0
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/README.md +208 -84
- package/{admin-api-types/apiSchema-6.5.3.0.json → api-types/adminApiSchema.json} +62470 -25854
- package/api-types/adminApiTypes.d.ts +84322 -0
- package/api-types/{apiSchema-6.4.19.0.json → storeApiSchema.json} +15295 -11433
- package/api-types/storeApiSchema.overrides.json +674 -0
- package/api-types/storeApiTypes.d.ts +8512 -0
- package/api-types/storeApiTypes.overrides.ts +182 -0
- package/dist/index.cjs +169 -106
- package/dist/index.d.cts +89259 -62623
- package/dist/index.d.mts +89259 -62623
- package/dist/index.d.ts +89259 -62623
- package/dist/index.mjs +165 -106
- package/package.json +20 -14
- package/admin-api-types/apiTypes-6.5.3.0.d.ts +0 -59568
- package/admin-api-types/index.d.ts +0 -1
- package/api-types/apiSchema-6.4.20.0.json +0 -13830
- package/api-types/apiSchema-6.5.0.0.json +0 -16669
- package/api-types/apiSchema-6.5.2.0.json +0 -12154
- package/api-types/apiSchema-6.5.3.0.json +0 -12154
- package/api-types/apiTypes-6.4.19.0.d.ts +0 -8170
- package/api-types/apiTypes-6.4.20.0.d.ts +0 -8168
- package/api-types/apiTypes-6.5.0.0.d.ts +0 -7244
- package/api-types/apiTypes-6.5.2.0.d.ts +0 -7738
- package/api-types/apiTypes-6.5.3.0.d.ts +0 -7738
- package/api-types/index.d.ts +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,41 @@
|
|
|
1
1
|
import { ofetch } from 'ofetch';
|
|
2
|
+
import { createHooks } from 'hookable';
|
|
3
|
+
import defu from 'defu';
|
|
4
|
+
|
|
5
|
+
function createHeaders(init) {
|
|
6
|
+
const _headers = {
|
|
7
|
+
"Content-Type": "application/json"
|
|
8
|
+
};
|
|
9
|
+
const handler = {
|
|
10
|
+
get: (target, prop) => {
|
|
11
|
+
if (prop === "apply") {
|
|
12
|
+
return apply;
|
|
13
|
+
}
|
|
14
|
+
return Reflect.get(target, prop);
|
|
15
|
+
},
|
|
16
|
+
set: (target, prop, value) => {
|
|
17
|
+
if (prop === "apply") {
|
|
18
|
+
throw new Error("Cannot override apply method");
|
|
19
|
+
}
|
|
20
|
+
return Reflect.set(target, prop, value);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const headersProxy = new Proxy(
|
|
24
|
+
_headers,
|
|
25
|
+
handler
|
|
26
|
+
);
|
|
27
|
+
function apply(headers) {
|
|
28
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
29
|
+
if (value) {
|
|
30
|
+
headersProxy[key] = value;
|
|
31
|
+
} else {
|
|
32
|
+
delete headersProxy[key];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
headersProxy.apply({ ...init });
|
|
37
|
+
return headersProxy;
|
|
38
|
+
}
|
|
2
39
|
|
|
3
40
|
var __defProp = Object.defineProperty;
|
|
4
41
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -9,14 +46,23 @@ var __publicField = (obj, key, value) => {
|
|
|
9
46
|
class ApiClientError extends Error {
|
|
10
47
|
constructor(response) {
|
|
11
48
|
let message = "Failed request";
|
|
12
|
-
|
|
49
|
+
const errorDetails = response._data || {
|
|
50
|
+
errors: [
|
|
51
|
+
{
|
|
52
|
+
title: "Unknown error",
|
|
53
|
+
detail: "API did not return errors, but request failed. Please check the network tab."
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
message += errorDetails.errors?.reduce((message2, error) => {
|
|
13
58
|
let pointer = "";
|
|
14
59
|
if (error.source?.pointer) {
|
|
15
60
|
pointer = `[${error.source.pointer}]`;
|
|
16
61
|
}
|
|
62
|
+
const details = error.detail ?? "No error details provided.";
|
|
17
63
|
return `${message2}
|
|
18
|
-
- [${error.title}]${pointer} ${
|
|
19
|
-
}, "");
|
|
64
|
+
- [${error.title}]${pointer} ${details}`;
|
|
65
|
+
}, "") ?? "";
|
|
20
66
|
super(message);
|
|
21
67
|
/**
|
|
22
68
|
* Flag to indicate if the request was successful.
|
|
@@ -43,9 +89,7 @@ class ApiClientError extends Error {
|
|
|
43
89
|
*/
|
|
44
90
|
__publicField(this, "headers");
|
|
45
91
|
this.name = "ApiClientError";
|
|
46
|
-
this.details =
|
|
47
|
-
errors: [{ title: "Unknown error", detail: "" }]
|
|
48
|
-
};
|
|
92
|
+
this.details = errorDetails;
|
|
49
93
|
this.ok = response.ok;
|
|
50
94
|
this.status = response.status;
|
|
51
95
|
this.statusText = response.statusText;
|
|
@@ -53,188 +97,203 @@ class ApiClientError extends Error {
|
|
|
53
97
|
this.headers = response.headers;
|
|
54
98
|
}
|
|
55
99
|
}
|
|
100
|
+
|
|
56
101
|
function errorInterceptor(response) {
|
|
57
102
|
throw new ApiClientError(response);
|
|
58
103
|
}
|
|
59
104
|
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const pathParams = requestPath.match(/{[^}]+}/g)?.map((param) => param.substring(1, param.length - 1)) || [];
|
|
64
|
-
const requestPathWithParams = pathParams.reduce((acc, paramName) => {
|
|
65
|
-
return acc.replace(`{${paramName}}`, params[paramName]);
|
|
105
|
+
function createPathWithParams(requestPath, pathParams) {
|
|
106
|
+
return Object.keys(pathParams || {}).reduce((acc, paramName) => {
|
|
107
|
+
return acc.replace(`{${paramName}}`, pathParams[paramName]);
|
|
66
108
|
}, requestPath);
|
|
67
|
-
const queryParamNames = queryParams?.split(",") || [];
|
|
68
|
-
const headerParamnames = headerParams?.split(",") || [];
|
|
69
|
-
const headers = {};
|
|
70
|
-
headerParamnames.forEach((paramName) => {
|
|
71
|
-
headers[paramName] = params[paramName];
|
|
72
|
-
});
|
|
73
|
-
const query = {};
|
|
74
|
-
queryParamNames.forEach((paramName) => {
|
|
75
|
-
let queryParamName = paramName;
|
|
76
|
-
if (Array.isArray(params[paramName]) && !queryParamName.includes("[]")) {
|
|
77
|
-
queryParamName += "[]";
|
|
78
|
-
}
|
|
79
|
-
query[queryParamName] = params[paramName];
|
|
80
|
-
});
|
|
81
|
-
const returnOptions = {
|
|
82
|
-
method: method.toUpperCase(),
|
|
83
|
-
headers,
|
|
84
|
-
query
|
|
85
|
-
};
|
|
86
|
-
if (!params) {
|
|
87
|
-
return [requestPathWithParams, returnOptions];
|
|
88
|
-
}
|
|
89
|
-
Object.keys(params).forEach((key) => {
|
|
90
|
-
if (!pathParams.includes(key) && !queryParamNames.includes(key) && !headerParamnames.includes(key)) {
|
|
91
|
-
returnOptions.body ?? (returnOptions.body = {});
|
|
92
|
-
Reflect.set(returnOptions.body, key, params[key]);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
return [requestPathWithParams, returnOptions];
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
function createAPIClient(params) {
|
|
99
|
-
const defaultHeaders = {
|
|
100
|
-
"sw-access-key": params.accessToken
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
112
|
+
const defaultHeaders = createHeaders({
|
|
113
|
+
"sw-access-key": params.accessToken,
|
|
114
|
+
Accept: "application/json",
|
|
115
|
+
"sw-context-token": params.contextToken,
|
|
116
|
+
...params.defaultHeaders
|
|
117
|
+
});
|
|
118
|
+
const apiClientHooks = createHooks();
|
|
105
119
|
const apiFetch = ofetch.create({
|
|
106
120
|
baseURL: params.baseURL,
|
|
107
121
|
// async onRequest({ request, options }) {},
|
|
108
122
|
// async onRequestError({ request, options, error }) {},
|
|
109
123
|
async onResponse(context) {
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
apiClientHooks.callHook("onSuccessResponse", context.response);
|
|
125
|
+
if (context.response.headers.has("sw-context-token") && defaultHeaders["sw-context-token"] !== context.response.headers.get("sw-context-token")) {
|
|
126
|
+
const newContextToken = context.response.headers.get(
|
|
127
|
+
"sw-context-token"
|
|
128
|
+
);
|
|
112
129
|
defaultHeaders["sw-context-token"] = newContextToken;
|
|
113
|
-
|
|
130
|
+
apiClientHooks.callHook("onContextChanged", newContextToken);
|
|
114
131
|
}
|
|
115
132
|
},
|
|
116
|
-
async onResponseError({
|
|
133
|
+
async onResponseError({ response }) {
|
|
134
|
+
apiClientHooks.callHook("onResponseError", response);
|
|
117
135
|
errorInterceptor(response);
|
|
118
136
|
}
|
|
119
137
|
});
|
|
120
138
|
async function invoke(pathParam, ...params2) {
|
|
121
|
-
const [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
return apiFetch(
|
|
139
|
+
const [, method, requestPath] = pathParam.split(" ");
|
|
140
|
+
const currentParams = params2[0] || {};
|
|
141
|
+
const requestPathWithParams = createPathWithParams(
|
|
126
142
|
requestPath,
|
|
127
|
-
|
|
128
|
-
...options,
|
|
129
|
-
headers: {
|
|
130
|
-
...defaultHeaders,
|
|
131
|
-
...options.headers
|
|
132
|
-
}
|
|
133
|
-
}
|
|
143
|
+
currentParams.pathParams
|
|
134
144
|
);
|
|
145
|
+
const fetchOptions = {
|
|
146
|
+
...currentParams.fetchOptions || {}
|
|
147
|
+
};
|
|
148
|
+
const resp = await apiFetch.raw(requestPathWithParams, {
|
|
149
|
+
...fetchOptions,
|
|
150
|
+
method,
|
|
151
|
+
body: currentParams.body,
|
|
152
|
+
headers: defu(defaultHeaders, currentParams.headers),
|
|
153
|
+
query: currentParams.query
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
data: resp._data,
|
|
157
|
+
status: resp.status
|
|
158
|
+
};
|
|
135
159
|
}
|
|
136
160
|
return {
|
|
137
|
-
invoke
|
|
161
|
+
invoke,
|
|
162
|
+
/**
|
|
163
|
+
* Default headers used in every client request (if not overriden in specific request).
|
|
164
|
+
*/
|
|
165
|
+
defaultHeaders,
|
|
166
|
+
hook: apiClientHooks.hook
|
|
138
167
|
};
|
|
139
168
|
}
|
|
169
|
+
|
|
140
170
|
function createAuthorizationHeader(token) {
|
|
141
171
|
if (!token)
|
|
142
|
-
return
|
|
172
|
+
return "";
|
|
143
173
|
if (token.startsWith("Bearer "))
|
|
144
174
|
return token;
|
|
145
175
|
return `Bearer ${token}`;
|
|
146
176
|
}
|
|
147
177
|
function createAdminAPIClient(params) {
|
|
178
|
+
const isTokenBasedAuth = params.credentials?.grant_type === "client_credentials";
|
|
148
179
|
const sessionData = {
|
|
149
180
|
accessToken: params.sessionData?.accessToken || "",
|
|
150
181
|
refreshToken: params.sessionData?.refreshToken || "",
|
|
151
182
|
expirationTime: Number(params.sessionData?.expirationTime || 0)
|
|
152
183
|
};
|
|
184
|
+
const defaultHeaders = createHeaders({
|
|
185
|
+
Authorization: createAuthorizationHeader(sessionData.accessToken),
|
|
186
|
+
Accept: "application/json"
|
|
187
|
+
});
|
|
188
|
+
const apiClientHooks = createHooks();
|
|
189
|
+
function getSessionData() {
|
|
190
|
+
return { ...sessionData };
|
|
191
|
+
}
|
|
192
|
+
function setSessionData(data) {
|
|
193
|
+
sessionData.accessToken = data.accessToken;
|
|
194
|
+
sessionData.refreshToken = data.refreshToken || "";
|
|
195
|
+
sessionData.expirationTime = data.expirationTime;
|
|
196
|
+
return getSessionData();
|
|
197
|
+
}
|
|
153
198
|
function updateSessionData(responseData) {
|
|
154
199
|
if (responseData?.access_token) {
|
|
155
200
|
defaultHeaders.Authorization = createAuthorizationHeader(
|
|
156
201
|
responseData.access_token
|
|
157
202
|
);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
...sessionData
|
|
203
|
+
const dataCopy = setSessionData({
|
|
204
|
+
accessToken: responseData.access_token,
|
|
205
|
+
refreshToken: responseData.refresh_token,
|
|
206
|
+
expirationTime: Date.now() + responseData.expires_in * 1e3
|
|
163
207
|
});
|
|
208
|
+
apiClientHooks.callHook("onAuthChange", dataCopy);
|
|
164
209
|
}
|
|
165
210
|
}
|
|
166
|
-
function setSessionData(data) {
|
|
167
|
-
sessionData.accessToken = data.accessToken;
|
|
168
|
-
sessionData.refreshToken = data.refreshToken;
|
|
169
|
-
sessionData.expirationTime = data.expirationTime;
|
|
170
|
-
return getSessionData();
|
|
171
|
-
}
|
|
172
|
-
function getSessionData() {
|
|
173
|
-
return { ...sessionData };
|
|
174
|
-
}
|
|
175
|
-
const defaultHeaders = {
|
|
176
|
-
Authorization: createAuthorizationHeader(sessionData.accessToken)
|
|
177
|
-
};
|
|
178
211
|
const apiFetch = ofetch.create({
|
|
179
212
|
baseURL: params.baseURL,
|
|
180
213
|
async onRequest({ request, options }) {
|
|
181
214
|
const isExpired = sessionData.expirationTime <= Date.now();
|
|
182
215
|
if (isExpired && !request.toString().includes("/oauth/token")) {
|
|
216
|
+
if (!params.credentials && !isTokenBasedAuth && !sessionData.refreshToken) {
|
|
217
|
+
console.warn(
|
|
218
|
+
"[ApiClientWarning] No `credentials` or `sessionData` provided. Provide at least one of them to ensure authentication."
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const body = params.credentials && !sessionData.refreshToken ? params.credentials : {
|
|
222
|
+
grant_type: "refresh_token",
|
|
223
|
+
client_id: "administration",
|
|
224
|
+
refresh_token: sessionData.refreshToken
|
|
225
|
+
};
|
|
183
226
|
await ofetch("/oauth/token", {
|
|
184
227
|
baseURL: params.baseURL,
|
|
185
228
|
method: "POST",
|
|
186
|
-
body
|
|
187
|
-
grant_type: "refresh_token",
|
|
188
|
-
client_id: "administration",
|
|
189
|
-
refresh_token: sessionData.refreshToken || ""
|
|
190
|
-
},
|
|
229
|
+
body,
|
|
191
230
|
headers: defaultHeaders,
|
|
192
231
|
onResponseError({ response }) {
|
|
193
232
|
errorInterceptor(response);
|
|
194
233
|
},
|
|
195
234
|
onResponse(context) {
|
|
235
|
+
if (!context.response._data)
|
|
236
|
+
return;
|
|
196
237
|
updateSessionData(context.response._data);
|
|
238
|
+
options.headers = {
|
|
239
|
+
...options.headers,
|
|
240
|
+
Authorization: createAuthorizationHeader(
|
|
241
|
+
context.response._data.access_token
|
|
242
|
+
)
|
|
243
|
+
};
|
|
197
244
|
}
|
|
198
245
|
});
|
|
199
246
|
}
|
|
200
247
|
},
|
|
201
248
|
async onResponse(context) {
|
|
249
|
+
apiClientHooks.callHook("onSuccessResponse", context.response);
|
|
202
250
|
updateSessionData(context.response._data);
|
|
203
251
|
},
|
|
204
|
-
async onResponseError({
|
|
252
|
+
async onResponseError({ response }) {
|
|
253
|
+
apiClientHooks.callHook("onResponseError", response);
|
|
205
254
|
errorInterceptor(response);
|
|
206
255
|
}
|
|
207
256
|
});
|
|
208
|
-
async function invoke(pathParam, params2) {
|
|
209
|
-
const [
|
|
210
|
-
|
|
211
|
-
params2
|
|
212
|
-
);
|
|
213
|
-
return apiFetch(
|
|
257
|
+
async function invoke(pathParam, ...params2) {
|
|
258
|
+
const [, method, requestPath] = pathParam.split(" ");
|
|
259
|
+
const requestPathWithParams = createPathWithParams(
|
|
214
260
|
requestPath,
|
|
215
|
-
|
|
216
|
-
...options,
|
|
217
|
-
headers: {
|
|
218
|
-
...defaultHeaders,
|
|
219
|
-
...options.headers
|
|
220
|
-
}
|
|
221
|
-
}
|
|
261
|
+
params2[0]?.pathParams
|
|
222
262
|
);
|
|
263
|
+
const fetchOptions = {
|
|
264
|
+
...params2[0]?.fetchOptions || {}
|
|
265
|
+
};
|
|
266
|
+
const resp = await apiFetch.raw(requestPathWithParams, {
|
|
267
|
+
...fetchOptions,
|
|
268
|
+
method,
|
|
269
|
+
body: params2[0]?.body,
|
|
270
|
+
headers: defu(defaultHeaders, params2[0]?.headers),
|
|
271
|
+
query: params2[0]?.query
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
data: resp._data,
|
|
275
|
+
status: resp.status
|
|
276
|
+
};
|
|
223
277
|
}
|
|
224
278
|
return {
|
|
225
|
-
/**
|
|
226
|
-
* Invoke API request based on provided path definition.
|
|
227
|
-
*/
|
|
228
279
|
invoke,
|
|
229
280
|
/**
|
|
230
281
|
* Enables to change session data in runtime. Useful for testing purposes.
|
|
231
|
-
* Setting session data with this
|
|
282
|
+
* Setting session data with this method will **not** fire `onAuthChange` hook.
|
|
232
283
|
*/
|
|
233
284
|
setSessionData,
|
|
234
285
|
/**
|
|
235
286
|
* Returns current session data. Useful for testing purposes, as in most cases you'll want to use `onAuthChange` hook for that.
|
|
236
287
|
*/
|
|
237
|
-
getSessionData
|
|
288
|
+
getSessionData,
|
|
289
|
+
/**
|
|
290
|
+
* Default headers used in every client request (if not overriden in specific request).
|
|
291
|
+
*/
|
|
292
|
+
defaultHeaders,
|
|
293
|
+
/**
|
|
294
|
+
* Available hooks for the client.
|
|
295
|
+
*/
|
|
296
|
+
hook: apiClientHooks.hook
|
|
238
297
|
};
|
|
239
298
|
}
|
|
240
299
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopware/api-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Shopware client for API connection.",
|
|
5
5
|
"author": "Shopware",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/shopware/frontends.git"
|
|
@@ -35,31 +36,36 @@
|
|
|
35
36
|
"types": "./dist/index.d.mts",
|
|
36
37
|
"default": "./dist/index.mjs"
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
|
+
},
|
|
40
|
+
"./api-types": "./api-types/storeApiTypes.d.ts",
|
|
41
|
+
"./store-api-types": "./api-types/storeApiTypes.d.ts",
|
|
42
|
+
"./admin-api-types": "./api-types/adminApiTypes.d.ts"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
41
|
-
"@
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"eslint-config-shopware": "0.0.
|
|
45
|
+
"@codspeed/vitest-plugin": "3.1.0",
|
|
46
|
+
"@types/prettier": "3.0.0",
|
|
47
|
+
"@vitest/coverage-v8": "1.6.0",
|
|
48
|
+
"prettier": "3.3.2",
|
|
49
|
+
"unbuild": "2.0.0",
|
|
50
|
+
"vitest": "1.6.0",
|
|
51
|
+
"eslint-config-shopware": "0.0.9",
|
|
48
52
|
"tsconfig": "0.0.0"
|
|
49
53
|
},
|
|
50
54
|
"dependencies": {
|
|
51
|
-
"
|
|
55
|
+
"defu": "6.1.4",
|
|
56
|
+
"hookable": "5.5.3",
|
|
57
|
+
"ofetch": "1.3.4"
|
|
52
58
|
},
|
|
53
59
|
"scripts": {
|
|
54
60
|
"build": "export NODE_ENV=production && unbuild && pnpm build:types",
|
|
55
61
|
"build:types": "tsc ./src/*.ts --declaration --allowJs --emitDeclarationOnly --outDir ./temp --skipLibCheck",
|
|
56
62
|
"dev": "export NODE_ENV=development && unbuild --stub",
|
|
57
|
-
"generate": "esno ../api-gen/src/cli.ts generate",
|
|
58
63
|
"lint": "eslint src/**/*.ts* --fix --max-warnings=0 && pnpm run typecheck",
|
|
59
64
|
"typecheck": "tsc --noEmit",
|
|
60
|
-
"test": "vitest run
|
|
61
|
-
"test:types": "vitest typecheck --run",
|
|
65
|
+
"test": "vitest run --typecheck",
|
|
62
66
|
"test:bench": "vitest bench",
|
|
63
|
-
"test:watch": "vitest"
|
|
67
|
+
"test:watch": "vitest --typecheck",
|
|
68
|
+
"generate-types": "esno ../api-gen/src/cli.ts generate --apiType=store",
|
|
69
|
+
"generate-admin-types": "esno ../api-gen/src/cli.ts generate --apiType=admin"
|
|
64
70
|
}
|
|
65
71
|
}
|