feat(MED-131): update analyses sync to medusa store
This commit is contained in:
220
app/api/job/handler/sync-analysis-groups-store.ts
Normal file
220
app/api/job/handler/sync-analysis-groups-store.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
import Medusa from "@medusajs/js-sdk"
|
||||
import type { AdminProductCategory } from "@medusajs/types";
|
||||
import { listProductTypes } from "@lib/data/products";
|
||||
import { getAnalysisElements } from "~/lib/services/analysis-element.service";
|
||||
import { getAnalysisGroups } from "~/lib/services/analysis-group.service";
|
||||
import { createMedusaSyncFailEntry, createMedusaSyncSuccessEntry } from "~/lib/services/analyses.service";
|
||||
|
||||
const SYNLAB_SERVICES_CATEGORY_HANDLE = 'synlab-services';
|
||||
const SYNLAB_ANALYSIS_PRODUCT_TYPE_HANDLE = 'synlab-analysis';
|
||||
|
||||
const BASE_ANALYSIS_PRODUCT_HANDLE = 'analysis-base';
|
||||
|
||||
const getAdminSdk = () => {
|
||||
const medusaBackendUrl = process.env.MEDUSA_BACKEND_PUBLIC_URL!;
|
||||
const medusaPublishableApiKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY!;
|
||||
const key = process.env.MEDUSA_SECRET_API_KEY!;
|
||||
|
||||
if (!medusaBackendUrl || !medusaPublishableApiKey) {
|
||||
throw new Error('Medusa environment variables not set');
|
||||
}
|
||||
return new Medusa({
|
||||
baseUrl: medusaBackendUrl,
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
apiKey: key,
|
||||
});
|
||||
}
|
||||
|
||||
async function createProductCategories({
|
||||
medusa,
|
||||
}: {
|
||||
medusa: Medusa;
|
||||
}) {
|
||||
const existingProductCategories = await medusa.admin.productCategory.list();
|
||||
const parentCategory = existingProductCategories.product_categories.find(({ handle }) => handle === SYNLAB_SERVICES_CATEGORY_HANDLE);
|
||||
|
||||
if (!parentCategory) {
|
||||
throw new Error('Parent category not found');
|
||||
}
|
||||
|
||||
const analysisGroups = await getAnalysisGroups();
|
||||
if (!analysisGroups) {
|
||||
throw new Error('Analysis groups not found');
|
||||
}
|
||||
|
||||
const createdCategories: AdminProductCategory[] = [];
|
||||
for (const analysisGroup of analysisGroups) {
|
||||
console.info(`Processing analysis group '${analysisGroup.name}'`);
|
||||
|
||||
const isExisting = existingProductCategories.product_categories.find(({ name }) => name === analysisGroup.name);
|
||||
const isNewlyCreated = createdCategories.find(({ name }) => name === analysisGroup.name);
|
||||
if (isExisting || isNewlyCreated) {
|
||||
console.info(`Analysis group '${analysisGroup.name}' already exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const createResponse = await medusa.admin.productCategory.create({
|
||||
name: analysisGroup.name,
|
||||
handle: analysisGroup.name,
|
||||
parent_category_id: parentCategory.id,
|
||||
is_active: true,
|
||||
metadata: {
|
||||
analysisGroupOriginalId: analysisGroup.original_id,
|
||||
analysisGroupId: analysisGroup.id,
|
||||
},
|
||||
});
|
||||
console.info(`Successfully created category, id=${createResponse.product_category.id}`);
|
||||
createdCategories.push(createResponse.product_category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In case a reset is needed
|
||||
*/
|
||||
async function deleteProducts({
|
||||
medusa,
|
||||
}: {
|
||||
medusa: Medusa;
|
||||
}) {
|
||||
const { product_categories: allCategories } = await medusa.admin.productCategory.list();
|
||||
const { products: existingProducts } = await medusa.admin.product.list({
|
||||
category_id: allCategories.map(({ id }) => id),
|
||||
});
|
||||
|
||||
for (const product of existingProducts) {
|
||||
await medusa.admin.product.delete(product.id);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAnalysisPackagesType() {
|
||||
const { productTypes } = await listProductTypes();
|
||||
const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === SYNLAB_ANALYSIS_PRODUCT_TYPE_HANDLE);
|
||||
if (!analysisPackagesType) {
|
||||
throw new Error('Synlab analysis packages type not found');
|
||||
}
|
||||
return analysisPackagesType;
|
||||
}
|
||||
|
||||
async function getProductDefaultFields({
|
||||
medusa,
|
||||
}: {
|
||||
medusa: Medusa;
|
||||
}) {
|
||||
const baseProductsResponse = await medusa.admin.product.list({ handle: BASE_ANALYSIS_PRODUCT_HANDLE })
|
||||
const baseProduct = baseProductsResponse.products[0];
|
||||
if (!baseProduct) {
|
||||
throw new Error('Base product not found');
|
||||
}
|
||||
const defaultSalesChannels = baseProduct.sales_channels;
|
||||
if (!Array.isArray(defaultSalesChannels)) {
|
||||
throw new Error('Base analysis product has no required sales channels');
|
||||
}
|
||||
const defaultProductOption = baseProduct.options;
|
||||
if (!Array.isArray(defaultProductOption)) {
|
||||
throw new Error('Base analysis product has no required options');
|
||||
}
|
||||
const defaultProductVariant = baseProduct.variants?.[0];
|
||||
if (!defaultProductVariant) {
|
||||
throw new Error('Base analysis product has no required variant');
|
||||
}
|
||||
|
||||
return {
|
||||
defaultSalesChannels,
|
||||
defaultProductOption,
|
||||
defaultProductVariant,
|
||||
}
|
||||
}
|
||||
|
||||
async function createProducts({
|
||||
medusa,
|
||||
}: {
|
||||
medusa: Medusa;
|
||||
}) {
|
||||
const { product_categories: allCategories } = await medusa.admin.productCategory.list();
|
||||
|
||||
const [
|
||||
{ products: existingProducts },
|
||||
analysisElements,
|
||||
analysisPackagesType,
|
||||
{
|
||||
defaultSalesChannels,
|
||||
defaultProductOption,
|
||||
defaultProductVariant,
|
||||
}
|
||||
] = await Promise.all([
|
||||
medusa.admin.product.list({
|
||||
category_id: allCategories.map(({ id }) => id),
|
||||
}),
|
||||
getAnalysisElements(),
|
||||
getAnalysisPackagesType(),
|
||||
getProductDefaultFields({ medusa }),
|
||||
])
|
||||
|
||||
for (const analysisElement of analysisElements) {
|
||||
const { analysis_id_original: originalId } = analysisElement;
|
||||
const isExisting = existingProducts.find(({ metadata }) => metadata?.analysisIdOriginal === originalId);
|
||||
if (isExisting) {
|
||||
console.info(`Analysis element '${analysisElement.analysis_name_lab}' already exists`);
|
||||
continue;
|
||||
}
|
||||
const { analysis_name_lab: name } = analysisElement;
|
||||
if (!name) {
|
||||
console.error(`Analysis element '${originalId}' has no name`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const category = allCategories.find(({ metadata }) => metadata?.analysisGroupId === analysisElement.parent_analysis_group_id);
|
||||
if (!category) {
|
||||
console.error(`Category not found for analysis element '${name}'`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const createResponse = await medusa.admin.product.create({
|
||||
title: name,
|
||||
handle: `analysis-element-${analysisElement.id}`,
|
||||
categories: [{ id: category.id }],
|
||||
options: defaultProductOption.map(({ id, title, values }) => ({
|
||||
id,
|
||||
title,
|
||||
values: values?.map(({ value }) => value) ?? [],
|
||||
})),
|
||||
metadata: {
|
||||
analysisIdOriginal: originalId,
|
||||
},
|
||||
is_giftcard: false,
|
||||
discountable: false,
|
||||
status: 'published',
|
||||
sales_channels: defaultSalesChannels.map(({ id }) => ({ id })),
|
||||
variants: [
|
||||
{
|
||||
title: defaultProductVariant.title!,
|
||||
prices: defaultProductVariant.prices!,
|
||||
manage_inventory: false,
|
||||
},
|
||||
],
|
||||
type_id: analysisPackagesType.id,
|
||||
});
|
||||
console.info(`Successfully created product, id=${createResponse.product.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default async function syncAnalysisGroupsStore() {
|
||||
const medusa = getAdminSdk();
|
||||
|
||||
try {
|
||||
await createProductCategories({ medusa });
|
||||
|
||||
// await deleteProducts({ medusa });
|
||||
// return;
|
||||
|
||||
await createProducts({ medusa });
|
||||
|
||||
await createMedusaSyncSuccessEntry();
|
||||
} catch (e) {
|
||||
await createMedusaSyncFailEntry(JSON.stringify(e));
|
||||
console.error(e);
|
||||
throw new Error(
|
||||
`Failed to sync analyses to Medusa, error: ${JSON.stringify(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user