More service naming consistency cleanup
This commit is contained in:
+23
-22
@@ -22,9 +22,10 @@ import { StandardsParametersSchema } from "@blade-and-brawn/calculator";
|
||||
|
||||
// SERVICES
|
||||
// -----------
|
||||
|
||||
const calculatorService = new CalculatorService();
|
||||
const commerceService = new CommerceService();
|
||||
const s = {
|
||||
Calculator: new CalculatorService(),
|
||||
Commerce: new CommerceService()
|
||||
};
|
||||
|
||||
// ELYSIA
|
||||
// -----------
|
||||
@@ -103,7 +104,7 @@ export const app = new Elysia()
|
||||
(app) => app
|
||||
// Non-authenticated
|
||||
.post("/calculate", async ({ body }) => {
|
||||
return { levels: calculatorService.calculate(body.player, body.activityPerformances) };
|
||||
return { levels: s.Calculator.calculate(body.player, body.activityPerformances) };
|
||||
}, {
|
||||
body: t.Object({
|
||||
player: PlayerSchema,
|
||||
@@ -117,23 +118,23 @@ export const app = new Elysia()
|
||||
return { sessionId: token.sessionId.toString() };
|
||||
})
|
||||
.put("/config/:id", async ({ params: { id }, body: { datasetId, parameters } }) => {
|
||||
await calculatorService.standards.setConfig(id, datasetId, parameters);
|
||||
await s.Calculator.Standards.Config.update(id, datasetId, parameters);
|
||||
}, {
|
||||
params: t.Object({ id: t.String() }),
|
||||
body: t.Object({ datasetId: t.String(), parameters: StandardsParametersSchema })
|
||||
})
|
||||
.post("/config", async ({ body: { datasetId, parameters } }) => {
|
||||
await calculatorService.standards.createConfig(datasetId, parameters);
|
||||
await s.Calculator.Standards.Config.create(datasetId, parameters);
|
||||
}, {
|
||||
body: t.Object({ datasetId: t.String(), parameters: StandardsParametersSchema })
|
||||
})
|
||||
.post("/config/:id/select", async ({ params: { id } }) => {
|
||||
await calculatorService.standards.selectConfig(id);
|
||||
.post("/config/:id/switch", async ({ params: { id } }) => {
|
||||
await s.Calculator.Standards.Config.switch(id);
|
||||
}, {
|
||||
params: t.Object({ id: t.String() })
|
||||
})
|
||||
.get("/config", async () => {
|
||||
return await calculatorService.standards.getSelectedConfig();
|
||||
return await s.Calculator.Standards.Config.get();
|
||||
})
|
||||
)
|
||||
|
||||
@@ -149,17 +150,17 @@ export const app = new Elysia()
|
||||
app
|
||||
// Sync status
|
||||
.get("/", async ({ sessionId }) => {
|
||||
const latestSync = await commerceService.getLatestSync(sessionId);
|
||||
const latestSync = await s.Commerce.Sync.getLatest(sessionId);
|
||||
if (!latestSync) throw new NotFoundError("No sync found");
|
||||
return {
|
||||
startDate: latestSync.started_at,
|
||||
status: commerceService.deriveSyncStatus(latestSync),
|
||||
status: s.Commerce.Sync.deriveStatus(latestSync),
|
||||
syncingPrintfulProductIds: latestSync.syncing_printful_product_ids
|
||||
}
|
||||
})
|
||||
// Run sync
|
||||
.post("/:printfulProductId?", async ({ params: { printfulProductId }, sessionId }) => {
|
||||
await commerceService.sync({
|
||||
await s.Commerce.Sync.start({
|
||||
session: { id: sessionId, name: "Portal" },
|
||||
filter: {
|
||||
printfulProductIds: printfulProductId ? [printfulProductId] : null
|
||||
@@ -168,13 +169,13 @@ export const app = new Elysia()
|
||||
}, { params: t.Object({ printfulProductId: t.Optional(t.Numeric()) }) }),
|
||||
)
|
||||
.get("/:printfulProductId", async ({ params: { printfulProductId } }) => {
|
||||
const printfulProduct = await commerceService.printful.Products.get(printfulProductId);
|
||||
const printfulProduct = await s.Commerce.Printful.Products.get(printfulProductId);
|
||||
if (!printfulProduct) throw new NotFoundError("Missing printful product");
|
||||
|
||||
const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0];
|
||||
if (!webflowProductId) throw new NotFoundError("Missing webflow product ID");
|
||||
|
||||
const webflowProduct = await commerceService.webflow.Products.get(webflowProductId);
|
||||
const webflowProduct = await s.Commerce.Webflow.Products.get(webflowProductId);
|
||||
|
||||
return { printfulProduct, webflowProduct };
|
||||
}, {
|
||||
@@ -192,7 +193,7 @@ export const app = new Elysia()
|
||||
const printfulProduct = payload.data.sync_product;
|
||||
log.info({ productId: printfulProduct.id }, "printful webhook: product updated");
|
||||
|
||||
await commerceService.sync({
|
||||
await s.Commerce.Sync.start({
|
||||
session: { id: randomUUIDv7(), name: "Printful" },
|
||||
filter: {
|
||||
printfulProductIds: [printfulProduct.id]
|
||||
@@ -206,7 +207,7 @@ export const app = new Elysia()
|
||||
log.info({ externalId: payload.data.sync_product.external_id, webflowProductId }, "printful webhook: product deleted");
|
||||
if (!webflowProductId) throw new NotFoundError("Missing webflow product ID");
|
||||
|
||||
await commerceService.webflow.Products.remove(webflowProductId);
|
||||
await s.Commerce.Webflow.Products.remove(webflowProductId);
|
||||
break;
|
||||
}
|
||||
case Printful.Webhook.Event.PackageShipped: {
|
||||
@@ -214,12 +215,12 @@ export const app = new Elysia()
|
||||
const shipInfo = payload.data.shipment;
|
||||
log.info({ webflowOrderId, carrier: shipInfo.carrier, tracking: shipInfo.tracking_number }, "printful webhook: package shipped");
|
||||
|
||||
await commerceService.webflow.Orders.update(webflowOrderId, {
|
||||
await s.Commerce.Webflow.Orders.update(webflowOrderId, {
|
||||
shippingTrackingURL: shipInfo.tracking_url,
|
||||
shippingTracking: shipInfo.tracking_number,
|
||||
shippingProvider: shipInfo.carrier,
|
||||
});
|
||||
await commerceService.webflow.Orders.fulfill(webflowOrderId, {
|
||||
await s.Commerce.Webflow.Orders.fulfill(webflowOrderId, {
|
||||
sendOrderFulfilledEmail: true,
|
||||
});
|
||||
break;
|
||||
@@ -229,7 +230,7 @@ export const app = new Elysia()
|
||||
}
|
||||
})
|
||||
.post("/webhook/webflow", async ({ request, body }) => {
|
||||
if (!commerceService.webflow.Util.verifyWebflowSignature(request, body))
|
||||
if (!s.Commerce.Webflow.Util.verifyWebflowSignature(request, body))
|
||||
throw status(400, "Invalid signature");
|
||||
|
||||
const payload = body as Webflow.Webhook.EventPayload;
|
||||
@@ -239,7 +240,7 @@ export const app = new Elysia()
|
||||
const webflowOrder = payload.payload;
|
||||
log.info({ orderId: webflowOrder.orderId }, "webflow webhook: order created");
|
||||
|
||||
await commerceService.printful.Orders.create({
|
||||
await s.Commerce.Printful.Orders.create({
|
||||
external_id: webflowOrder.orderId,
|
||||
// TODO: derive from webflow
|
||||
shipping: "STANDARD",
|
||||
@@ -272,8 +273,8 @@ app.listen(3000, async () => {
|
||||
log.info({ port: 3000 }, "server started")
|
||||
|
||||
try {
|
||||
const frontQueuedSync = await commerceService.queue.front();
|
||||
if (frontQueuedSync) await commerceService.sync(frontQueuedSync);
|
||||
const frontQueuedSync = await s.Commerce.Sync.Queue.front();
|
||||
if (frontQueuedSync) await s.Commerce.Sync.start(frontQueuedSync);
|
||||
}
|
||||
catch (err) {
|
||||
log.error({ err }, "failed to resume product sync queue")
|
||||
|
||||
@@ -5,45 +5,46 @@ import { Value } from "@sinclair/typebox/value";
|
||||
import { log } from "../util";
|
||||
|
||||
export class CalculatorService {
|
||||
private calculator: LevelCalculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATASET));
|
||||
standards: StandardsService = new StandardsService(this.calculator);
|
||||
private Calculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATASET));
|
||||
Standards: StandardsService = new StandardsService(this.Calculator);
|
||||
|
||||
calculate(player: Player, activityPerformances: ActivityPerformance[]) {
|
||||
this.standards.refreshConfig({ onlyIfStale: true }).catch((err) => log.error({ err }, "failed to refresh standards config"));
|
||||
return this.calculator.calculate(player, activityPerformances);
|
||||
this.Standards.Config.refresh({ onlyIfStale: true }).catch((err) => log.error({ err }, "failed to refresh standards config"));
|
||||
return this.Calculator.calculate(player, activityPerformances);
|
||||
}
|
||||
}
|
||||
|
||||
class StandardsService {
|
||||
private static readonly CONFIG_REFRESH_INTERVAL_MS = 30_000;
|
||||
Config: StandardsConfigService;
|
||||
|
||||
constructor(private Calculator: LevelCalculator) {
|
||||
this.Config = new StandardsConfigService(this.Calculator);
|
||||
}
|
||||
}
|
||||
|
||||
class StandardsConfigService {
|
||||
private static readonly REFRESH_INTERVAL_MS = 30_000;
|
||||
private cachedParametersStringified: string | null = null;
|
||||
private cachedDatasetId: string | null = null;
|
||||
private lastRefreshedAt = 0;
|
||||
|
||||
constructor(private calculator: LevelCalculator) { }
|
||||
constructor(private Calculator: LevelCalculator) { }
|
||||
|
||||
async getSelectedConfig(opt: { skipCache?: boolean } = {}): Promise<StandardsParameters> {
|
||||
if (!opt.skipCache) return this.calculator.standards.params;
|
||||
|
||||
await this.refreshConfig();
|
||||
return this.calculator.standards.params;
|
||||
async create(datasetId: string, parameters: StandardsParameters) {
|
||||
await db.insertInto("standards_configs")
|
||||
.values({ parameters, dataset_id: datasetId })
|
||||
.execute();
|
||||
}
|
||||
|
||||
async selectConfig(id: string) {
|
||||
await db.transaction().execute(async (trx) => {
|
||||
await trx.updateTable("standards_configs")
|
||||
.set({ is_selected: false })
|
||||
.where("is_selected", "=", true)
|
||||
.execute();
|
||||
const result = await trx.updateTable("standards_configs")
|
||||
.set({ is_selected: true })
|
||||
.where("id", "=", id)
|
||||
.execute();
|
||||
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
||||
});
|
||||
/** Retrieves the currently selected configuration */
|
||||
async get(opt: { skipCache?: boolean } = {}): Promise<StandardsParameters> {
|
||||
if (!opt.skipCache) return this.Calculator.Standards.params;
|
||||
|
||||
await this.refresh();
|
||||
return this.Calculator.Standards.params;
|
||||
}
|
||||
|
||||
async setConfig(id: string, datasetId: string, parameters: StandardsParameters) {
|
||||
async update(id: string, datasetId: string, parameters: StandardsParameters) {
|
||||
const result = await db.updateTable("standards_configs")
|
||||
.set({ parameters, dataset_id: datasetId })
|
||||
.where("id", "=", id)
|
||||
@@ -51,15 +52,9 @@ class StandardsService {
|
||||
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
||||
}
|
||||
|
||||
async createConfig(datasetId: string, parameters: StandardsParameters) {
|
||||
await db.insertInto("standards_configs")
|
||||
.values({ parameters, dataset_id: datasetId })
|
||||
.execute();
|
||||
}
|
||||
|
||||
async refreshConfig(opt: { onlyIfStale?: boolean } = {}) {
|
||||
async refresh(opt: { onlyIfStale?: boolean } = {}) {
|
||||
if (opt.onlyIfStale)
|
||||
if (Date.now() - this.lastRefreshedAt < StandardsService.CONFIG_REFRESH_INTERVAL_MS) return;
|
||||
if (Date.now() - this.lastRefreshedAt < StandardsConfigService.REFRESH_INTERVAL_MS) return;
|
||||
|
||||
this.lastRefreshedAt = Date.now();
|
||||
|
||||
@@ -79,8 +74,22 @@ class StandardsService {
|
||||
const parametersStringified = JSON.stringify(config.parameters);
|
||||
if (parametersStringified === this.cachedParametersStringified && config.datasetId === this.cachedDatasetId) return;
|
||||
|
||||
this.calculator.standards = new Standards(config.dataset, config.parameters);
|
||||
this.Calculator.Standards = new Standards(config.dataset, config.parameters);
|
||||
this.cachedParametersStringified = parametersStringified;
|
||||
this.cachedDatasetId = config.datasetId;
|
||||
}
|
||||
|
||||
async switch(id: string) {
|
||||
await db.transaction().execute(async (trx) => {
|
||||
await trx.updateTable("standards_configs")
|
||||
.set({ is_selected: false })
|
||||
.where("is_selected", "=", true)
|
||||
.execute();
|
||||
const result = await trx.updateTable("standards_configs")
|
||||
.set({ is_selected: true })
|
||||
.where("id", "=", id)
|
||||
.execute();
|
||||
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,32 +19,33 @@ type QueuedSync = {
|
||||
class AlreadyClaimedError extends Error { }
|
||||
|
||||
export class CommerceService {
|
||||
public readonly printful: PrintfulClient;
|
||||
public readonly webflow: WebflowClient;
|
||||
private readonly productSyncer: ProductSyncer;
|
||||
readonly queue: SyncQueue;
|
||||
|
||||
constructor() {
|
||||
this.printful = new PrintfulClient({
|
||||
public readonly Printful = new PrintfulClient({
|
||||
token: env.PRINTFUL_AUTH,
|
||||
storeId: env.PRINTFUL_STORE_ID,
|
||||
});
|
||||
this.webflow = new WebflowClient({
|
||||
public readonly Webflow = new WebflowClient({
|
||||
siteId: env.WEBFLOW_SITE_ID,
|
||||
collectionsId: env.WEBFLOW_COLLECTION_ID,
|
||||
token: env.WEBFLOW_AUTH,
|
||||
webhookSecret: env.WEBFLOW_WEBHOOK_SECRET,
|
||||
});
|
||||
this.productSyncer = new ProductSyncer(this.printful, this.webflow, log);
|
||||
this.queue = new SyncQueue();
|
||||
readonly Sync = new SyncService(this.Printful, this.Webflow);
|
||||
}
|
||||
|
||||
class SyncService {
|
||||
readonly Queue = new SyncQueueService();
|
||||
private readonly ProductSyncer: ProductSyncer;
|
||||
|
||||
constructor(private readonly Printful: PrintfulClient, private readonly Webflow: WebflowClient) {
|
||||
this.ProductSyncer = new ProductSyncer(this.Printful, this.Webflow, log);
|
||||
}
|
||||
|
||||
async sync(queuedSync: QueuedSync) {
|
||||
async start(queuedSync: QueuedSync) {
|
||||
let id = queuedSync.id ?? null;
|
||||
let freedSlot = false;
|
||||
try {
|
||||
log.info({ session: queuedSync.session }, "starting sync run");
|
||||
await this.productSyncer.sync({
|
||||
await this.ProductSyncer.run({
|
||||
filter: queuedSync.filter,
|
||||
onStart: async (startDate) => {
|
||||
const values: Insertable<ProductSyncs> = {
|
||||
@@ -95,7 +96,7 @@ export class CommerceService {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeSync = await this.getActiveSync();
|
||||
const activeSync = await this.getActive();
|
||||
|
||||
const activeSyncConflict = activeSync && err instanceof DatabaseError && err.code === "23505";
|
||||
if (activeSyncConflict) {
|
||||
@@ -106,11 +107,11 @@ export class CommerceService {
|
||||
.set({ syncing_printful_product_ids: [], ended_at: new Date(), has_failed: true })
|
||||
.where("id", "=", activeSync.id)
|
||||
.execute();
|
||||
await this.sync(queuedSync);
|
||||
await this.start(queuedSync);
|
||||
}
|
||||
else {
|
||||
log.info("sync already active, enqueuing next sync");
|
||||
await this.queue.enqueue(queuedSync);
|
||||
await this.Queue.enqueue(queuedSync);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -127,8 +128,8 @@ export class CommerceService {
|
||||
finally {
|
||||
if (freedSlot) {
|
||||
try {
|
||||
const frontQueuedSync = await this.queue.front();
|
||||
if (frontQueuedSync) await this.sync(frontQueuedSync);
|
||||
const frontQueuedSync = await this.Queue.front();
|
||||
if (frontQueuedSync) await this.start(frontQueuedSync);
|
||||
}
|
||||
catch (err) {
|
||||
log.error({ err }, "failed to process next queued sync")
|
||||
@@ -137,7 +138,7 @@ export class CommerceService {
|
||||
}
|
||||
}
|
||||
|
||||
async getActiveSync(sessionId?: string) {
|
||||
async getActive(sessionId?: string) {
|
||||
return await db.selectFrom("product_syncs")
|
||||
.selectAll()
|
||||
.$if(sessionId !== undefined, (qb) =>
|
||||
@@ -149,7 +150,7 @@ export class CommerceService {
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async getLatestSync(sessionId?: string) {
|
||||
async getLatest(sessionId?: string) {
|
||||
return await db.selectFrom("product_syncs")
|
||||
.selectAll()
|
||||
.$if(sessionId !== undefined, (qb) =>
|
||||
@@ -160,14 +161,14 @@ export class CommerceService {
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
deriveSyncStatus(sync: Selectable<ProductSyncs>): SyncStatus {
|
||||
deriveStatus(sync: Selectable<ProductSyncs>): SyncStatus {
|
||||
if (!sync.started_at) return "queued";
|
||||
if (!sync.ended_at) return "active";
|
||||
return sync.has_failed ? "failed" : "succeeded";
|
||||
}
|
||||
}
|
||||
|
||||
class SyncQueue {
|
||||
class SyncQueueService {
|
||||
async enqueue(queuedSync: QueuedSync) {
|
||||
const values: Insertable<ProductSyncs> = {
|
||||
session_id: queuedSync.session.id,
|
||||
|
||||
@@ -73,10 +73,10 @@ const metricPriority = (m: NumberMetric) => {
|
||||
export const DEFAULT_STANDARDS_DATASET = defaultStandardsDataset as StandardsDataset;
|
||||
|
||||
export class LevelCalculator {
|
||||
standards: Standards;
|
||||
Standards: Standards;
|
||||
|
||||
public constructor(standards: Standards) {
|
||||
this.standards = standards;
|
||||
this.Standards = standards;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
@@ -138,7 +138,7 @@ export class LevelCalculator {
|
||||
(Object.values(Attribute) as Attribute[]).forEach((attribute) => {
|
||||
const attrActivityPerformances = activityPerformances.filter(
|
||||
(p) =>
|
||||
this.standards.byActivity(p.activity).getMetadata()
|
||||
this.Standards.byActivity(p.activity).getMetadata()
|
||||
.attribute === attribute,
|
||||
);
|
||||
attrLevels[attribute] = this.calculateAttributeLevel(
|
||||
@@ -160,7 +160,7 @@ export class LevelCalculator {
|
||||
if (
|
||||
activityPerformances.some(
|
||||
(p) =>
|
||||
this.standards.byActivity(p.activity).getMetadata()
|
||||
this.Standards.byActivity(p.activity).getMetadata()
|
||||
.attribute !== attribute,
|
||||
)
|
||||
) {
|
||||
@@ -168,7 +168,7 @@ export class LevelCalculator {
|
||||
}
|
||||
|
||||
// must perform all of an attributes activities
|
||||
for (const activity of this.standards.getAttributeActivities(
|
||||
for (const activity of this.Standards.getAttributeActivities(
|
||||
attribute,
|
||||
)) {
|
||||
const filteredActivites = activityPerformances.map(
|
||||
@@ -183,7 +183,7 @@ export class LevelCalculator {
|
||||
}
|
||||
|
||||
const activityLevels = activityPerformances.map((p) => {
|
||||
const interpolatedStandard = this.standards
|
||||
const interpolatedStandard = this.Standards
|
||||
.byActivity(p.activity)
|
||||
.byMetrics(player.metrics)
|
||||
.getOneInterpolated();
|
||||
|
||||
@@ -30,14 +30,14 @@ export class ProductSyncer {
|
||||
private log: pino.Logger;
|
||||
|
||||
constructor(
|
||||
private printful: PrintfulClient,
|
||||
private webflow: WebflowClient,
|
||||
private Printful: PrintfulClient,
|
||||
private Webflow: WebflowClient,
|
||||
log?: pino.Logger,
|
||||
) {
|
||||
this.log = (log ?? pino({ level: "silent" })).child({ component: "ProductSyncer" });
|
||||
}
|
||||
|
||||
async sync(opt: ProductSyncerOptions = {}) {
|
||||
async run(opt: ProductSyncerOptions = {}) {
|
||||
const startDate = new Date();
|
||||
await opt.onStart?.(startDate);
|
||||
|
||||
@@ -47,10 +47,10 @@ export class ProductSyncer {
|
||||
this.log.info("sync started (all)");
|
||||
|
||||
this.log.debug("retrieving webflow products");
|
||||
const allWebflowProducts = await this.webflow.Products.list({ forceAll: true });
|
||||
const allWebflowProducts = await this.Webflow.Products.list({ forceAll: true });
|
||||
|
||||
this.log.debug("retrieving printful products");
|
||||
const allPrintfulProducts = await this.printful.Products.list({ forceAll: true });
|
||||
const allPrintfulProducts = await this.Printful.Products.list({ forceAll: true });
|
||||
|
||||
this.log.info({ printfulProductCount: allPrintfulProducts.length, filter: opt.filter ?? "N/A" }, "generating meta printful products");
|
||||
const metaPrintfulProducts = await this.generateMetaPrintfulProducts(allWebflowProducts, allPrintfulProducts, opt);
|
||||
@@ -120,7 +120,7 @@ export class ProductSyncer {
|
||||
this.log.warn({ webflowSku: newWebflowSkus[0] }, "could not find existing webflow SKU that matches");
|
||||
}
|
||||
|
||||
await this.webflow.Products.update(webflowProduct.product.id, {
|
||||
await this.Webflow.Products.update(webflowProduct.product.id, {
|
||||
product: { fieldData: webflowProductFieldData },
|
||||
sku: newWebflowSkus[0],
|
||||
});
|
||||
@@ -133,13 +133,13 @@ export class ProductSyncer {
|
||||
newWebflowSku.id,
|
||||
);
|
||||
if (existingWebflowSku) {
|
||||
await this.webflow.Products.Skus.update(
|
||||
await this.Webflow.Products.Skus.update(
|
||||
metaPrintfulProduct.webflowProductId,
|
||||
existingWebflowSku.id,
|
||||
newWebflowSku,
|
||||
);
|
||||
} else {
|
||||
await this.webflow.Products.Skus.create(
|
||||
await this.Webflow.Products.Skus.create(
|
||||
metaPrintfulProduct.webflowProductId,
|
||||
[newWebflowSku],
|
||||
);
|
||||
@@ -148,18 +148,18 @@ export class ProductSyncer {
|
||||
} else {
|
||||
this.log.info("existing webflow product not found, creating it");
|
||||
|
||||
const webflowProductId = await this.webflow.Products.create({
|
||||
const webflowProductId = await this.Webflow.Products.create({
|
||||
product: { fieldData: webflowProductFieldData },
|
||||
sku: newWebflowSkus[0],
|
||||
});
|
||||
metaPrintfulProduct.webflowProductId = webflowProductId;
|
||||
|
||||
if (newWebflowSkus.length > 1)
|
||||
await this.webflow.Products.Skus.create(webflowProductId, newWebflowSkus.slice(1));
|
||||
await this.Webflow.Products.Skus.create(webflowProductId, newWebflowSkus.slice(1));
|
||||
}
|
||||
|
||||
// Re-fetch after SKU mutations so newly created SKUs have their real IDs.
|
||||
const freshWebflowProduct = await this.webflow.Products.get(metaPrintfulProduct.webflowProductId);
|
||||
const freshWebflowProduct = await this.Webflow.Products.get(metaPrintfulProduct.webflowProductId);
|
||||
if (!freshWebflowProduct) throw new Error("webflow product missing");
|
||||
|
||||
// Update printful external IDs
|
||||
@@ -186,7 +186,7 @@ export class ProductSyncer {
|
||||
|
||||
await sleep(10000);
|
||||
this.log.info({ printfulProduct: printfulProduct.sync_product.id, externalId: expectedExternalId }, "updating printful product's external ID");
|
||||
await this.printful.Products.update(printfulProduct.sync_product.id, {
|
||||
await this.Printful.Products.update(printfulProduct.sync_product.id, {
|
||||
sync_product: {
|
||||
id: printfulProduct.sync_product.id,
|
||||
external_id: expectedExternalId,
|
||||
@@ -286,7 +286,7 @@ export class ProductSyncer {
|
||||
metaPrintfulProduct.webflowProductId = webflowProductId;
|
||||
}
|
||||
|
||||
const fullPrintfulProduct = await this.printful.Products.get(printfulProduct.id);
|
||||
const fullPrintfulProduct = await this.Printful.Products.get(printfulProduct.id);
|
||||
if (!fullPrintfulProduct) throw new Error(`Printful product ${printfulProduct.id} not found`);
|
||||
|
||||
const isDuplicate = allPrintfulProducts.some((p) => p.id !== printfulProduct.id && p.name === printfulProduct.name);
|
||||
|
||||
Reference in New Issue
Block a user