152 lines
4.6 KiB
TypeScript
152 lines
4.6 KiB
TypeScript
![]() |
import { BuilderIoProviderConfig } from "@/modules/configuration";
|
||
|
import { WebhookProductVariantFragment } from "../../../../generated/graphql";
|
||
|
import { createLogger } from "@saleor/apps-shared";
|
||
|
|
||
|
// https://www.builder.io/c/docs/write-api
|
||
|
export class BuilderIoClient {
|
||
|
private endpoint: string;
|
||
|
private logger = createLogger({ name: "BuilderIoClient" });
|
||
|
|
||
|
constructor(private config: BuilderIoProviderConfig.FullShape) {
|
||
|
this.endpoint = `https://builder.io/api/v1/write/${config.modelName}`;
|
||
|
}
|
||
|
|
||
|
private mapVariantToFields(variant: WebhookProductVariantFragment) {
|
||
|
const { channels, productId, productName, productSlug, variantId, variantName } =
|
||
|
this.config.productVariantFieldsMapping;
|
||
|
|
||
|
return {
|
||
|
[channels]: variant.channelListings,
|
||
|
[productId]: variant.product.id,
|
||
|
[productName]: variant.product.name,
|
||
|
[productSlug]: variant.product.slug,
|
||
|
[variantId]: variant.id,
|
||
|
[variantName]: variant.name,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
async uploadProductVariant(variant: WebhookProductVariantFragment) {
|
||
|
this.logger.debug({ variantId: variant.id }, "uploadProductVariant called");
|
||
|
|
||
|
try {
|
||
|
const response = await fetch(this.endpoint, {
|
||
|
method: "POST",
|
||
|
headers: {
|
||
|
"Content-Type": "application/json",
|
||
|
Authorization: `Bearer ${this.config.privateApiKey}`,
|
||
|
},
|
||
|
body: JSON.stringify({
|
||
|
data: this.mapVariantToFields(variant),
|
||
|
published: "published",
|
||
|
}),
|
||
|
});
|
||
|
} catch (err) {
|
||
|
this.logger.error(err, "Failed to upload product variant");
|
||
|
|
||
|
throw err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async updateProductVariantCall(
|
||
|
builderIoEntryId: string,
|
||
|
variant: WebhookProductVariantFragment
|
||
|
) {
|
||
|
try {
|
||
|
const response = await fetch(this.endpoint + `/${builderIoEntryId}`, {
|
||
|
method: "PUT",
|
||
|
headers: {
|
||
|
"Content-Type": "application/json",
|
||
|
Authorization: `Bearer ${this.config.privateApiKey}`,
|
||
|
},
|
||
|
body: JSON.stringify({
|
||
|
data: this.mapVariantToFields(variant),
|
||
|
published: "published",
|
||
|
}),
|
||
|
});
|
||
|
} catch (err) {
|
||
|
this.logger.error(err, "Failed to upload product variant");
|
||
|
|
||
|
throw err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async updateProductVariant(variant: WebhookProductVariantFragment) {
|
||
|
const entriesToUpdate = await this.fetchBuilderIoEntryIds(variant.id);
|
||
|
|
||
|
this.logger.debug(
|
||
|
{
|
||
|
entriesToUpdate,
|
||
|
},
|
||
|
"Trying to update variants in builder.io with following IDs"
|
||
|
);
|
||
|
|
||
|
return Promise.all(
|
||
|
entriesToUpdate.map((id) => {
|
||
|
return this.updateProductVariantCall(id, variant);
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
|
||
|
async upsertProductVariant(variant: WebhookProductVariantFragment) {
|
||
|
const entriesToUpdate = await this.fetchBuilderIoEntryIds(variant.id);
|
||
|
|
||
|
if (entriesToUpdate.length === 0) {
|
||
|
this.logger.debug("Didnt find any entries to update, will upload new variant");
|
||
|
|
||
|
return this.uploadProductVariant(variant);
|
||
|
} else {
|
||
|
this.logger.debug({ entriesToUpdate }, "Found entries in builder.io, will update them");
|
||
|
|
||
|
return Promise.all(
|
||
|
entriesToUpdate.map((id) => {
|
||
|
return this.updateProductVariantCall(id, variant);
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async deleteProductVariant(variantId: string) {
|
||
|
const idsToDelete = await this.fetchBuilderIoEntryIds(variantId);
|
||
|
|
||
|
this.logger.debug({ ids: idsToDelete }, "Will try to delete items in Builder.io");
|
||
|
|
||
|
return Promise.all(
|
||
|
idsToDelete.map((id) =>
|
||
|
fetch(this.endpoint + `/${id}`, {
|
||
|
method: "DELETE",
|
||
|
headers: {
|
||
|
"Content-Type": "application/json",
|
||
|
Authorization: `Bearer ${this.config.privateApiKey}`,
|
||
|
},
|
||
|
})
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Can return more than 1. Builder doesnt have unique fields.
|
||
|
*/
|
||
|
private fetchBuilderIoEntryIds(variantId: string): Promise<string[]> {
|
||
|
this.logger.trace(
|
||
|
{
|
||
|
modelName: this.config.modelName,
|
||
|
variantID: variantId,
|
||
|
variantFieldMapping: this.config.productVariantFieldsMapping.variantId,
|
||
|
},
|
||
|
"Trying to fetch variant from Builder.io"
|
||
|
);
|
||
|
|
||
|
return fetch(
|
||
|
`https://cdn.builder.io/api/v3/content/${this.config.modelName}?apiKey=${this.config.publicApiKey}&query.data.${this.config.productVariantFieldsMapping.variantId}.$eq=${variantId}&limit=10&includeUnpublished=false&cacheSeconds=0`
|
||
|
)
|
||
|
.then((res) => res.json())
|
||
|
.then((data) => {
|
||
|
return data.results.map((result: any) => result.id) as string[];
|
||
|
})
|
||
|
.catch((err) => {
|
||
|
this.logger.error(err, "Failed to fetch builder.io entry id");
|
||
|
throw err;
|
||
|
});
|
||
|
}
|
||
|
}
|