251 lines
7.5 KiB
TypeScript
251 lines
7.5 KiB
TypeScript
import { listProductTypes } from '@lib/data/products';
|
|
import Medusa from '@medusajs/js-sdk';
|
|
import type { AdminProductCategory } from '@medusajs/types';
|
|
|
|
import {
|
|
createMedusaSyncFailEntry,
|
|
createMedusaSyncSuccessEntry,
|
|
} from '~/lib/services/analyses.service';
|
|
import { getAnalysisElements } from '~/lib/services/analysis-element.service';
|
|
import { getAnalysisGroups } from '~/lib/services/analysis-group.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 { product_categories: existingProductCategories } =
|
|
await medusa.admin.productCategory.list();
|
|
const parentCategory = existingProductCategories.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.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);
|
|
}
|
|
}
|
|
|
|
async function getChildProductCategories({ medusa }: { medusa: Medusa }) {
|
|
const { product_categories: allCategories } =
|
|
await medusa.admin.productCategory.list();
|
|
const childCategories = allCategories.filter(
|
|
({ parent_category_id }) => parent_category_id !== null,
|
|
);
|
|
return childCategories;
|
|
}
|
|
|
|
async function deleteProductCategories({
|
|
medusa,
|
|
categories,
|
|
}: {
|
|
medusa: Medusa;
|
|
categories: AdminProductCategory[];
|
|
}) {
|
|
for (const category of categories) {
|
|
await medusa.admin.productCategory.delete(category.id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* In case a reset is needed
|
|
*/
|
|
async function deleteProducts({ medusa }: { medusa: Medusa }) {
|
|
const { products: existingProducts } = await medusa.admin.product.list({
|
|
fields: 'id,collection_id',
|
|
limit: 1000,
|
|
});
|
|
|
|
await Promise.all(
|
|
existingProducts
|
|
.filter((a) => !a.collection_id)
|
|
.map(({ id }) => medusa.admin.product.delete(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({ getAll: true }),
|
|
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;
|
|
}
|
|
|
|
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,
|
|
});
|
|
}
|
|
}
|
|
|
|
export default async function syncAnalysisGroupsStore() {
|
|
const medusa = getAdminSdk();
|
|
|
|
try {
|
|
await createProductCategories({ medusa });
|
|
|
|
// const categories = await getChildProductCategories({ medusa });
|
|
// await deleteProductCategories({ medusa, categories });
|
|
// 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)}`,
|
|
);
|
|
}
|
|
}
|