@hubcommerce/n11 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/CHANGELOG.md +48 -0
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/categories/api.d.ts +12 -0
- package/dist/categories/api.d.ts.map +1 -0
- package/dist/categories/api.js +34 -0
- package/dist/client.d.ts +37 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +70 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/connection.d.ts +5 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +4 -0
- package/dist/constants/defaults.d.ts +16 -0
- package/dist/constants/defaults.d.ts.map +1 -0
- package/dist/constants/defaults.js +15 -0
- package/dist/constants/hosts.d.ts +9 -0
- package/dist/constants/hosts.d.ts.map +1 -0
- package/dist/constants/hosts.js +25 -0
- package/dist/constants/page-limits.d.ts +15 -0
- package/dist/constants/page-limits.d.ts.map +1 -0
- package/dist/constants/page-limits.js +14 -0
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +162 -0
- package/dist/http/query.d.ts +2 -0
- package/dist/http/query.d.ts.map +1 -0
- package/dist/http/query.js +10 -0
- package/dist/http/response.d.ts +12 -0
- package/dist/http/response.d.ts.map +1 -0
- package/dist/http/response.js +43 -0
- package/dist/http/security.d.ts +6 -0
- package/dist/http/security.d.ts.map +1 -0
- package/dist/http/security.js +29 -0
- package/dist/http/signals.d.ts +5 -0
- package/dist/http/signals.d.ts.map +1 -0
- package/dist/http/signals.js +26 -0
- package/dist/http.d.ts +12 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +143 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/invoice/api.d.ts +14 -0
- package/dist/invoice/api.d.ts.map +1 -0
- package/dist/invoice/api.js +12 -0
- package/dist/invoice/parse.d.ts +3 -0
- package/dist/invoice/parse.d.ts.map +1 -0
- package/dist/invoice/parse.js +12 -0
- package/dist/invoice/send-link.d.ts +9 -0
- package/dist/invoice/send-link.d.ts.map +1 -0
- package/dist/invoice/send-link.js +16 -0
- package/dist/invoice/soap-body.d.ts +6 -0
- package/dist/invoice/soap-body.d.ts.map +1 -0
- package/dist/invoice/soap-body.js +23 -0
- package/dist/invoice/validate.d.ts +7 -0
- package/dist/invoice/validate.d.ts.map +1 -0
- package/dist/invoice/validate.js +73 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +61 -0
- package/dist/orders/api.d.ts +22 -0
- package/dist/orders/api.d.ts.map +1 -0
- package/dist/orders/api.js +40 -0
- package/dist/orders/date.d.ts +12 -0
- package/dist/orders/date.d.ts.map +1 -0
- package/dist/orders/date.js +21 -0
- package/dist/orders/labor-cost.d.ts +9 -0
- package/dist/orders/labor-cost.d.ts.map +1 -0
- package/dist/orders/labor-cost.js +23 -0
- package/dist/orders/list.d.ts +23 -0
- package/dist/orders/list.d.ts.map +1 -0
- package/dist/orders/list.js +76 -0
- package/dist/orders/paths.d.ts +7 -0
- package/dist/orders/paths.d.ts.map +1 -0
- package/dist/orders/paths.js +6 -0
- package/dist/orders/reconciler.d.ts +30 -0
- package/dist/orders/reconciler.d.ts.map +1 -0
- package/dist/orders/reconciler.js +38 -0
- package/dist/orders/shipping.d.ts +11 -0
- package/dist/orders/shipping.d.ts.map +1 -0
- package/dist/orders/shipping.js +57 -0
- package/dist/orders/update.d.ts +15 -0
- package/dist/orders/update.d.ts.map +1 -0
- package/dist/orders/update.js +25 -0
- package/dist/products/api.d.ts +21 -0
- package/dist/products/api.d.ts.map +1 -0
- package/dist/products/api.js +35 -0
- package/dist/products/builder.d.ts +18 -0
- package/dist/products/builder.d.ts.map +1 -0
- package/dist/products/builder.js +138 -0
- package/dist/products/paths.d.ts +6 -0
- package/dist/products/paths.d.ts.map +1 -0
- package/dist/products/paths.js +5 -0
- package/dist/products/query.d.ts +9 -0
- package/dist/products/query.d.ts.map +1 -0
- package/dist/products/query.js +49 -0
- package/dist/products/tasks.d.ts +12 -0
- package/dist/products/tasks.d.ts.map +1 -0
- package/dist/products/tasks.js +49 -0
- package/dist/qna/answer.d.ts +10 -0
- package/dist/qna/answer.d.ts.map +1 -0
- package/dist/qna/answer.js +23 -0
- package/dist/qna/api.d.ts +17 -0
- package/dist/qna/api.d.ts.map +1 -0
- package/dist/qna/api.js +20 -0
- package/dist/qna/detail.d.ts +9 -0
- package/dist/qna/detail.d.ts.map +1 -0
- package/dist/qna/detail.js +18 -0
- package/dist/qna/list.d.ts +9 -0
- package/dist/qna/list.d.ts.map +1 -0
- package/dist/qna/list.js +16 -0
- package/dist/qna/parse.d.ts +6 -0
- package/dist/qna/parse.d.ts.map +1 -0
- package/dist/qna/parse.js +65 -0
- package/dist/qna/soap-body.d.ts +14 -0
- package/dist/qna/soap-body.d.ts.map +1 -0
- package/dist/qna/soap-body.js +48 -0
- package/dist/qna/validate.d.ts +7 -0
- package/dist/qna/validate.d.ts.map +1 -0
- package/dist/qna/validate.js +75 -0
- package/dist/rate-limit/governor.d.ts +8 -0
- package/dist/rate-limit/governor.d.ts.map +1 -0
- package/dist/rate-limit/governor.js +30 -0
- package/dist/rate-limit/groups.d.ts +34 -0
- package/dist/rate-limit/groups.d.ts.map +1 -0
- package/dist/rate-limit/groups.js +32 -0
- package/dist/retry.d.ts +5 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +22 -0
- package/dist/returns/api.d.ts +21 -0
- package/dist/returns/api.d.ts.map +1 -0
- package/dist/returns/api.js +36 -0
- package/dist/returns/call.d.ts +2 -0
- package/dist/returns/call.d.ts.map +1 -0
- package/dist/returns/call.js +1 -0
- package/dist/returns/cancel-partial.d.ts +9 -0
- package/dist/returns/cancel-partial.d.ts.map +1 -0
- package/dist/returns/cancel-partial.js +18 -0
- package/dist/returns/list.d.ts +9 -0
- package/dist/returns/list.d.ts.map +1 -0
- package/dist/returns/list.js +16 -0
- package/dist/returns/mutations.d.ts +11 -0
- package/dist/returns/mutations.d.ts.map +1 -0
- package/dist/returns/mutations.js +22 -0
- package/dist/returns/parse.d.ts +6 -0
- package/dist/returns/parse.d.ts.map +1 -0
- package/dist/returns/parse.js +89 -0
- package/dist/returns/reasons.d.ts +11 -0
- package/dist/returns/reasons.d.ts.map +1 -0
- package/dist/returns/reasons.js +20 -0
- package/dist/returns/soap-body.d.ts +26 -0
- package/dist/returns/soap-body.d.ts.map +1 -0
- package/dist/returns/soap-body.js +95 -0
- package/dist/soap/call.d.ts +8 -0
- package/dist/soap/call.d.ts.map +1 -0
- package/dist/soap/call.js +22 -0
- package/dist/soap/client.d.ts +13 -0
- package/dist/soap/client.d.ts.map +1 -0
- package/dist/soap/client.js +125 -0
- package/dist/soap/envelope.d.ts +10 -0
- package/dist/soap/envelope.d.ts.map +1 -0
- package/dist/soap/envelope.js +47 -0
- package/dist/soap/operations.d.ts +6 -0
- package/dist/soap/operations.d.ts.map +1 -0
- package/dist/soap/operations.js +91 -0
- package/dist/soap/parse.d.ts +17 -0
- package/dist/soap/parse.d.ts.map +1 -0
- package/dist/soap/parse.js +37 -0
- package/dist/tasks/api.d.ts +11 -0
- package/dist/tasks/api.d.ts.map +1 -0
- package/dist/tasks/api.js +44 -0
- package/dist/tasks/module.d.ts +14 -0
- package/dist/tasks/module.d.ts.map +1 -0
- package/dist/tasks/module.js +15 -0
- package/dist/tasks/poller.d.ts +12 -0
- package/dist/tasks/poller.d.ts.map +1 -0
- package/dist/tasks/poller.js +133 -0
- package/dist/tasks/types.d.ts +27 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/types.js +2 -0
- package/dist/testing/fixtures/category-leaf.json +11 -0
- package/dist/testing/fixtures/order-package.json +12 -0
- package/dist/testing/fixtures/task-partial-fail.json +18 -0
- package/dist/testing/fixtures.d.ts +76 -0
- package/dist/testing/fixtures.d.ts.map +1 -0
- package/dist/testing/fixtures.js +85 -0
- package/dist/testing/guard.d.ts +21 -0
- package/dist/testing/guard.d.ts.map +1 -0
- package/dist/testing/guard.js +50 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/mock-client.d.ts +49 -0
- package/dist/testing/mock-client.d.ts.map +1 -0
- package/dist/testing/mock-client.js +100 -0
- package/dist/types/invoice.d.ts +21 -0
- package/dist/types/invoice.d.ts.map +1 -0
- package/dist/types/invoice.js +1 -0
- package/dist/types/orders.d.ts +127 -0
- package/dist/types/orders.d.ts.map +1 -0
- package/dist/types/orders.js +1 -0
- package/dist/types/products.d.ts +146 -0
- package/dist/types/products.d.ts.map +1 -0
- package/dist/types/products.js +1 -0
- package/dist/types/qna.d.ts +61 -0
- package/dist/types/qna.d.ts.map +1 -0
- package/dist/types/qna.js +1 -0
- package/dist/types/returns.d.ts +96 -0
- package/dist/types/returns.d.ts.map +1 -0
- package/dist/types/returns.js +1 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/wire/normalize.d.ts +20 -0
- package/dist/wire/normalize.d.ts.map +1 -0
- package/dist/wire/normalize.js +158 -0
- package/dist/wire/validate.d.ts +6 -0
- package/dist/wire/validate.d.ts.map +1 -0
- package/dist/wire/validate.js +53 -0
- package/package.json +53 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** SOAP response govdesinden tag icerigi cikarir (namespace-duyarsiz). */
|
|
2
|
+
export function extractXmlTag(xml, tagName) {
|
|
3
|
+
const pattern = new RegExp(`<(?:\\w+:)?${tagName}[^>]*>([\\s\\S]*?)<\\/(?:\\w+:)?${tagName}>`, "i");
|
|
4
|
+
const match = xml.match(pattern);
|
|
5
|
+
return match?.[1]?.trim() || undefined;
|
|
6
|
+
}
|
|
7
|
+
/** Tekrarlayan sibling elementleri parse eder. */
|
|
8
|
+
export function extractXmlElements(xml, tagName) {
|
|
9
|
+
const pattern = new RegExp(`<(?:\\w+:)?${tagName}[^>]*>([\\s\\S]*?)<\\/(?:\\w+:)?${tagName}>`, "gi");
|
|
10
|
+
const results = [];
|
|
11
|
+
let match;
|
|
12
|
+
while ((match = pattern.exec(xml)) !== null) {
|
|
13
|
+
results.push(match[1] ?? "");
|
|
14
|
+
}
|
|
15
|
+
return results;
|
|
16
|
+
}
|
|
17
|
+
export function parseSoapResult(xml) {
|
|
18
|
+
return {
|
|
19
|
+
status: extractXmlTag(xml, "status"),
|
|
20
|
+
errorMessage: extractXmlTag(xml, "errorMessage"),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function parsePagingData(xml) {
|
|
24
|
+
const fragment = extractXmlTag(xml, "pagingData") ?? xml;
|
|
25
|
+
return {
|
|
26
|
+
currentPage: extractXmlTag(fragment, "currentPage"),
|
|
27
|
+
pageSize: extractXmlTag(fragment, "pageSize"),
|
|
28
|
+
totalCount: extractXmlTag(fragment, "totalCount"),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function isSoapFailure(xml) {
|
|
32
|
+
const status = extractXmlTag(xml, "status")?.toLowerCase();
|
|
33
|
+
return status === "failure";
|
|
34
|
+
}
|
|
35
|
+
export function extractSoapFaultString(xml) {
|
|
36
|
+
return extractXmlTag(xml, "faultstring");
|
|
37
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { N11HttpClient } from "../http.js";
|
|
2
|
+
import type { N11Config, N11Result } from "../types.js";
|
|
3
|
+
import type { TaskKind, TaskStatusPayload } from "./types.js";
|
|
4
|
+
export declare class TasksApi {
|
|
5
|
+
private readonly http;
|
|
6
|
+
private readonly _config;
|
|
7
|
+
constructor(http: N11HttpClient, _config: N11Config);
|
|
8
|
+
/** Faz 1 — POST task-details/page-query (G4 tek poll hedefi) */
|
|
9
|
+
pageQuery(taskId: string, _kind: TaskKind, signal?: AbortSignal): Promise<N11Result<TaskStatusPayload>>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/tasks/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAK9D,qBAAa,QAAQ;IAEjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,SAAS;IAKrC,gEAAgE;IAC1D,SAAS,CACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;CAoCzC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TASK_DETAILS_PATH } from "./types.js";
|
|
2
|
+
import { N11_TASK_DETAILS_MAX_PAGE_SIZE } from "../constants/page-limits.js";
|
|
3
|
+
import { normalizeTaskStatusPayload } from "../wire/normalize.js";
|
|
4
|
+
export class TasksApi {
|
|
5
|
+
http;
|
|
6
|
+
_config;
|
|
7
|
+
constructor(http, _config) {
|
|
8
|
+
this.http = http;
|
|
9
|
+
this._config = _config;
|
|
10
|
+
void this._config;
|
|
11
|
+
}
|
|
12
|
+
/** Faz 1 — POST task-details/page-query (G4 tek poll hedefi) */
|
|
13
|
+
async pageQuery(taskId, _kind, signal) {
|
|
14
|
+
void _kind;
|
|
15
|
+
const numericTaskId = Number(taskId);
|
|
16
|
+
const body = {
|
|
17
|
+
taskId: Number.isFinite(numericTaskId) ? numericTaskId : taskId,
|
|
18
|
+
pageable: {
|
|
19
|
+
page: 0,
|
|
20
|
+
size: N11_TASK_DETAILS_MAX_PAGE_SIZE,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const result = await this.http.requestJson({
|
|
24
|
+
resource: "ms",
|
|
25
|
+
method: "POST",
|
|
26
|
+
path: TASK_DETAILS_PATH,
|
|
27
|
+
body,
|
|
28
|
+
retryPolicy: "safe",
|
|
29
|
+
signal,
|
|
30
|
+
});
|
|
31
|
+
if (!result.ok) {
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
const normalized = normalizeTaskStatusPayload(result.data);
|
|
35
|
+
if (!normalized.status) {
|
|
36
|
+
return {
|
|
37
|
+
ok: false,
|
|
38
|
+
reason: "Task detail yanitinda status bulunamadi.",
|
|
39
|
+
raw: normalized.raw,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { ok: true, data: normalized };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TaskPollResult, TaskPollOptions, TaskStatusPayload } from "./types.js";
|
|
2
|
+
import type { TaskKind } from "./types.js";
|
|
3
|
+
import type { TasksApi } from "./api.js";
|
|
4
|
+
import type { TaskPoller } from "./poller.js";
|
|
5
|
+
import type { N11Result } from "../types.js";
|
|
6
|
+
/** Facade: pageQuery (TasksApi) + waitForResult (TaskPoller). */
|
|
7
|
+
export declare class N11TasksModule {
|
|
8
|
+
private readonly api;
|
|
9
|
+
private readonly poller;
|
|
10
|
+
constructor(api: TasksApi, poller: TaskPoller);
|
|
11
|
+
pageQuery(taskId: string, kind: TaskKind, signal?: AbortSignal): Promise<N11Result<TaskStatusPayload>>;
|
|
12
|
+
waitForResult(taskId: string, options: TaskPollOptions): Promise<N11Result<TaskPollResult>>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/tasks/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,iEAAiE;AACjE,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,UAAU;IAGrC,SAAS,CACP,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAIxC,aAAa,CACX,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;CAGtC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Facade: pageQuery (TasksApi) + waitForResult (TaskPoller). */
|
|
2
|
+
export class N11TasksModule {
|
|
3
|
+
api;
|
|
4
|
+
poller;
|
|
5
|
+
constructor(api, poller) {
|
|
6
|
+
this.api = api;
|
|
7
|
+
this.poller = poller;
|
|
8
|
+
}
|
|
9
|
+
pageQuery(taskId, kind, signal) {
|
|
10
|
+
return this.api.pageQuery(taskId, kind, signal);
|
|
11
|
+
}
|
|
12
|
+
waitForResult(taskId, options) {
|
|
13
|
+
return this.poller.waitForResult(taskId, options);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { N11Hooks, N11Result } from "../types.js";
|
|
2
|
+
import type { TaskPollOptions, TaskPollResult, TaskStatusFetcher } from "./types.js";
|
|
3
|
+
export declare class TaskPoller {
|
|
4
|
+
private readonly fetchStatus;
|
|
5
|
+
private readonly getHooks;
|
|
6
|
+
private readonly debug;
|
|
7
|
+
private readonly inFlight;
|
|
8
|
+
constructor(fetchStatus: TaskStatusFetcher, getHooks: () => N11Hooks, debug?: boolean);
|
|
9
|
+
waitForResult(taskId: string, options: TaskPollOptions): Promise<N11Result<TaskPollResult>>;
|
|
10
|
+
private pollUntilTerminal;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../../src/tasks/poller.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAY,eAAe,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAyB/F,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IALxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyD;gBAG/D,WAAW,EAAE,iBAAiB,EAC9B,QAAQ,EAAE,MAAM,QAAQ,EACxB,KAAK,UAAQ;IAGhC,aAAa,CACX,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YAevB,iBAAiB;CAkFhC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { N11_DEFAULT_TASK_BACKOFF_MULTIPLIER, N11_DEFAULT_TASK_POLL_INTERVAL_MS, N11_DEFAULT_TASK_POLL_MAX_INTERVAL_MS, N11_DEFAULT_TASK_POLL_TIMEOUT_MS, N11_TASK_IN_PROGRESS_STATUS, N11_TASK_TERMINAL_STATUSES, } from "../constants/defaults.js";
|
|
2
|
+
import { TaskPollAbortedError, TaskTimeoutError, UnknownTaskStatusError, toFailure, } from "../errors.js";
|
|
3
|
+
import { buildHttpObservabilityMeta, n11Log } from "../logger.js";
|
|
4
|
+
const MAX_UNKNOWN_STATUS = 3;
|
|
5
|
+
function classifyTaskStatus(status) {
|
|
6
|
+
const normalized = status.toUpperCase();
|
|
7
|
+
if (normalized === N11_TASK_IN_PROGRESS_STATUS) {
|
|
8
|
+
return "continue";
|
|
9
|
+
}
|
|
10
|
+
if (N11_TASK_TERMINAL_STATUSES.includes(normalized)) {
|
|
11
|
+
return "terminal";
|
|
12
|
+
}
|
|
13
|
+
return "unknown";
|
|
14
|
+
}
|
|
15
|
+
function toPollResult(payload) {
|
|
16
|
+
return {
|
|
17
|
+
status: payload.status,
|
|
18
|
+
failedItemCount: payload.failedItemCount,
|
|
19
|
+
errors: payload.errors,
|
|
20
|
+
rows: payload.rows,
|
|
21
|
+
raw: payload.raw,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export class TaskPoller {
|
|
25
|
+
fetchStatus;
|
|
26
|
+
getHooks;
|
|
27
|
+
debug;
|
|
28
|
+
inFlight = new Map();
|
|
29
|
+
constructor(fetchStatus, getHooks, debug = false) {
|
|
30
|
+
this.fetchStatus = fetchStatus;
|
|
31
|
+
this.getHooks = getHooks;
|
|
32
|
+
this.debug = debug;
|
|
33
|
+
}
|
|
34
|
+
waitForResult(taskId, options) {
|
|
35
|
+
const key = `${options.kind}:${taskId}`;
|
|
36
|
+
const existing = this.inFlight.get(key);
|
|
37
|
+
if (existing) {
|
|
38
|
+
return existing;
|
|
39
|
+
}
|
|
40
|
+
const promise = this.pollUntilTerminal(taskId, options);
|
|
41
|
+
this.inFlight.set(key, promise);
|
|
42
|
+
void promise.finally(() => {
|
|
43
|
+
this.inFlight.delete(key);
|
|
44
|
+
});
|
|
45
|
+
return promise;
|
|
46
|
+
}
|
|
47
|
+
async pollUntilTerminal(taskId, options) {
|
|
48
|
+
const intervalMs = options.intervalMs ?? N11_DEFAULT_TASK_POLL_INTERVAL_MS;
|
|
49
|
+
const maxIntervalMs = options.maxIntervalMs ?? N11_DEFAULT_TASK_POLL_MAX_INTERVAL_MS;
|
|
50
|
+
const timeoutMs = options.timeoutMs ?? N11_DEFAULT_TASK_POLL_TIMEOUT_MS;
|
|
51
|
+
const backoffMultiplier = options.backoffMultiplier ?? N11_DEFAULT_TASK_BACKOFF_MULTIPLIER;
|
|
52
|
+
const deadline = Date.now() + timeoutMs;
|
|
53
|
+
const started = Date.now();
|
|
54
|
+
let attempt = 0;
|
|
55
|
+
let currentInterval = intervalMs;
|
|
56
|
+
let unknownStreak = 0;
|
|
57
|
+
let lastStatus;
|
|
58
|
+
while (Date.now() < deadline) {
|
|
59
|
+
if (options.signal?.aborted) {
|
|
60
|
+
return toFailure(new TaskPollAbortedError(taskId));
|
|
61
|
+
}
|
|
62
|
+
attempt += 1;
|
|
63
|
+
const elapsedMs = Date.now() - started;
|
|
64
|
+
this.getHooks().onTaskPoll?.({
|
|
65
|
+
taskId,
|
|
66
|
+
kind: options.kind,
|
|
67
|
+
attempt,
|
|
68
|
+
status: lastStatus,
|
|
69
|
+
elapsedMs,
|
|
70
|
+
});
|
|
71
|
+
n11Log(this.debug, "debug", "task poll", buildHttpObservabilityMeta({
|
|
72
|
+
correlationId: taskId,
|
|
73
|
+
resource: "ms",
|
|
74
|
+
method: "POST",
|
|
75
|
+
path: "/product/task-details/page-query",
|
|
76
|
+
host: "api.n11.com",
|
|
77
|
+
transport: "rest",
|
|
78
|
+
taskPollAttempt: attempt,
|
|
79
|
+
operation: options.kind,
|
|
80
|
+
}));
|
|
81
|
+
const result = await this.fetchStatus(taskId, options.kind, options.signal);
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
if (result.code === "NOT_IMPLEMENTED") {
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
if (result.retryable) {
|
|
87
|
+
await sleep(currentInterval, options.signal);
|
|
88
|
+
currentInterval = Math.min(currentInterval * backoffMultiplier, maxIntervalMs);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
lastStatus = result.data.status;
|
|
94
|
+
const verdict = classifyTaskStatus(result.data.status);
|
|
95
|
+
if (verdict === "terminal") {
|
|
96
|
+
return { ok: true, data: toPollResult(result.data) };
|
|
97
|
+
}
|
|
98
|
+
if (verdict === "unknown") {
|
|
99
|
+
unknownStreak += 1;
|
|
100
|
+
if (unknownStreak >= MAX_UNKNOWN_STATUS) {
|
|
101
|
+
return toFailure(new UnknownTaskStatusError(result.data.status, taskId));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
unknownStreak = 0;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
await sleep(currentInterval, options.signal);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
112
|
+
return toFailure(new TaskPollAbortedError(taskId));
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
currentInterval = Math.min(currentInterval * backoffMultiplier, maxIntervalMs);
|
|
117
|
+
}
|
|
118
|
+
return toFailure(new TaskTimeoutError(taskId, timeoutMs, lastStatus));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function sleep(ms, signal) {
|
|
122
|
+
if (signal?.aborted) {
|
|
123
|
+
throw new DOMException("Aborted", "AbortError");
|
|
124
|
+
}
|
|
125
|
+
await new Promise((resolve, reject) => {
|
|
126
|
+
const timer = setTimeout(resolve, ms);
|
|
127
|
+
const onAbort = () => {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
130
|
+
};
|
|
131
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
132
|
+
});
|
|
133
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type TaskKind = "productCreate" | "productUpdate" | "priceStockUpdate" | "productDelete";
|
|
2
|
+
export type TaskPollOptions = {
|
|
3
|
+
kind: TaskKind;
|
|
4
|
+
intervalMs?: number;
|
|
5
|
+
maxIntervalMs?: number;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
backoffMultiplier?: number;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
};
|
|
10
|
+
export type TaskPollResult = {
|
|
11
|
+
status: string;
|
|
12
|
+
failedItemCount?: number;
|
|
13
|
+
errors?: unknown[];
|
|
14
|
+
rows?: unknown[];
|
|
15
|
+
raw: Record<string, unknown>;
|
|
16
|
+
};
|
|
17
|
+
export type TaskStatusPayload = {
|
|
18
|
+
status: string;
|
|
19
|
+
failedItemCount?: number;
|
|
20
|
+
errors?: unknown[];
|
|
21
|
+
rows?: unknown[];
|
|
22
|
+
raw: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
export type TaskStatusFetcher = (taskId: string, kind: TaskKind, signal?: AbortSignal) => Promise<import("../types.js").N11Result<TaskStatusPayload>>;
|
|
25
|
+
/** G4: yalnizca task-details/page-query — ayri GetTaskStatus yok. */
|
|
26
|
+
export declare const TASK_DETAILS_PATH = "/product/task-details/page-query";
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tasks/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAChB,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,eAAe,CAAC;AAEpB,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,EACd,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC,OAAO,aAAa,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAEjE,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,qCAAqC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"taskId": 1095,
|
|
3
|
+
"status": "PROCESSED",
|
|
4
|
+
"skus": {
|
|
5
|
+
"content": [
|
|
6
|
+
{
|
|
7
|
+
"itemCode": "OK-1",
|
|
8
|
+
"status": "SUCCESS",
|
|
9
|
+
"reasons": ["Başarıyla tamamlandı."]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"itemCode": "FAIL-1",
|
|
13
|
+
"status": "FAIL",
|
|
14
|
+
"reasons": ["Stok kodu bulunamadi."]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** tests/fixtures/category-leaf.json ile ayni icerik — yaprak kategori attribute mock. */
|
|
2
|
+
export declare const CATEGORY_LEAF_FIXTURE: {
|
|
3
|
+
id: number;
|
|
4
|
+
name: string;
|
|
5
|
+
categoryAttributes: {
|
|
6
|
+
attributeId: number;
|
|
7
|
+
attributeName: string;
|
|
8
|
+
required: boolean;
|
|
9
|
+
}[];
|
|
10
|
+
};
|
|
11
|
+
/** tests/fixtures/order-package.json ile ayni icerik — shipmentPackages list mock. */
|
|
12
|
+
export declare const ORDER_PACKAGE_FIXTURE: {
|
|
13
|
+
content: {
|
|
14
|
+
id: string;
|
|
15
|
+
orderNumber: string;
|
|
16
|
+
shipmentPackageStatus: string;
|
|
17
|
+
}[];
|
|
18
|
+
totalPages: number;
|
|
19
|
+
page: number;
|
|
20
|
+
size: number;
|
|
21
|
+
};
|
|
22
|
+
/** tests/fixtures/task-partial-fail.json ile ayni icerik — task-details partial fail mock. */
|
|
23
|
+
export declare const TASK_PARTIAL_FAIL_FIXTURE: {
|
|
24
|
+
taskId: number;
|
|
25
|
+
status: string;
|
|
26
|
+
skus: {
|
|
27
|
+
content: {
|
|
28
|
+
itemCode: string;
|
|
29
|
+
status: string;
|
|
30
|
+
reasons: string[];
|
|
31
|
+
}[];
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/** tests/fixtures/return-claim-list.xml ile ayni icerik — ClaimReturnList SOAP mock. */
|
|
35
|
+
export declare const RETURN_CLAIM_LIST_FIXTURE = "<?xml version=\"1.0\"?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <SOAP-ENV:Body>\n <ClaimReturnListResponse>\n <result><status>success</status></result>\n <claimReturnList>\n <claimReturn>\n <claimReturnId>25391658</claimReturnId>\n <status>CANCELLED</status>\n <orderNumber>123456789000</orderNumber>\n <productName>Deneme Urun</productName>\n <quantity>1</quantity>\n </claimReturn>\n <claimReturn>\n <claimReturnId>25383473</claimReturnId>\n <status>REQUESTED</status>\n <orderNumber>123456789001</orderNumber>\n </claimReturn>\n </claimReturnList>\n <pagingData>\n <currentPage>0</currentPage>\n <pageSize>20</pageSize>\n <totalCount>2</totalCount>\n <pageCount>1</pageCount>\n </pagingData>\n </ClaimReturnListResponse>\n </SOAP-ENV:Body>\n</SOAP-ENV:Envelope>";
|
|
36
|
+
/** tests/fixtures/invoice-send-link.xml ile ayni icerik — SaveLinkSellerInvoice SOAP mock. */
|
|
37
|
+
export declare const INVOICE_SEND_LINK_FIXTURE = "<?xml version=\"1.0\"?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <SOAP-ENV:Body>\n <SaveLinkSellerInvoiceResponse>\n <result><status>success</status></result>\n <url>https://example.com/fatura.pdf</url>\n <orderNumber>202665912318</orderNumber>\n </SaveLinkSellerInvoiceResponse>\n </SOAP-ENV:Body>\n</SOAP-ENV:Envelope>";
|
|
38
|
+
/** tests/fixtures/qna-open-question.xml ile ayni icerik — GetProductQuestionList SOAP mock. */
|
|
39
|
+
export declare const QNA_OPEN_QUESTION_FIXTURE = "<?xml version=\"1.0\"?>\n<env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <env:Body>\n <GetProductQuestionListResponse>\n <result><status>success</status></result>\n <productQuestions>\n <productQuestion>\n <id>114646171</id>\n <productId>730196627</productId>\n <productTitle>Cicek Desenli Fincan Takimi</productTitle>\n <question>Urun kargoya nasil verilmektedir?</question>\n <questionSubject>Kargo hakkinda</questionSubject>\n </productQuestion>\n </productQuestions>\n <pagingData>\n <currentPage>0</currentPage>\n <pageSize>10</pageSize>\n <totalCount>1</totalCount>\n <pageCount>1</pageCount>\n </pagingData>\n </GetProductQuestionListResponse>\n </env:Body>\n</env:Envelope>";
|
|
40
|
+
/** Operasyon adi → varsayilan SOAP response fixture haritasi. */
|
|
41
|
+
export declare const SOAP_RESPONSE_FIXTURES: {
|
|
42
|
+
readonly ClaimReturnList: "<?xml version=\"1.0\"?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <SOAP-ENV:Body>\n <ClaimReturnListResponse>\n <result><status>success</status></result>\n <claimReturnList>\n <claimReturn>\n <claimReturnId>25391658</claimReturnId>\n <status>CANCELLED</status>\n <orderNumber>123456789000</orderNumber>\n <productName>Deneme Urun</productName>\n <quantity>1</quantity>\n </claimReturn>\n <claimReturn>\n <claimReturnId>25383473</claimReturnId>\n <status>REQUESTED</status>\n <orderNumber>123456789001</orderNumber>\n </claimReturn>\n </claimReturnList>\n <pagingData>\n <currentPage>0</currentPage>\n <pageSize>20</pageSize>\n <totalCount>2</totalCount>\n <pageCount>1</pageCount>\n </pagingData>\n </ClaimReturnListResponse>\n </SOAP-ENV:Body>\n</SOAP-ENV:Envelope>";
|
|
43
|
+
readonly SaveLinkSellerInvoice: "<?xml version=\"1.0\"?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <SOAP-ENV:Body>\n <SaveLinkSellerInvoiceResponse>\n <result><status>success</status></result>\n <url>https://example.com/fatura.pdf</url>\n <orderNumber>202665912318</orderNumber>\n </SaveLinkSellerInvoiceResponse>\n </SOAP-ENV:Body>\n</SOAP-ENV:Envelope>";
|
|
44
|
+
readonly GetProductQuestionList: "<?xml version=\"1.0\"?>\n<env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n <env:Body>\n <GetProductQuestionListResponse>\n <result><status>success</status></result>\n <productQuestions>\n <productQuestion>\n <id>114646171</id>\n <productId>730196627</productId>\n <productTitle>Cicek Desenli Fincan Takimi</productTitle>\n <question>Urun kargoya nasil verilmektedir?</question>\n <questionSubject>Kargo hakkinda</questionSubject>\n </productQuestion>\n </productQuestions>\n <pagingData>\n <currentPage>0</currentPage>\n <pageSize>10</pageSize>\n <totalCount>1</totalCount>\n <pageCount>1</pageCount>\n </pagingData>\n </GetProductQuestionListResponse>\n </env:Body>\n</env:Envelope>";
|
|
45
|
+
};
|
|
46
|
+
/** REST path → varsayilan JSON response fixture haritasi. */
|
|
47
|
+
export declare const REST_RESPONSE_FIXTURES: {
|
|
48
|
+
readonly "/categories": {
|
|
49
|
+
readonly categories: readonly [{
|
|
50
|
+
readonly id: 1;
|
|
51
|
+
readonly name: "Elektronik";
|
|
52
|
+
}];
|
|
53
|
+
};
|
|
54
|
+
readonly "/rest/delivery/v1/shipmentPackages": {
|
|
55
|
+
content: {
|
|
56
|
+
id: string;
|
|
57
|
+
orderNumber: string;
|
|
58
|
+
shipmentPackageStatus: string;
|
|
59
|
+
}[];
|
|
60
|
+
totalPages: number;
|
|
61
|
+
page: number;
|
|
62
|
+
size: number;
|
|
63
|
+
};
|
|
64
|
+
readonly "/ms/product/task-details/page-query": {
|
|
65
|
+
taskId: number;
|
|
66
|
+
status: string;
|
|
67
|
+
skus: {
|
|
68
|
+
content: {
|
|
69
|
+
itemCode: string;
|
|
70
|
+
status: string;
|
|
71
|
+
reasons: string[];
|
|
72
|
+
}[];
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/testing/fixtures.ts"],"names":[],"mappings":"AAIA,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB;;;;;;;;CAAmB,CAAC;AAEtD,sFAAsF;AACtF,eAAO,MAAM,qBAAqB;;;;;;;;;CAAmB,CAAC;AAEtD,8FAA8F;AAC9F,eAAO,MAAM,yBAAyB;;;;;;;;;;CAAsB,CAAC;AAE7D,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,88BA2BjB,CAAC;AAEtB,8FAA8F;AAC9F,eAAO,MAAM,yBAAyB,uYASjB,CAAC;AAEtB,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,+zBAsBtB,CAAC;AAEjB,iEAAiE;AACjE,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX,6DAA6D;AAC7D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIzB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import categoryLeafJson from "./fixtures/category-leaf.json" with { type: "json" };
|
|
2
|
+
import orderPackageJson from "./fixtures/order-package.json" with { type: "json" };
|
|
3
|
+
import taskPartialFailJson from "./fixtures/task-partial-fail.json" with { type: "json" };
|
|
4
|
+
/** tests/fixtures/category-leaf.json ile ayni icerik — yaprak kategori attribute mock. */
|
|
5
|
+
export const CATEGORY_LEAF_FIXTURE = categoryLeafJson;
|
|
6
|
+
/** tests/fixtures/order-package.json ile ayni icerik — shipmentPackages list mock. */
|
|
7
|
+
export const ORDER_PACKAGE_FIXTURE = orderPackageJson;
|
|
8
|
+
/** tests/fixtures/task-partial-fail.json ile ayni icerik — task-details partial fail mock. */
|
|
9
|
+
export const TASK_PARTIAL_FAIL_FIXTURE = taskPartialFailJson;
|
|
10
|
+
/** tests/fixtures/return-claim-list.xml ile ayni icerik — ClaimReturnList SOAP mock. */
|
|
11
|
+
export const RETURN_CLAIM_LIST_FIXTURE = `<?xml version="1.0"?>
|
|
12
|
+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
13
|
+
<SOAP-ENV:Body>
|
|
14
|
+
<ClaimReturnListResponse>
|
|
15
|
+
<result><status>success</status></result>
|
|
16
|
+
<claimReturnList>
|
|
17
|
+
<claimReturn>
|
|
18
|
+
<claimReturnId>25391658</claimReturnId>
|
|
19
|
+
<status>CANCELLED</status>
|
|
20
|
+
<orderNumber>123456789000</orderNumber>
|
|
21
|
+
<productName>Deneme Urun</productName>
|
|
22
|
+
<quantity>1</quantity>
|
|
23
|
+
</claimReturn>
|
|
24
|
+
<claimReturn>
|
|
25
|
+
<claimReturnId>25383473</claimReturnId>
|
|
26
|
+
<status>REQUESTED</status>
|
|
27
|
+
<orderNumber>123456789001</orderNumber>
|
|
28
|
+
</claimReturn>
|
|
29
|
+
</claimReturnList>
|
|
30
|
+
<pagingData>
|
|
31
|
+
<currentPage>0</currentPage>
|
|
32
|
+
<pageSize>20</pageSize>
|
|
33
|
+
<totalCount>2</totalCount>
|
|
34
|
+
<pageCount>1</pageCount>
|
|
35
|
+
</pagingData>
|
|
36
|
+
</ClaimReturnListResponse>
|
|
37
|
+
</SOAP-ENV:Body>
|
|
38
|
+
</SOAP-ENV:Envelope>`;
|
|
39
|
+
/** tests/fixtures/invoice-send-link.xml ile ayni icerik — SaveLinkSellerInvoice SOAP mock. */
|
|
40
|
+
export const INVOICE_SEND_LINK_FIXTURE = `<?xml version="1.0"?>
|
|
41
|
+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
42
|
+
<SOAP-ENV:Body>
|
|
43
|
+
<SaveLinkSellerInvoiceResponse>
|
|
44
|
+
<result><status>success</status></result>
|
|
45
|
+
<url>https://example.com/fatura.pdf</url>
|
|
46
|
+
<orderNumber>202665912318</orderNumber>
|
|
47
|
+
</SaveLinkSellerInvoiceResponse>
|
|
48
|
+
</SOAP-ENV:Body>
|
|
49
|
+
</SOAP-ENV:Envelope>`;
|
|
50
|
+
/** tests/fixtures/qna-open-question.xml ile ayni icerik — GetProductQuestionList SOAP mock. */
|
|
51
|
+
export const QNA_OPEN_QUESTION_FIXTURE = `<?xml version="1.0"?>
|
|
52
|
+
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
|
53
|
+
<env:Body>
|
|
54
|
+
<GetProductQuestionListResponse>
|
|
55
|
+
<result><status>success</status></result>
|
|
56
|
+
<productQuestions>
|
|
57
|
+
<productQuestion>
|
|
58
|
+
<id>114646171</id>
|
|
59
|
+
<productId>730196627</productId>
|
|
60
|
+
<productTitle>Cicek Desenli Fincan Takimi</productTitle>
|
|
61
|
+
<question>Urun kargoya nasil verilmektedir?</question>
|
|
62
|
+
<questionSubject>Kargo hakkinda</questionSubject>
|
|
63
|
+
</productQuestion>
|
|
64
|
+
</productQuestions>
|
|
65
|
+
<pagingData>
|
|
66
|
+
<currentPage>0</currentPage>
|
|
67
|
+
<pageSize>10</pageSize>
|
|
68
|
+
<totalCount>1</totalCount>
|
|
69
|
+
<pageCount>1</pageCount>
|
|
70
|
+
</pagingData>
|
|
71
|
+
</GetProductQuestionListResponse>
|
|
72
|
+
</env:Body>
|
|
73
|
+
</env:Envelope>`;
|
|
74
|
+
/** Operasyon adi → varsayilan SOAP response fixture haritasi. */
|
|
75
|
+
export const SOAP_RESPONSE_FIXTURES = {
|
|
76
|
+
ClaimReturnList: RETURN_CLAIM_LIST_FIXTURE,
|
|
77
|
+
SaveLinkSellerInvoice: INVOICE_SEND_LINK_FIXTURE,
|
|
78
|
+
GetProductQuestionList: QNA_OPEN_QUESTION_FIXTURE,
|
|
79
|
+
};
|
|
80
|
+
/** REST path → varsayilan JSON response fixture haritasi. */
|
|
81
|
+
export const REST_RESPONSE_FIXTURES = {
|
|
82
|
+
"/categories": { categories: [{ id: 1, name: "Elektronik" }] },
|
|
83
|
+
"/rest/delivery/v1/shipmentPackages": ORDER_PACKAGE_FIXTURE,
|
|
84
|
+
"/ms/product/task-details/page-query": TASK_PARTIAL_FAIL_FIXTURE,
|
|
85
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { N11Config } from "../types.js";
|
|
2
|
+
export declare class ProdMutationGuardError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class ForbiddenHostError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class QaSmokeGuardError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/** G5 — sandbox yok; prod mutasyonlar varsayilan kapali. */
|
|
12
|
+
export declare function assertProdMutationGuard(config: N11Config, operation: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* G5 — tek host api.n11.com; prod disi veya override host engellenir.
|
|
15
|
+
*/
|
|
16
|
+
export declare function forbiddenHostGuard(_config: N11Config, url: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* qa-smoke oncesi — read-only smoke; tek prod host guard.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assertSafeForQaSmoke(config: N11Config): void;
|
|
21
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/testing/guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAID,4DAA4D;AAC5D,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAOlF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAcxE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAQ5D"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { N11_API_HOST } from "../constants/hosts.js";
|
|
2
|
+
export class ProdMutationGuardError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "ProdMutationGuardError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class ForbiddenHostError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "ForbiddenHostError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class QaSmokeGuardError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "QaSmokeGuardError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const NON_PROD_HOST_FRAGMENTS = ["sandbox", "-sit", "localhost", "127.0.0.1", "staging."];
|
|
21
|
+
/** G5 — sandbox yok; prod mutasyonlar varsayilan kapali. */
|
|
22
|
+
export function assertProdMutationGuard(config, operation) {
|
|
23
|
+
if (config.allowProdMutations) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
throw new ProdMutationGuardError(`Prod mutasyon guard: ${operation} — N11_ALLOW_PROD_MUTATIONS=1 veya allowProdMutations: true`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* G5 — tek host api.n11.com; prod disi veya override host engellenir.
|
|
30
|
+
*/
|
|
31
|
+
export function forbiddenHostGuard(_config, url) {
|
|
32
|
+
const normalized = url.trim().toLowerCase();
|
|
33
|
+
if (!normalized.startsWith(N11_API_HOST.toLowerCase())) {
|
|
34
|
+
throw new ForbiddenHostError(`Yalnizca ${N11_API_HOST} host kullanilabilir — reddedilen: ${url}`);
|
|
35
|
+
}
|
|
36
|
+
for (const fragment of NON_PROD_HOST_FRAGMENTS) {
|
|
37
|
+
if (normalized.includes(fragment)) {
|
|
38
|
+
throw new ForbiddenHostError(`Prod disi host fragment yasak: ${fragment}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* qa-smoke oncesi — read-only smoke; tek prod host guard.
|
|
44
|
+
*/
|
|
45
|
+
export function assertSafeForQaSmoke(config) {
|
|
46
|
+
if (config.allowProdMutations) {
|
|
47
|
+
throw new QaSmokeGuardError("qa-smoke read-only — allowProdMutations kapali olmali (N11_ALLOW_PROD_MUTATIONS unset).");
|
|
48
|
+
}
|
|
49
|
+
forbiddenHostGuard(config, N11_API_HOST);
|
|
50
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const N11_TESTING_SUBPATH = "@hubcommerce/n11/testing";
|
|
2
|
+
export { assertProdMutationGuard, assertSafeForQaSmoke, forbiddenHostGuard, ForbiddenHostError, ProdMutationGuardError, QaSmokeGuardError, } from "./guard.js";
|
|
3
|
+
export { CATEGORY_LEAF_FIXTURE, INVOICE_SEND_LINK_FIXTURE, ORDER_PACKAGE_FIXTURE, QNA_OPEN_QUESTION_FIXTURE, REST_RESPONSE_FIXTURES, RETURN_CLAIM_LIST_FIXTURE, SOAP_RESPONSE_FIXTURES, TASK_PARTIAL_FAIL_FIXTURE, } from "./fixtures.js";
|
|
4
|
+
export { createMockHttpClient, createMockN11Client, createMockSoapClient, } from "./mock-client.js";
|
|
5
|
+
export type { CreateMockHttpClientOptions, CreateMockN11ClientOptions, CreateMockSoapClientOptions, MockHttpCallRecord, MockHttpClient, MockHttpResponse, MockN11ClientBundle, MockSoapCallRecord, MockSoapClient, MockSoapResponse, } from "./mock-client.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,6BAA6B,CAAC;AAE9D,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,2BAA2B,EAC3B,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const N11_TESTING_SUBPATH = "@hubcommerce/n11/testing";
|
|
2
|
+
export { assertProdMutationGuard, assertSafeForQaSmoke, forbiddenHostGuard, ForbiddenHostError, ProdMutationGuardError, QaSmokeGuardError, } from "./guard.js";
|
|
3
|
+
export { CATEGORY_LEAF_FIXTURE, INVOICE_SEND_LINK_FIXTURE, ORDER_PACKAGE_FIXTURE, QNA_OPEN_QUESTION_FIXTURE, REST_RESPONSE_FIXTURES, RETURN_CLAIM_LIST_FIXTURE, SOAP_RESPONSE_FIXTURES, TASK_PARTIAL_FAIL_FIXTURE, } from "./fixtures.js";
|
|
4
|
+
export { createMockHttpClient, createMockN11Client, createMockSoapClient, } from "./mock-client.js";
|