Skip to content

Commit

Permalink
Update and use existing item definition
Browse files Browse the repository at this point in the history
  • Loading branch information
Tadjaur committed Dec 15, 2024
1 parent 8771cd4 commit a49fdd0
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 396 deletions.
26 changes: 10 additions & 16 deletions packages/validations/src/validations/itemRoutesValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,19 @@ export const addItemGlobal = z.object({
name: z.string(),
weight: z.number(),
unit: z.string(),
type: z.string(),
type: z.enum(['Food', 'Water', 'Essentials']),
ownerId: z.string(),
sku: z.string().optional(),
productUrl: z.string().optional(),
description: z.string().optional(),
productDetails: z
.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
.optional(),
seller: z.string().optional(),
image_urls: z.string().optional(),
});

export const addNewItemGlobal = z.object({
name: z.string(),
weight: z.number(),
unit: z.string(),
category: z.enum(['Food', 'Water', 'Essentials']),
sku: z.string(),
productUrl: z.string(),
description: z.string(),
productDetails: z.record(
z.string(),
z.union([z.string(), z.number(), z.boolean()]),
),
seller: z.string(),
imageUrls: z.string(),
});
export type AddItemGlobalType = z.infer<typeof addItemGlobal>;

export const importItemsGlobal = z.object({
content: z.string(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod';
import { addNewItemGlobal } from './itemRoutesValidator';
import { addItemGlobal } from './itemRoutesValidator';

export const getPackTemplates = z.object({
filter: z
Expand All @@ -23,7 +23,7 @@ export const getPackTemplate = z.union([
name: z.string(),
id: z.string().optional(),
}),
] as const);
]);

export const createPackFromTemplate = z.object({
packTemplateId: z.string().min(1),
Expand All @@ -37,10 +37,12 @@ export const addPackTemplate = z.object({
itemsOwnerId: z.string(),
itemPackTemplates: z.array(
z.object({
item: addNewItemGlobal,
item: addItemGlobal.omit({ ownerId: true }),
quantity: z.number(),
}),
),
});

export type AddPackTemplateType = z.infer<typeof addPackTemplate>;

export const addPackTemplates = z.array(addPackTemplate);
111 changes: 70 additions & 41 deletions server/src/controllers/item/importItemsGlobal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Context } from 'hono';
import {
addItemGlobalService,
addItemGlobalServiceBatch,
bulkAddItemsGlobalService,
} from '../../services/item/item.service';
import { protectedProcedure } from '../../trpc';
import * as validator from '@packrat/validations';
Expand Down Expand Up @@ -72,6 +72,61 @@ export const importItemsGlobal = async (c: Context) => {
}
};

/**
* Converts a list of raw CSV items into an iterable of validated items.
* @param {Array<Record<string, unknown>>} csvRawItems - The raw CSV items.
* @param {string} ownerId - The ID of the owner.
* @returns {Iterable<validator.AddItemGlobalType>} An iterable that yields the validated items.
*/
function* sanitizeItemsIterator(
csvRawItems: Array<Record<string, unknown>>,
ownerId: string,
): Generator<validator.AddItemGlobalType> {
for (let idx = 0; idx < csvRawItems.length; idx++) {
const item = csvRawItems[idx];

const productDetailsStr = `${item.techs}`
.replace(/'([^']*)'\s*:/g, '"$1":') // Replace single quotes keys with double quotes.
.replace(/:\s*'([^']*)'/g, ': "$1"') // Replace single quotes values with double quotes.
.replace(/\\x([0-9A-Fa-f]{2})/g, (match, hex) => {
// Replace hex escape sequences with UTF-8 characters
const codePoint = parseInt(hex, 16);
return String.fromCharCode(codePoint);
});

console.log(`${idx} / ${csvRawItems.length}`);
let parsedProductDetails:
| validator.AddItemGlobalType['productDetails']
| null = null;
try {
parsedProductDetails = JSON.parse(productDetailsStr);
} catch (e) {
console.log(
`${productDetailsStr}\nFailed to parse product details for item ${item.Name}: ${e.message}`,
);
throw e;
}

const validatedItem: validator.AddItemGlobalType = {
name: String(item.Name),
weight: Number(item.Weight),
unit: String(item.Unit),
type: String(item.Category) as ItemCategoryEnum,
ownerId,
image_urls: item.image_urls && String(item.image_urls),
sku: item.sku && String(item.sku),
productUrl: item.product_url && String(item.product_url),
description: item.description && String(item.description),
seller: item.seller && String(item.seller),
};

if (parsedProductDetails) {
validatedItem.productDetails = parsedProductDetails;
}

yield validatedItem;
}
}
export function importItemsGlobalRoute() {
const expectedHeaders = [
'Name',
Expand Down Expand Up @@ -114,49 +169,23 @@ export function importItemsGlobalRoute() {
results.data.pop();
}

let idx = 0;
await addItemGlobalServiceBatch(
results.data,
true,
const errors: Error[] = [];
const createdItems = await bulkAddItemsGlobalService(
sanitizeItemsIterator(results.data, ownerId),
opts.ctx.executionCtx,
(item) => {
const productDetailsStr = `${item.techs}`
.replace(/'([^']*)'\s*:/g, '"$1":') // Replace single quotes keys with double quotes.
.replace(/:\s*'([^']*)'/g, ': "$1"') // Replace single quotes values with double quotes.
.replace(/\\x([0-9A-Fa-f]{2})/g, (match, hex) => {
// Replace hex escape sequences with UTF-8 characters
const codePoint = parseInt(hex, 16);
return String.fromCharCode(codePoint);
});

idx++;
console.log(`${idx} / ${results.data.length}`);
try {
const parsedProductDetails = JSON.parse(productDetailsStr);
} catch (e) {
console.log(
`${productDetailsStr}\nFailed to parse product details for item ${item.Name}: ${e.message}`,
);
throw e;
}

return {
name: String(item.Name),
weight: Number(item.Weight),
unit: String(item.Unit),
type: String(item.Category) as ItemCategoryEnum,
ownerId,
executionCtx: opts.ctx.executionCtx,
image_urls: item.image_urls && String(item.image_urls),
sku: item.sku && String(item.sku),
productUrl: item.product_url && String(item.product_url),
description: item.description && String(item.description),
seller: item.seller && String(item.seller),
productDetails: JSON.parse(productDetailsStr),
};
{
onItemCreationError: (error) => {
errors.push(error);
},
},
);
return resolve('items');

return resolve({
status: 'success',
items: createdItems,
errorsCount: errors.length,
errors,
});
} catch (error) {
console.error(error);
return reject(new Error(`Failed to add items: ${error.message}`));
Expand Down
4 changes: 2 additions & 2 deletions server/src/controllers/user/getUsers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { protectedProcedure, publicProcedure } from '../../trpc';
import { protectedProcedure } from '../../trpc';
import { responseHandler } from '../../helpers/responseHandler';
import { User } from '../../drizzle/methods/User';

Expand All @@ -13,7 +13,7 @@ export const getUsers = async (c) => {
};

export function getUsersRoute() {
return publicProcedure.query(async (opts) => {
return protectedProcedure.query(async (opts) => {
const userClass = new User();
const users = await userClass.findMany();
return users;
Expand Down
Loading

0 comments on commit a49fdd0

Please sign in to comment.