import Component from '#models/component' import Computer from '#models/computer' import InvalidComponentSelectionException from '#exceptions/invalid_component_selection_exception' async function handleComponents(componentsId: number[], computer: Computer, userId: number) { const currentComponents = await computer.related('components').query() const currentComponentsIds = currentComponents.map((component) => component.id) const componentsToAdd = componentsId.filter((id) => !currentComponentsIds.includes(id)) const componentsToRemove = currentComponentsIds.filter((id) => !componentsId.includes(id)) const componentsToAddRecords = await Promise.all( componentsToAdd.map((id: number) => Component.query().where('userId', userId).andWhere('id', id).preload('type').firstOrFail() ) ) // Ensure at most one component of each "unique" type ends up attached to // the computer once the additions/removals are applied. const remainingComponentIds = currentComponentsIds.filter( (id) => !componentsToRemove.includes(id) ) const remainingComponents = remainingComponentIds.length ? await Component.query().whereIn('id', remainingComponentIds).preload('type') : [] const finalComponents = [...remainingComponents, ...componentsToAddRecords] const uniqueTypeCounts = new Map() for (const component of finalComponents) { if (!component.type.unique) continue const count = (uniqueTypeCounts.get(component.type.id) ?? 0) + 1 uniqueTypeCounts.set(component.type.id, count) if (count > 1) { throw new InvalidComponentSelectionException( `Only one component of type "${component.type.name}" can be attached to a computer at a time.` ) } } await Promise.all( componentsToAddRecords.map(async (component) => { component.computerId = computer.id await component.save() }) ) await Promise.all( componentsToRemove.map(async (id: number) => { const component = await Component.query() .where('userId', userId) .andWhere('id', id) .firstOrFail() component.computerId = null await component.save() }) ) } export { handleComponents }