diff --git a/src/items/ean/ean.service.ts b/src/items/ean/ean.service.ts new file mode 100644 index 0000000..e8f0309 --- /dev/null +++ b/src/items/ean/ean.service.ts @@ -0,0 +1,33 @@ +import { Controller } from '@nestjs/common'; +import assert from 'assert'; + +@Controller() +export class EANService { + // https://en.wikipedia.org/wiki/International_Article_Number#Calculation_of_checksum_digit + calcChecksum(id: string) { + assert(id.length === 12); + let sum = id + .split('') + .map((d) => parseInt(d, 10)) + .reduce((s, d, i) => s + d * (i % 2 === 0 ? 3 : 1), 10); + while (sum >= 10) { + sum -= 10; + } + return sum; + } + + isValid(ean: string) { + return this.calcChecksum(ean.slice(0, 12)) === parseInt(ean[12], 10); + } + + toID(ean: string) { + if (!this.isValid(ean)) { + throw new Error(`Invalid EAN: "${ean}"`); + } + } + + fromID(id: string) { + assert(id.length === 12); + return id + this.calcChecksum(id); + } +} diff --git a/src/items/items.model.ts b/src/items/items.model.ts index 29ef4fa..61a6e9e 100644 --- a/src/items/items.model.ts +++ b/src/items/items.model.ts @@ -7,6 +7,9 @@ export class ItemModel { @Field((type) => ID) id: string; + @Field() + ean13: string; + @Field() name: string; diff --git a/src/items/items.module.ts b/src/items/items.module.ts index 8d0ff3a..d2ad63d 100644 --- a/src/items/items.module.ts +++ b/src/items/items.module.ts @@ -4,10 +4,11 @@ import { Connection } from 'typeorm'; import { ItemsResolver } from './items.resolver'; import { ItemsService } from './items.service'; import { Item } from './items.entity'; +import { EANService } from './ean/ean.service'; @Module({ imports: [TypeOrmModule.forFeature([Item])], - providers: [ItemsResolver, ItemsService], + providers: [ItemsResolver, ItemsService, EANService], }) export class ItemsModule { constructor(private connection: Connection) {} diff --git a/src/items/items.resolver.ts b/src/items/items.resolver.ts index 8cc21a1..f980371 100644 --- a/src/items/items.resolver.ts +++ b/src/items/items.resolver.ts @@ -8,18 +8,24 @@ import { Resolver, } from '@nestjs/graphql'; import { NewItemInput } from './dto/new-item.input'; +import { EANService } from './ean/ean.service'; import { ItemModel } from './items.model'; import { ItemsService } from './items.service'; @Resolver((of) => ItemModel) export class ItemsResolver { - constructor(private itemsService: ItemsService) {} + constructor(private itemsService: ItemsService, private eans: EANService) {} @Query((returns) => ItemModel, { nullable: true }) async item(@Args('id', { type: () => ID }) id: string) { return this.itemsService.getItem(id); } + @ResolveField() + ean13(@Parent() item: ItemModel) { + return this.eans.fromID(item.id); + } + @ResolveField() async parent(@Parent() item: ItemModel) { return this.itemsService.getItemParent(item);