Services refactor
This commit is contained in:
+115
-184
@@ -1,199 +1,130 @@
|
||||
import type { Printful } from "./util/types";
|
||||
import { FetchError, type DeepPartial } from "./util/misc";
|
||||
|
||||
export class PrintfulService {
|
||||
static Products = class {
|
||||
static Variants = class {
|
||||
static async get(
|
||||
variantId: number | string,
|
||||
): Promise<Printful.Products.SyncVariant | undefined> {
|
||||
const res = await fetch(
|
||||
`${env().API_URL}/store/variants/${variantId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS },
|
||||
},
|
||||
);
|
||||
type ClientOptions = {
|
||||
token: string;
|
||||
storeId: string;
|
||||
};
|
||||
|
||||
if (res.status === 404 || res.status === 400) {
|
||||
return;
|
||||
}
|
||||
type Credentials = {
|
||||
apiUrl: string;
|
||||
authHeaders: Record<string, string>;
|
||||
};
|
||||
|
||||
if (!res.ok) {
|
||||
throw new FetchError("Failed to get Printful variant", res);
|
||||
}
|
||||
class VariantsClient {
|
||||
constructor(private creds: Credentials) { }
|
||||
|
||||
const payload =
|
||||
(await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.SyncVariant>;
|
||||
return payload.result;
|
||||
}
|
||||
};
|
||||
|
||||
static async getAll(
|
||||
offset: number = 0,
|
||||
): Promise<Printful.Products.SyncProduct[]> {
|
||||
const allSyncProducts: Printful.Products.SyncProduct[] = [];
|
||||
|
||||
while (true) {
|
||||
const res = await fetch(
|
||||
`${env().API_URL}/store/products?offset=${offset}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS },
|
||||
},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new FetchError(
|
||||
"Failed to get all Printful products",
|
||||
res,
|
||||
);
|
||||
}
|
||||
|
||||
const payload =
|
||||
(await res.json()) as Printful.Products.MetaDataMulti<Printful.Products.SyncProduct>;
|
||||
|
||||
allSyncProducts.push(...payload.result);
|
||||
offset = allSyncProducts.length;
|
||||
|
||||
if (allSyncProducts.length >= payload.paging.total) break;
|
||||
}
|
||||
|
||||
return allSyncProducts;
|
||||
}
|
||||
|
||||
static async get(
|
||||
productId: number | string,
|
||||
): Promise<Printful.Products.Product | undefined> {
|
||||
const res = await fetch(
|
||||
`${env().API_URL}/store/products/${productId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS },
|
||||
},
|
||||
);
|
||||
|
||||
if (res.status === 404 || res.status === 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
throw new FetchError("Failed to get Printful product", res);
|
||||
}
|
||||
|
||||
const payload =
|
||||
(await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.Product>;
|
||||
return payload.result;
|
||||
}
|
||||
|
||||
static async update(
|
||||
printfulProductId: number,
|
||||
printfulProduct: DeepPartial<Printful.Products.Product>,
|
||||
) {
|
||||
const res = await fetch(
|
||||
`${env().API_URL}/store/products/${printfulProductId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...env().AUTH_HEADERS,
|
||||
},
|
||||
body: JSON.stringify(printfulProduct),
|
||||
},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new FetchError("Failed to update Printful product", res);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static Orders = class {
|
||||
static async create(printfulOrder: Printful.Orders.Order) {
|
||||
const res = await fetch(`${env().API_URL}/orders`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...env().AUTH_HEADERS,
|
||||
},
|
||||
body: JSON.stringify(printfulOrder),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new FetchError("Failed to create printful order", res);
|
||||
}
|
||||
}
|
||||
|
||||
static async getAll(
|
||||
offset: number = 0,
|
||||
): Promise<Printful.Orders.Order[]> {
|
||||
const allPrintfulOrders: Printful.Orders.Order[] = [];
|
||||
|
||||
while (true) {
|
||||
const res = await fetch(
|
||||
`${env().API_URL}/orders?offset=${offset}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS },
|
||||
},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new FetchError(
|
||||
"Failed to get all Printful orders",
|
||||
res,
|
||||
);
|
||||
}
|
||||
|
||||
const payload =
|
||||
(await res.json()) as Printful.Products.MetaDataMulti<Printful.Orders.Order>;
|
||||
|
||||
allPrintfulOrders.push(...payload.result);
|
||||
offset = allPrintfulOrders.length;
|
||||
|
||||
if (allPrintfulOrders.length >= payload.paging.total) break;
|
||||
}
|
||||
|
||||
return allPrintfulOrders;
|
||||
}
|
||||
};
|
||||
|
||||
static Util = class {
|
||||
static getVariantMainImage(
|
||||
syncVariant: Printful.Products.SyncVariant,
|
||||
): string {
|
||||
const previewFile = syncVariant.files.find(
|
||||
(f) => f.type === "preview",
|
||||
);
|
||||
return previewFile?.preview_url ?? syncVariant.product.image;
|
||||
}
|
||||
};
|
||||
async get(variantId: number | string): Promise<Printful.Products.SyncVariant | undefined> {
|
||||
const res = await fetch(`${this.creds.apiUrl}/store/variants/${variantId}`, {
|
||||
method: "GET",
|
||||
headers: this.creds.authHeaders,
|
||||
});
|
||||
if (res.status === 404 || res.status === 400) return;
|
||||
if (!res.ok) throw new FetchError("Failed to get Printful variant", res);
|
||||
const payload = (await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.SyncVariant>;
|
||||
return payload.result;
|
||||
}
|
||||
}
|
||||
|
||||
export const env = () => {
|
||||
if (typeof Bun === "undefined") {
|
||||
throw new Error(
|
||||
"Must be in a server context. Make sure to run using --bun.",
|
||||
);
|
||||
class ProductsClient {
|
||||
readonly Variants: VariantsClient;
|
||||
|
||||
constructor(private creds: Credentials) {
|
||||
this.Variants = new VariantsClient(creds);
|
||||
}
|
||||
|
||||
const vars = {
|
||||
AUTH_TOKEN: Bun.env.PRINTFUL_AUTH,
|
||||
STORE_ID: Bun.env.PRINTFUL_STORE_ID,
|
||||
} as Record<string, string>;
|
||||
|
||||
for (const varKey in vars) {
|
||||
if (!vars[varKey]) {
|
||||
console.log(`Missing ${varKey} environment variable`);
|
||||
async getAll(offset = 0): Promise<Printful.Products.SyncProduct[]> {
|
||||
const allSyncProducts: Printful.Products.SyncProduct[] = [];
|
||||
while (true) {
|
||||
const res = await fetch(`${this.creds.apiUrl}/store/products?offset=${offset}`, {
|
||||
method: "GET",
|
||||
headers: this.creds.authHeaders,
|
||||
});
|
||||
if (!res.ok) throw new FetchError("Failed to get all Printful products", res);
|
||||
const payload = (await res.json()) as Printful.Products.MetaDataMulti<Printful.Products.SyncProduct>;
|
||||
allSyncProducts.push(...payload.result);
|
||||
offset = allSyncProducts.length;
|
||||
if (allSyncProducts.length >= payload.paging.total) break;
|
||||
}
|
||||
return allSyncProducts;
|
||||
}
|
||||
|
||||
return {
|
||||
...vars,
|
||||
API_URL: "https://api.printful.com",
|
||||
AUTH_HEADERS: {
|
||||
Authorization: `Bearer ${vars.AUTH_TOKEN}`,
|
||||
"X-PF-Store-Id": `${vars.STORE_ID}`,
|
||||
async get(productId: number | string): Promise<Printful.Products.Product | undefined> {
|
||||
const res = await fetch(`${this.creds.apiUrl}/store/products/${productId}`, {
|
||||
method: "GET",
|
||||
headers: this.creds.authHeaders,
|
||||
});
|
||||
if (res.status === 404 || res.status === 400) return;
|
||||
if (!res.ok) throw new FetchError("Failed to get Printful product", res);
|
||||
const payload = (await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.Product>;
|
||||
return payload.result;
|
||||
}
|
||||
|
||||
async update(printfulProductId: number, printfulProduct: DeepPartial<Printful.Products.Product>) {
|
||||
const res = await fetch(`${this.creds.apiUrl}/store/products/${printfulProductId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...this.creds.authHeaders,
|
||||
},
|
||||
body: JSON.stringify(printfulProduct),
|
||||
});
|
||||
if (!res.ok) throw new FetchError("Failed to update Printful product", res);
|
||||
}
|
||||
}
|
||||
|
||||
class OrdersClient {
|
||||
constructor(private creds: Credentials) { }
|
||||
|
||||
async create(printfulOrder: Printful.Orders.Order) {
|
||||
const res = await fetch(`${this.creds.apiUrl}/orders`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...this.creds.authHeaders,
|
||||
},
|
||||
body: JSON.stringify(printfulOrder),
|
||||
});
|
||||
if (!res.ok) throw new FetchError("Failed to create printful order", res);
|
||||
}
|
||||
|
||||
async getAll(offset = 0): Promise<Printful.Orders.Order[]> {
|
||||
const allPrintfulOrders: Printful.Orders.Order[] = [];
|
||||
while (true) {
|
||||
const res = await fetch(`${this.creds.apiUrl}/orders?offset=${offset}`, {
|
||||
method: "GET",
|
||||
headers: this.creds.authHeaders,
|
||||
});
|
||||
if (!res.ok) throw new FetchError("Failed to get all Printful orders", res);
|
||||
const payload = (await res.json()) as Printful.Products.MetaDataMulti<Printful.Orders.Order>;
|
||||
allPrintfulOrders.push(...payload.result);
|
||||
offset = allPrintfulOrders.length;
|
||||
if (allPrintfulOrders.length >= payload.paging.total) break;
|
||||
}
|
||||
return allPrintfulOrders;
|
||||
}
|
||||
}
|
||||
|
||||
export class PrintfulClient {
|
||||
readonly Products: ProductsClient;
|
||||
readonly Orders: OrdersClient;
|
||||
|
||||
constructor(options: ClientOptions) {
|
||||
const creds: Credentials = {
|
||||
apiUrl: "https://api.printful.com",
|
||||
authHeaders: {
|
||||
Authorization: `Bearer ${options.token}`,
|
||||
"X-PF-Store-Id": options.storeId,
|
||||
},
|
||||
};
|
||||
this.Products = new ProductsClient(creds);
|
||||
this.Orders = new OrdersClient(creds);
|
||||
}
|
||||
|
||||
static Util = {
|
||||
getVariantMainImage(syncVariant: Printful.Products.SyncVariant): string {
|
||||
const previewFile = syncVariant.files.find((f) => f.type === "preview");
|
||||
return previewFile?.preview_url ?? syncVariant.product.image;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user