Browse Source

Implement create mutation on ProductOptionGroups

Michael Bromley 7 years ago
parent
commit
274c06defb

+ 7 - 0
modules/core/api/product-option/product-option.api.graphql

@@ -2,3 +2,10 @@ type Query {
     productOptionGroups(languageCode: LanguageCode): [ProductOptionGroup]
     productOptionGroup(id: Int!, languageCode: LanguageCode): ProductOptionGroup
 }
+
+type Mutation {
+    "Create a new ProductOptionGroup"
+    createProductOptionGroup(input: CreateProductOptionGroupInput): ProductOptionGroup
+    #"Update an existing ProductOptionGroup"
+    #updateProductOptionGroup(input: UpdateProductOptionGroupInput): Product
+}

+ 21 - 3
modules/core/api/product-option/product-option.resolver.ts

@@ -1,21 +1,39 @@
 import { Mutation, Query, Resolver } from '@nestjs/graphql';
 import { ProductOptionGroup } from '../../entity/product-option-group/product-option-group.entity';
 import { Product } from '../../entity/product/product.entity';
+import { ProductOptionGroupService } from '../../service/product-option-group.service';
 import { ProductOptionService } from '../../service/product-option.service';
 import { ProductVariantService } from '../../service/product-variant.service';
 import { ProductService } from '../../service/product.service';
 
 @Resolver('Product')
 export class ProductOptionResolver {
-    constructor(private productOptionService: ProductOptionService) {}
+    constructor(
+        private productOptionGroupService: ProductOptionGroupService,
+        private productOptionService: ProductOptionService,
+    ) {}
 
     @Query('productOptionGroups')
     productOptionGroups(obj, args): Promise<ProductOptionGroup[]> {
-        return this.productOptionService.findAll(args.languageCode);
+        return this.productOptionGroupService.findAll(args.languageCode);
     }
 
     @Query('productOptionGroup')
     productOptionGroup(obj, args): Promise<ProductOptionGroup | undefined> {
-        return this.productOptionService.findOne(args.id, args.languageCode);
+        return this.productOptionGroupService.findOne(args.id, args.languageCode);
+    }
+
+    @Mutation()
+    async createProductOptionGroup(_, args): Promise<ProductOptionGroup> {
+        const { input } = args;
+        const group = await this.productOptionGroupService.create(args.input);
+
+        if (input.options && input.options.length) {
+            for (const option of input.options) {
+                await this.productOptionService.create(group, option);
+            }
+        }
+
+        return group;
     }
 }

+ 2 - 0
modules/core/app.module.ts

@@ -13,6 +13,7 @@ import { JwtStrategy } from './auth/jwt.strategy';
 import { PasswordService } from './auth/password.service';
 import { AdministratorService } from './service/administrator.service';
 import { CustomerService } from './service/customer.service';
+import { ProductOptionGroupService } from './service/product-option-group.service';
 import { ProductOptionService } from './service/product-option.service';
 import { ProductVariantService } from './service/product-variant.service';
 import { ProductService } from './service/product.service';
@@ -44,6 +45,7 @@ import { ProductService } from './service/product.service';
         ProductService,
         ProductOptionResolver,
         ProductOptionService,
+        ProductOptionGroupService,
         ProductVariantService,
         ProductResolver,
         PasswordService,

+ 7 - 0
modules/core/entity/product-option-group/product-option-group-translation.entity.ts

@@ -1,10 +1,17 @@
 import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
