Skip to content

Commit 4246eae

Browse files
authored
feat: add Barcode and PackingList API client (#6)
feat: add Barcode and PackingList API client
2 parents bd61ed1 + fe2179f commit 4246eae

5 files changed

Lines changed: 267 additions & 0 deletions

File tree

src/barcoding.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { BARCODING_BASE_PATH, PACKING_LIST_BASE_PATH } from "./constants";
2+
import type {
3+
components as BarcodingComponents,
4+
operations as BarcodingOperations,
5+
} from "./generated/barcoding-v3";
6+
import type {
7+
components as PackingListComponents,
8+
operations as PackingListOperations,
9+
} from "./generated/packing-list-v1";
10+
import {
11+
type DigiKeyHttpClient,
12+
positiveInteger,
13+
type QueryParameters,
14+
splitRequestOptions,
15+
} from "./http";
16+
import type { DigiKeyRequestOptions, JsonResponse, OperationQuery, ResponseContent } from "./types";
17+
18+
export type BarcodingSchemas = BarcodingComponents["schemas"];
19+
export type BarcodingOperationsMap = BarcodingOperations;
20+
export type PackingListSchemas = PackingListComponents["schemas"];
21+
export type PackingListOperationsMap = PackingListOperations;
22+
23+
export type ProductBarcodeResponse = JsonResponse<BarcodingOperations["ProductBarcode"]>;
24+
export type Product2DBarcodeResponse = JsonResponse<BarcodingOperations["Product2DBarcode"]>;
25+
export type PackListBarcodeResponse = JsonResponse<BarcodingOperations["PackListBarcode"]>;
26+
export type PackList2DBarcodeResponse = JsonResponse<BarcodingOperations["PackList2DBarcode"]>;
27+
export type GetPackingListResponse = ResponseContent<PackingListOperations["GetPackingList"]>;
28+
export type GetPackingListBySalesOrderIdResponse = ResponseContent<
29+
PackingListOperations["GetPackingListBySalesOrderId"]
30+
>;
31+
export type GetPackingListByPoNumberResponse = ResponseContent<
32+
PackingListOperations["GetPackingListByPoNumber"]
33+
>;
34+
35+
export type BarcodeOptions = DigiKeyRequestOptions &
36+
OperationQuery<BarcodingOperations["ProductBarcode"]>;
37+
export type PackingListOptions = DigiKeyRequestOptions &
38+
OperationQuery<PackingListOperations["GetPackingList"]>;
39+
40+
export class BarcodingClient {
41+
constructor(private readonly http: DigiKeyHttpClient) {}
42+
43+
productBarcode(barcode: string, options?: BarcodeOptions): Promise<ProductBarcodeResponse> {
44+
const [requestOptions, query] = splitRequestOptions(options);
45+
46+
return this.http.request<ProductBarcodeResponse>({
47+
method: "GET",
48+
basePath: BARCODING_BASE_PATH,
49+
path: `/ProductBarcodes/${encodeURIComponent(barcode)}`,
50+
query: query as QueryParameters,
51+
requestOptions,
52+
includeAccountId: false,
53+
requiredOAuthFlow: "authorizationCode",
54+
});
55+
}
56+
57+
product2DBarcode(barcode: string, options?: BarcodeOptions): Promise<Product2DBarcodeResponse> {
58+
const [requestOptions, query] = splitRequestOptions(options);
59+
60+
return this.http.request<Product2DBarcodeResponse>({
61+
method: "GET",
62+
basePath: BARCODING_BASE_PATH,
63+
path: `/Product2DBarcodes/${encodeURIComponent(barcode)}`,
64+
query: query as QueryParameters,
65+
requestOptions,
66+
includeAccountId: false,
67+
requiredOAuthFlow: "authorizationCode",
68+
});
69+
}
70+
71+
packListBarcode(barcode: string, options?: BarcodeOptions): Promise<PackListBarcodeResponse> {
72+
const [requestOptions, query] = splitRequestOptions(options);
73+
74+
return this.http.request<PackListBarcodeResponse>({
75+
method: "GET",
76+
basePath: BARCODING_BASE_PATH,
77+
path: `/PackListBarcodes/${encodeURIComponent(barcode)}`,
78+
query: query as QueryParameters,
79+
requestOptions,
80+
includeAccountId: false,
81+
requiredOAuthFlow: "authorizationCode",
82+
});
83+
}
84+
85+
packList2DBarcode(barcode: string, options?: BarcodeOptions): Promise<PackList2DBarcodeResponse> {
86+
const [requestOptions, query] = splitRequestOptions(options);
87+
88+
return this.http.request<PackList2DBarcodeResponse>({
89+
method: "GET",
90+
basePath: BARCODING_BASE_PATH,
91+
path: `/PackList2DBarcodes/${encodeURIComponent(barcode)}`,
92+
query: query as QueryParameters,
93+
requestOptions,
94+
includeAccountId: false,
95+
requiredOAuthFlow: "authorizationCode",
96+
});
97+
}
98+
99+
getPackingList(invoiceId: number, options?: PackingListOptions): Promise<GetPackingListResponse> {
100+
const [requestOptions, query] = splitRequestOptions(options);
101+
102+
return this.http.request<GetPackingListResponse>({
103+
method: "GET",
104+
basePath: PACKING_LIST_BASE_PATH,
105+
path: `/v1/invoice/${positiveInteger(invoiceId, "invoiceId")}`,
106+
query: query as QueryParameters,
107+
requestOptions,
108+
includeAccountId: false,
109+
requiredOAuthFlow: "authorizationCode",
110+
});
111+
}
112+
113+
getPackingListBySalesOrderId(
114+
salesOrderId: number,
115+
options?: PackingListOptions,
116+
): Promise<GetPackingListBySalesOrderIdResponse> {
117+
const [requestOptions, query] = splitRequestOptions(options);
118+
119+
return this.http.request<GetPackingListBySalesOrderIdResponse>({
120+
method: "GET",
121+
basePath: PACKING_LIST_BASE_PATH,
122+
path: `/v1/salesorderid/${positiveInteger(salesOrderId, "salesOrderId")}`,
123+
query: query as QueryParameters,
124+
requestOptions,
125+
includeAccountId: false,
126+
requiredOAuthFlow: "authorizationCode",
127+
});
128+
}
129+
130+
getPackingListByPoNumber(
131+
purchaseOrderNumber: string,
132+
options?: PackingListOptions,
133+
): Promise<GetPackingListByPoNumberResponse> {
134+
const [requestOptions, query] = splitRequestOptions(options);
135+
136+
return this.http.request<GetPackingListByPoNumberResponse>({
137+
method: "GET",
138+
basePath: PACKING_LIST_BASE_PATH,
139+
path: `/v1/purchaseordernumber/${encodeURIComponent(purchaseOrderNumber)}`,
140+
query: query as QueryParameters,
141+
requestOptions,
142+
includeAccountId: false,
143+
requiredOAuthFlow: "authorizationCode",
144+
});
145+
}
146+
}

src/client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DigiKeyAuthClient, type DigiKeyAuthClientOptions } from "./auth";
2+
import { BarcodingClient } from "./barcoding";
23
import type { DigiKeyEnvironment } from "./constants";
34
import { DigiKeyHttpClient } from "./http";
45
import { MyListsClient } from "./mylists";
@@ -35,6 +36,7 @@ export interface DigiKeyClientOptions {
3536

3637
export class DigiKeyClient {
3738
readonly auth?: DigiKeyAuthClient;
39+
readonly barcoding: BarcodingClient;
3840
readonly myLists: MyListsClient;
3941
readonly ordering: OrderingClient;
4042
readonly productSearch: ProductSearchClient;
@@ -63,6 +65,7 @@ export class DigiKeyClient {
6365
onResponse: options.onResponse,
6466
});
6567

68+
this.barcoding = new BarcodingClient(http);
6669
this.myLists = new MyListsClient(http);
6770
this.ordering = new OrderingClient(http);
6871
this.productSearch = new ProductSearchClient(http);

src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export const PRODUCT_SEARCH_BASE_PATH = "/products/v4";
99
export const PRODUCT_CHANGE_NOTIFICATIONS_BASE_PATH = "/ChangeNotifications/v3";
1010
export const MYLISTS_BASE_PATH = "/mylists/v1";
1111
export const ORDERING_BASE_PATH = "/Ordering/v3";
12+
export const BARCODING_BASE_PATH = "/Barcoding/v3";
13+
export const PACKING_LIST_BASE_PATH = "/packinglist";
1214

1315
export function apiBaseUrlForEnvironment(environment: DigiKeyEnvironment): string {
1416
return DIGIKEY_API_BASE_URLS[environment];

src/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ export type {
88
RefreshTokenOptions,
99
} from "./auth";
1010
export { DigiKeyAuthClient, DigiKeyRefreshTokenProvider } from "./auth";
11+
export type {
12+
BarcodeOptions,
13+
BarcodingOperationsMap,
14+
BarcodingSchemas,
15+
GetPackingListByPoNumberResponse,
16+
GetPackingListBySalesOrderIdResponse,
17+
GetPackingListResponse,
18+
PackingListOperationsMap,
19+
PackingListOptions,
20+
PackingListSchemas,
21+
PackList2DBarcodeResponse,
22+
PackListBarcodeResponse,
23+
Product2DBarcodeResponse,
24+
ProductBarcodeResponse,
25+
} from "./barcoding";
26+
export { BarcodingClient } from "./barcoding";
1127
export type { DigiKeyClientOptions } from "./client";
1228
export { DigiKeyClient } from "./client";
1329
export type { DigiKeyEnvironment } from "./constants";

tests/barcoding.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { describe, expect, it, vi } from "bun:test";
2+
import type { FetchLike } from "../src";
3+
import { DigiKeyClient } from "../src";
4+
5+
describe("BarcodingClient", () => {
6+
it("maps Barcode and PackingList endpoints to documented paths", async () => {
7+
const fetch = vi.fn<FetchLike>(async () => jsonResponse({ ok: true }));
8+
const client = authCodeClient(fetch);
9+
const calls: Array<[string, () => Promise<unknown>, string, Record<string, string>]> = [
10+
[
11+
"productBarcode",
12+
() =>
13+
client.barcoding.productBarcode("abc 123", { includes: "DigiKeyPartNumber,Quantity" }),
14+
"/Barcoding/v3/ProductBarcodes/abc%20123",
15+
{ includes: "DigiKeyPartNumber,Quantity" },
16+
],
17+
[
18+
"product2DBarcode",
19+
() => client.barcoding.product2DBarcode("qr 123"),
20+
"/Barcoding/v3/Product2DBarcodes/qr%20123",
21+
{},
22+
],
23+
[
24+
"packListBarcode",
25+
() => client.barcoding.packListBarcode("pack 1"),
26+
"/Barcoding/v3/PackListBarcodes/pack%201",
27+
{},
28+
],
29+
[
30+
"packList2DBarcode",
31+
() => client.barcoding.packList2DBarcode("pack 2"),
32+
"/Barcoding/v3/PackList2DBarcodes/pack%202",
33+
{},
34+
],
35+
[
36+
"getPackingList",
37+
() => client.barcoding.getPackingList(123, { includePdf: true }),
38+
"/packinglist/v1/invoice/123",
39+
{ includePdf: "true" },
40+
],
41+
[
42+
"getPackingListBySalesOrderId",
43+
() => client.barcoding.getPackingListBySalesOrderId(456),
44+
"/packinglist/v1/salesorderid/456",
45+
{},
46+
],
47+
[
48+
"getPackingListByPoNumber",
49+
() => client.barcoding.getPackingListByPoNumber("PO 1"),
50+
"/packinglist/v1/purchaseordernumber/PO%201",
51+
{},
52+
],
53+
];
54+
55+
for (const [name, call, pathname, query] of calls) {
56+
fetch.mockClear();
57+
await call();
58+
59+
const [input] = fetch.mock.calls[0]!;
60+
const url = new URL(String(input));
61+
expect(url.pathname, name).toBe(pathname);
62+
for (const [key, value] of Object.entries(query)) {
63+
expect(url.searchParams.get(key), name).toBe(value);
64+
}
65+
}
66+
});
67+
68+
it("rejects Barcoding when the OAuth flow is not declared", async () => {
69+
const fetch = vi.fn<FetchLike>(async () => jsonResponse({}));
70+
const client = new DigiKeyClient({
71+
clientId: "client-id",
72+
accessToken: "access-token",
73+
environment: "sandbox",
74+
fetch,
75+
});
76+
77+
await expect(client.barcoding.productBarcode("barcode")).rejects.toMatchObject({
78+
name: "DigiKeyConfigurationError",
79+
});
80+
expect(fetch).not.toHaveBeenCalled();
81+
});
82+
});
83+
84+
function authCodeClient(fetch: FetchLike): DigiKeyClient {
85+
return new DigiKeyClient({
86+
clientId: "client-id",
87+
accessToken: "access-token",
88+
oauthFlow: "authorizationCode",
89+
environment: "sandbox",
90+
fetch,
91+
});
92+
}
93+
94+
function jsonResponse(body: unknown): Response {
95+
return new Response(JSON.stringify(body), {
96+
headers: {
97+
"Content-Type": "application/json",
98+
},
99+
});
100+
}

0 commit comments

Comments
 (0)