+import { DeepPartial } from '../../common/common-types';
 import { LanguageCode } from '../../locale/language-code';
 import { Translation } from '../../locale/locale-types';
 import { ProductOptionGroup } from './product-option-group.entity';
 
 @Entity('product_option_group_translation')
 export class ProductOptionGroupTranslation implements Translation<ProductOptionGroup> {
+    constructor(input?: DeepPartial<Translation<ProductOptionGroup>>) {
+        if (input) {
+            Object.assign(this, input);
+        }
+    }
+
     @PrimaryGeneratedColumn() id: number;
 
     @Column() languageCode: LanguageCode;

+ 8 - 0
modules/core/entity/product-option-group/product-option-group.dto.ts

@@ -0,0 +1,8 @@
+import { TranslatedInput } from '../../locale/locale-types';
+import { CreateProductOptionDto } from '../product-option/product-option.dto';
+import { ProductOptionGroup } from './product-option-group.entity';
+
+export interface CreateProductOptionGroupDto extends TranslatedInput<ProductOptionGroup> {
+    code: string;
+    options?: CreateProductOptionDto[];
+}

+ 7 - 0
modules/core/entity/product-option-group/product-option-group.entity.ts

@@ -1,10 +1,17 @@
 import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
+import { DeepPartial } from '../../common/common-types';
 import { LocaleString, Translatable, Translation } from '../../locale/locale-types';
 import { ProductOption } from '../product-option/product-option.entity';
 import { ProductOptionGroupTranslation } from './product-option-group-translation.entity';
 
 @Entity('product_option_group')
 export class ProductOptionGroup implements Translatable {
+    constructor(input?: DeepPartial<ProductOptionGroup>) {
+        if (input) {
+            Object.assign(this, input);
+        }
+    }
+
     @PrimaryGeneratedColumn() id: number;
 
     name: LocaleString;

+ 14 - 1
modules/core/entity/product-option-group/product-option-group.graphql

@@ -1,5 +1,6 @@
 type ProductOptionGroup {
     id: Int
+    languageCode: LanguageCode
     code: String
     name: String
     options: [ProductOption]
@@ -10,4 +11,16 @@ type ProductOptionGroupTranslation {
     id: Int!
     languageCode: LanguageCode!
     name: String!
-}
+}
+
+input ProductOptionGroupTranslationInput {
+    id: Int
+    languageCode: LanguageCode!
+    name: String!
+}
+
+input CreateProductOptionGroupInput {
+    code: String!
+    translations: [ProductOptionGroupTranslationInput]!
+    options: [CreateProductOptionInput]
+}

+ 7 - 0
modules/core/entity/product-option/product-option-translation.entity.ts

@@ -1,10 +1,17 @@
 import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
+import { DeepPartial } from '../../common/common-types';
 import { LanguageCode } from '../../locale/language-code';
 import { Translation } from '../../locale/locale-types';
 import { ProductOption } from './product-option.entity';
 
 @Entity('product_option_translation')
 export class ProductOptionTranslation implements Translation<ProductOption> {
+    constructor(input?: DeepPartial<Translation<ProductOption>>) {
+        if (input) {
+            Object.assign(this, input);
+        }
+    }
+
     @PrimaryGeneratedColumn() id: number;
 
     @Column() languageCode: LanguageCode;

+ 6 - 0
modules/core/entity/product-option/product-option.dto.ts

@@ -0,0 +1,6 @@
+import { TranslatedInput } from '../../locale/locale-types';
+import { ProductOption } from './product-option.entity';
+
+export interface CreateProductOptionDto extends TranslatedInput<ProductOption> {
+    code?: string;
+}

+ 8 - 1
modules/core/entity/product-option/product-option.entity.ts

@@ -7,12 +7,19 @@ import {
     PrimaryGeneratedColumn,
     UpdateDateColumn,
 } from 'typeorm';
-import { LocaleString, Translatable, Translation } from '../../locale/locale-types';
+import { DeepPartial } from '../../common/common-types';
+import { LocaleString, Translatable, Translation, TranslationInput } from '../../locale/locale-types';
 import { ProductOptionGroup } from '../product-option-group/product-option-group.entity';
 import { ProductOptionTranslation } from './product-option-translation.entity';
 
 @Entity('product_option')
 export class ProductOption implements Translatable {
+    constructor(input?: DeepPartial<ProductOption>) {
+        if (input) {
+            Object.assign(this, input);
+        }
+    }
+
     @PrimaryGeneratedColumn() id: number;
 
     name: LocaleString;

+ 12 - 0
modules/core/entity/product-option/product-option.graphql

@@ -1,5 +1,6 @@
 type ProductOption {
     id: Int
+    languageCode: LanguageCode
     code: String
     name: String
     translations: [ProductOptionTranslation]
@@ -10,3 +11,14 @@ type ProductOptionTranslation {
     languageCode: LanguageCode!
     name: String!
 }
+
+input ProductOptionTranslationInput {
+    id: Int
+    languageCode: LanguageCode!
+    name: String!
+}
+
+input CreateProductOptionInput {
+    code: String!
+    translations: [ProductOptionGroupTranslationInput]!
+}

+ 9 - 8
modules/core/locale/translate-entity.ts

@@ -82,8 +82,9 @@ export function translateDeep<T extends Translatable>(
                 valueLevel0.forEach((nested1, index) => {
                     object = translatedEntity[path0][index];
                     property = path1;
-                    value = translateLeaf(object, property, languageCode);
+                    object[property] = translateLeaf(object, property, languageCode);
                 });
+                object = null;
             } else {
                 object = translatedEntity[path0];
                 property = path1;
@@ -102,12 +103,12 @@ export function translateDeep<T extends Translatable>(
     return translatedEntity;
 }
 
-function translateLeaf(object: any, property: string, languageCode: LanguageCode): any {
-    if (Array.isArray(object[property])) {
-        return object[property].map(nested2 => translateEntity(nested2, languageCode));
-    } else if (object[property]) {
-        return translateEntity(object[property], languageCode);
-    } else {
-        return undefined;
+function translateLeaf(object: object | undefined, property: string, languageCode: LanguageCode): any {
+    if (object && object[property]) {
+        if (Array.isArray(object[property])) {
+            return object[property].map(nested2 => translateEntity(nested2, languageCode));
+        } else if (object[property]) {
+            return translateEntity(object[property], languageCode);
+        }
     }
 }

+ 46 - 0
modules/core/service/product-option-group.service.ts

@@ -0,0 +1,46 @@
+import { Injectable } from '@nestjs/common';
+import { InjectConnection } from '@nestjs/typeorm';
+import { Connection } from 'typeorm';
+import { DEFAULT_LANGUAGE_CODE } from '../common/constants';
+import { ProductOptionGroupTranslation } from '../entity/product-option-group/product-option-group-translation.entity';
+import { CreateProductOptionGroupDto } from '../entity/product-option-group/product-option-group.dto';
+import { ProductOptionGroup } from '../entity/product-option-group/product-option-group.entity';
+import { LanguageCode } from '../locale/language-code';
+import { translateDeep } from '../locale/translate-entity';
+
+@Injectable()
+export class ProductOptionGroupService {
+    constructor(@InjectConnection() private connection: Connection) {}
+
+    findAll(lang: LanguageCode): Promise<ProductOptionGroup[]> {
+        return this.connection.manager
+            .find(ProductOptionGroup, {
+                relations: ['options'],
+            })
+            .then(groups => groups.map(group => translateDeep(group, lang, ['options'])));
+    }
+
+    findOne(id: number, lang: LanguageCode): Promise<ProductOptionGroup | undefined> {
+        return this.connection.manager
+            .findOne(ProductOptionGroup, id, {
+                relations: ['options'],
+            })
+            .then(group => group && translateDeep(group, lang, ['options']));
+    }
+
+    async create(createProductOptionGroupDto: CreateProductOptionGroupDto): Promise<ProductOptionGroup> {
+        const optionGroup = new ProductOptionGroup(createProductOptionGroupDto);
+        const translations: ProductOptionGroupTranslation[] = [];
+
+        for (const input of createProductOptionGroupDto.translations) {
+            const translation = new ProductOptionGroupTranslation(input);
+            translations.push(translation);
+            await this.connection.manager.save(translation);
+        }
+
+        optionGroup.translations = translations;
+        const createdGroup = await this.connection.manager.save(optionGroup);
+
+        return this.findOne(createdGroup.id, DEFAULT_LANGUAGE_CODE) as Promise<ProductOptionGroup>;
+    }
+}

+ 29 - 8
modules/core/service/product-option.service.ts

@@ -1,7 +1,11 @@
 import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
 import { Connection } from 'typeorm';
+import { DEFAULT_LANGUAGE_CODE } from '../common/constants';
 import { ProductOptionGroup } from '../entity/product-option-group/product-option-group.entity';
+import { ProductOptionTranslation } from '../entity/product-option/product-option-translation.entity';
+import { CreateProductOptionDto } from '../entity/product-option/product-option.dto';
+import { ProductOption } from '../entity/product-option/product-option.entity';
 import { LanguageCode } from '../locale/language-code';
 import { translateDeep } from '../locale/translate-entity';
 
@@ -9,19 +13,36 @@ import { translateDeep } from '../locale/translate-entity';
 export class ProductOptionService {
     constructor(@InjectConnection() private connection: Connection) {}
 
-    findAll(lang: LanguageCode): Promise<ProductOptionGroup[]> {
+    findAll(lang: LanguageCode): Promise<ProductOption[]> {
         return this.connection.manager
-            .find(ProductOptionGroup, {
-                relations: ['options'],
+            .find(ProductOption, {
+                relations: ['group'],
             })
-            .then(groups => groups.map(group => translateDeep(group, lang, ['options'])));
+            .then(groups => groups.map(group => translateDeep(group, lang)));
     }
 
-    findOne(id: number, lang: LanguageCode): Promise<ProductOptionGroup | undefined> {
+    findOne(id: number, lang: LanguageCode): Promise<ProductOption | undefined> {
         return this.connection.manager
-            .findOne(ProductOptionGroup, id, {
-                relations: ['options'],
+            .findOne(ProductOption, id, {
+                relations: ['group'],
             })
-            .then(group => group && translateDeep(group, lang, ['options']));
+            .then(group => group && translateDeep(group, lang));
+    }
+
+    async create(group: ProductOptionGroup, createProductOptionDto: CreateProductOptionDto): Promise<ProductOption> {
+        const option = new ProductOption(createProductOptionDto);
+        const translations: ProductOptionTranslation[] = [];
+
+        for (const input of createProductOptionDto.translations) {
+            const translation = new ProductOptionTranslation(input);
+            translations.push(translation);
+            await this.connection.manager.save(translation);
+        }
+
+        option.translations = translations;
+        option.group = group;
+        const createdGroup = await this.connection.manager.save(option);
+
+        return this.findOne(createdGroup.id, DEFAULT_LANGUAGE_CODE) as Promise<ProductOption>;
     }
 }