Browse Source

Establish User, Customer, Administrator entities & relations

Michael Bromley 7 years ago
parent
commit
0d547c882d

+ 18 - 0
modules/core/api/customer/customer.controller.ts

@@ -0,0 +1,18 @@
+import { Controller, Get, Param } from '@nestjs/common';
+import { CustomerService } from './customer.service';
+import { Customer } from "../../entity/customer/customer.interface";
+
+@Controller('customers')
+export class CustomerController {
+    constructor(private userService: CustomerService) {}
+
+    @Get()
+    findAll(): Promise<Customer[]> {
+        return this.userService.findAll();
+    }
+
+    @Get(':id')
+    findOne(@Param() params): Promise<Customer> {
+        return this.userService.findOne(params.id);
+    }
+}

+ 25 - 0
modules/core/api/customer/customer.resolver.ts

@@ -0,0 +1,25 @@
+import { Query, ResolveProperty, Resolver } from '@nestjs/graphql';
+import { CustomerService } from './customer.service';
+import { Address } from '../../entity/address/address.interface';
+import { CustomerEntity } from "../../entity/customer/customer.entity";
+import { Customer } from "../../entity/customer/customer.interface";
+
+@Resolver('Customer')
+export class CustomerResolver {
+    constructor(private customerService: CustomerService) {}
+
+    @Query('customers')
+    customers(): Promise<Customer[]> {
+        return this.customerService.findAll();
+    }
+
+    @Query('customer')
+    customer(obj, args): Promise<Customer> {
+        return this.customerService.findOne(args.id);
+    }
+
+    @ResolveProperty('addresses')
+    addresses(customer: CustomerEntity): Promise<Address[]> {
+        return this.customerService.findAddressesByCustomerId(customer.id);
+    }
+}

+ 28 - 0
modules/core/api/customer/customer.service.ts

@@ -0,0 +1,28 @@
+import { Injectable } from '@nestjs/common';
+import { InjectConnection } from '@nestjs/typeorm';
+import { Connection } from 'typeorm';
+import { AddressEntity } from '../../entity/address/address.entity';
+import { Address } from '../../entity/address/address.interface';
+import { CustomerEntity } from "../../entity/customer/customer.entity";
+import { Customer } from "../../entity/customer/customer.interface";
+
+@Injectable()
+export class CustomerService {
+    constructor(@InjectConnection() private connection: Connection) {}
+
+    findAll(): Promise<Customer[]> {
+        return this.connection.manager.find(CustomerEntity);
+    }
+
+    findOne(userId: number): Promise<Customer> {
+        return this.connection.manager.findOne(CustomerEntity, userId);
+    }
+
+    findAddressesByCustomerId(customerId: number): Promise<Address[]> {
+        return this.connection
+            .getRepository(AddressEntity)
+            .createQueryBuilder('address')
+            .where('address.customerId = :id', { id: customerId })
+            .getMany();
+    }
+}

+ 4 - 0
modules/core/api/customer/customer.types.graphql

@@ -0,0 +1,4 @@
+type Query {
+  customers: [Customer]
+  customer(id: Int!): Customer
+}

+ 0 - 21
modules/core/api/product/product.types.graphql

@@ -2,24 +2,3 @@ type Query {
   products(lang: String): [Product]
   product(id: Int!, lang: String): Product
 }
-
-type Product {
-    id: Int
-    name: String
-    slug: String
-    description: String
-    image: String
-    variants: [ProductVariant]
-    createdAt: String
-    updatedAt: String
-}
-
-type ProductVariant {
-    id: Int
-    sku: String
-    name: String
-    image: String
-    price: Int
-    createdAt: String
-    updatedAt: String
-}

+ 0 - 18
modules/core/api/user/user.controller.ts

@@ -1,18 +0,0 @@
-import { Controller, Get, Param } from '@nestjs/common';
-import { UserService } from './user.service';
-import { User } from '../../entity/user/user.interface';
-
-@Controller('users')
-export class UserController {
-    constructor(private userService: UserService) {}
-
-    @Get()
-    findAll(): Promise<User[]> {
-        return this.userService.findAll();
-    }
-
-    @Get(':id')
-    findOne(@Param() params): Promise<User> {
-        return this.userService.findOne(params.id);
-    }
-}

+ 0 - 25
modules/core/api/user/user.resolver.ts

@@ -1,25 +0,0 @@
-import { Query, ResolveProperty, Resolver } from '@nestjs/graphql';
-import { UserEntity } from '../../entity/user/user.entity';
-import { UserService } from './user.service';
-import { Address } from '../../entity/address/address.interface';
-import { User } from '../../entity/user/user.interface';
-
-@Resolver('User')
-export class UserResolver {
-    constructor(private userService: UserService) {}
-
-    @Query('users')
-    users(): Promise<User[]> {
-        return this.userService.findAll();
-    }
-
-    @Query('user')
-    user(obj, args): Promise<User> {
-        return this.userService.findOne(args.id);
-    }
-
-    @ResolveProperty('addresses')
-    addresses(user: UserEntity): Promise<Address[]> {
-        return this.userService.findAddressesByUserId(user.id);
-    }
-}

+ 0 - 28
modules/core/api/user/user.service.ts

@@ -1,28 +0,0 @@
-import { Injectable } from '@nestjs/common';
-import { InjectConnection } from '@nestjs/typeorm';
-import { Connection } from 'typeorm';
-import { UserEntity } from '../../entity/user/user.entity';
-import { AddressEntity } from '../../entity/address/address.entity';
-import { User } from '../../entity/user/user.interface';
-import { Address } from '../../entity/address/address.interface';
-
-@Injectable()
-export class UserService {
-    constructor(@InjectConnection() private connection: Connection) {}
-
-    findAll(): Promise<User[]> {
-        return this.connection.manager.find(UserEntity);
-    }
-
-    findOne(userId: number): Promise<User> {
-        return this.connection.manager.findOne(UserEntity, userId);
-    }
-
-    findAddressesByUserId(userId: number): Promise<Address[]> {
-        return this.connection
-            .getRepository(AddressEntity)
-            .createQueryBuilder('address')
-            .where('address.userId = :id', { id: userId })
-            .getMany();
-    }
-}

+ 13 - 5
modules/core/app.module.ts

@@ -3,12 +3,13 @@ import { TypeOrmModule } from '@nestjs/typeorm';
 import { GraphQLModule } from '@nestjs/graphql';
 import { graphqlExpress, graphiqlExpress } from 'apollo-server-express';
 import { GraphQLFactory } from '@nestjs/graphql';
-import { UserService } from './api/user/user.service';
-import { UserController } from './api/user/user.controller';
-import { UserResolver } from './api/user/user.resolver';
+import { CustomerService } from './api/customer/customer.service';
+import { CustomerController } from './api/customer/customer.controller';
+import { CustomerResolver } from './api/customer/customer.resolver';
 import { ProductService } from './api/product/product.service';
 import { ProductResolver } from './api/product/product.resolver';
 import { LocaleService } from './locale/locale.service';
+import { PasswordService } from "./auth/password.service";
 
 @Module({
     imports: [
@@ -25,8 +26,15 @@ import { LocaleService } from './locale/locale.service';
             database: 'test',
         }),
     ],
-    controllers: [UserController],
-    providers: [UserService, UserResolver, ProductService, ProductResolver, LocaleService],
+    controllers: [CustomerController],
+    providers: [
+        CustomerService,
+        CustomerResolver,
+        ProductService,
+        ProductResolver,
+        LocaleService,
+        PasswordService
+    ],
 })
 export class AppModule implements NestModule {
     constructor(private readonly graphQLFactory: GraphQLFactory) {}

+ 16 - 0
modules/core/auth/password.service.ts

@@ -0,0 +1,16 @@
+import { Injectable } from "@nestjs/common";
+import * as bcrypt from 'bcrypt';
+
+const SALT_ROUNDS = 12;
+
+@Injectable()
+export class PasswordService {
+
+    hash(plaintext: string): Promise<string> {
+        return bcrypt.hash(plaintext, SALT_ROUNDS);
+    }
+
+    check(plaintext: string, hash: string): Promise<boolean> {
+        return bcrypt.compare(plaintext, hash);
+    }
+}

+ 7 - 0
modules/core/auth/roles.ts

@@ -0,0 +1,7 @@
+/**
+ * All possible authorization roles for registered users.
+ */
+export enum Role {
+    Customer = 'Customer',
+    Superadmin = 'Superadmin'
+}

+ 3 - 2
modules/core/entity/address/address.entity.ts

@@ -1,13 +1,14 @@
 import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
 import { UserEntity } from '../user/user.entity';
 import { Address } from './address.interface';
+import { CustomerEntity } from "../customer/customer.entity";
 
 @Entity('address')
 export class AddressEntity implements Address {
     @PrimaryGeneratedColumn() id: number;
 
-    @ManyToOne(type => UserEntity, user => user.addresses)
-    user: UserEntity;
+    @ManyToOne(type => CustomerEntity, customer => customer.addresses)
+    customer: CustomerEntity;
 
     @Column() fullName: string;
 

+ 0 - 17
modules/core/api/user/user.types.graphql → modules/core/entity/address/address.graphql

@@ -1,22 +1,5 @@
-type Query {
-  users: [User]
-  user(id: Int!): User
-}
-
-type User {
-  id: Int
-  firstName: String
-  lastName: String
-  phoneNumber: String
-  emailAddress: String
-  addresses: [Address]
-  createdAt: String
-  updatedAt: String
-}
-
 type Address {
   id: Int
-  user: User
   fullName: String
   company: String
   streetLine1: String

+ 31 - 0
modules/core/entity/administrator/administrator.entity.ts

@@ -0,0 +1,31 @@
+import {
+    Column,
+    CreateDateColumn,
+    Entity,
+    JoinColumn,
+    OneToOne,
+    PrimaryGeneratedColumn,
+    UpdateDateColumn
+} from 'typeorm';
+import { Administrator } from "./administrator.interface";
+import { UserEntity } from "../user/user.entity";
+import { User } from "../user/user.interface";
+
+@Entity('administrator')
+export class AdministratorEntity implements Administrator {
+    @PrimaryGeneratedColumn() id: number;
+
+    @Column() firstName: string;
+
+    @Column() lastName: string;
+
+    @Column() emailAddress: string;
+
+    @OneToOne(type => UserEntity)
+    @JoinColumn()
+    user: User;
+
+    @CreateDateColumn() createdAt: string;
+
+    @UpdateDateColumn() updatedAt: string;
+}

+ 9 - 0
modules/core/entity/administrator/administrator.graphql

@@ -0,0 +1,9 @@
+type Administrator {
+    id: Int
+    firstName: String
+    lastName: String
+    emailAddress: String
+    user: User
+    createdAt: String
+    updatedAt: String
+}

+ 14 - 0
modules/core/entity/administrator/administrator.interface.ts

@@ -0,0 +1,14 @@
+import { User } from "../user/user.interface";
+
+/**
+ * An administrator of the system.
+ */
+export interface Administrator {
+    id: number;
+    firstName: string;
+    lastName: string;
+    emailAddress: string;
+    user: User;
+    createdAt: string;
+    updatedAt: string;
+}

+ 37 - 0
modules/core/entity/customer/customer.entity.ts

@@ -0,0 +1,37 @@
+import {
+    Column,
+    CreateDateColumn,
+    Entity, JoinColumn,
+    OneToMany,
+    OneToOne,
+    PrimaryGeneratedColumn,
+    UpdateDateColumn
+} from 'typeorm';
+import { AddressEntity } from '../address/address.entity';
+import { Customer } from "./customer.interface";
+import { UserEntity } from "../user/user.entity";
+import { User } from "../user/user.interface";
+
+@Entity('customer')
+export class CustomerEntity implements Customer {
+    @PrimaryGeneratedColumn() id: number;
+
+    @Column() firstName: string;
+
+    @Column() lastName: string;
+
+    @Column() phoneNumber: string;
+
+    @Column() emailAddress: string;
+
+    @OneToMany(type => AddressEntity, address => address.customer)
+    addresses: AddressEntity[];
+
+    @OneToOne(type => UserEntity, { eager: true })
+    @JoinColumn()
+    user?: User;
+
+    @CreateDateColumn() createdAt: string;
+
+    @UpdateDateColumn() updatedAt: string;
+}

+ 11 - 0
modules/core/entity/customer/customer.graphql

@@ -0,0 +1,11 @@
+type Customer {
+  id: Int
+  firstName: String
+  lastName: String
+  phoneNumber: String
+  emailAddress: String
+  addresses: [Address]
+  user: User
+  createdAt: String
+  updatedAt: String
+}

+ 18 - 0
modules/core/entity/customer/customer.interface.ts

@@ -0,0 +1,18 @@
+import { Address } from "../address/address.interface";
+import { User } from "../user/user.interface";
+
+/**
+ * A customer, i.e. a user who has trasacted with the shop in some way. A Customer may also be associated with
+ * a registered User, but in the case of anonymous checkouts, there will be no associated User.
+ */
+export interface Customer {
+    id: number;
+    firstName: string;
+    lastName: string;
+    phoneNumber: string;
+    emailAddress: string;
+    addresses: Address[];
+    user?: User;
+    createdAt: string;
+    updatedAt: string;
+}

+ 9 - 0
modules/core/entity/product-variant/product-variant.graphql

@@ -0,0 +1,9 @@
+type ProductVariant {
+    id: Int
+    sku: String
+    name: String
+    image: String
+    price: Int
+    createdAt: String
+    updatedAt: String
+}

+ 10 - 0
modules/core/entity/product/product.graphql

@@ -0,0 +1,10 @@
+type Product {
+    id: Int
+    name: String
+    slug: String
+    description: String
+    image: String
+    variants: [ProductVariant]
+    createdAt: String
+    updatedAt: String
+}

+ 5 - 7
modules/core/entity/user/user.entity.ts

@@ -1,21 +1,19 @@
 import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
 import { AddressEntity } from '../address/address.entity';
 import { User } from './user.interface';
+import { Role } from "../../auth/roles";
 
 @Entity('user')
 export class UserEntity implements User {
     @PrimaryGeneratedColumn() id: number;
 
-    @Column() firstName: string;
+    @Column() identifier: string;
 
-    @Column() lastName: string;
+    @Column() passwordHash: string;
 
-    @Column() phoneNumber: string;
+    @Column('simple-array') roles: Role[];
 
-    @Column() emailAddress: string;
-
-    @OneToMany(type => AddressEntity, address => address.user)
-    addresses: AddressEntity[];
+    @Column() lastLogin: string;
 
     @CreateDateColumn() createdAt: string;
 

+ 9 - 0
modules/core/entity/user/user.graphql

@@ -0,0 +1,9 @@
+type User {
+    id: Int
+    identifier: String
+    passwordHash: String
+    roles: [String]
+    lastLogin: String
+    createdAt: String
+    updatedAt: String
+}

+ 9 - 5
modules/core/entity/user/user.interface.ts

@@ -1,12 +1,16 @@
 import { Address } from '../address/address.interface';
+import { Role } from "../../auth/roles";
 
+/**
+ * A registered user of the system, either a Customer or Administrator. The User interface / entity is responsible
+ * for the identity of the user for the purposes of authentication & authorization.
+ */
 export class User {
     id: number;
-    firstName: string;
-    lastName: string;
-    phoneNumber: string;
-    emailAddress: string;
-    addresses: Address[];
+    identifier: string;
+    passwordHash: string;
+    roles: Role[];
+    lastLogin: string;
     createdAt: string;
     updatedAt: string;
 }

+ 160 - 0
modules/testing/mock-data.service.ts

@@ -0,0 +1,160 @@
+import * as faker from "faker/locale/en_GB";
+import { Connection, createConnection } from "typeorm";
+import { CustomerEntity } from "../core/entity/customer/customer.entity";
+import { ProductVariantEntity } from "../core/entity/product-variant/product-variant.entity";
+import { ProductEntity } from "../core/entity/product/product.entity";
+import { ProductVariantTranslationEntity } from "../core/entity/product-variant/product-variant-translation.entity";
+import { AddressEntity } from "../core/entity/address/address.entity";
+import { Role } from "../core/auth/roles";
+import { ProductTranslationEntity } from "../core/entity/product/product-translation.entity";
+import { PasswordService } from "../core/auth/password.service";
+import { UserEntity } from "../core/entity/user/user.entity";
+import { AdministratorEntity } from "../core/entity/administrator/administrator.entity";
+
+/**
+ * A Class used for generating mock data.
+ */
+export class MockDataService {
+    connection: Connection;
+
+    populate(): Promise<any> {
+        return createConnection({
+            type: 'mysql',
+            entities: ['./**/entity/**/*.entity.ts'],
+            synchronize: true,
+            logging: false,
+            host: '192.168.99.100',
+            port: 3306,
+            username: 'root',
+            password: '',
+            database: 'test',
+        }).then(async connection => {
+            this.connection = connection;
+
+            await this.clearAllTables();
+            await this.populateCustomersAndAddresses();
+            await this.populateProducts();
+            await this.populateAdministrators();
+        });
+    }
+
+    async clearAllTables() {
+        await this.connection.synchronize(true);
+        console.log('Cleared all tables');
+    }
+
+    async populateProducts() {
+        for (let i = 0; i < 5; i++) {
+            const product = new ProductEntity();
+            product.image = faker.image.imageUrl();
+
+            const name = faker.commerce.productName();
+            const slug = name.toLowerCase().replace(/\s+/g, '-');
+            const description = faker.lorem.sentence();
+
+            const translation1 = this.makeProductTranslation('en', name, slug, description);
+            const translation2 = this.makeProductTranslation('de', name, slug, description);
+            await this.connection.manager.save(translation1);
+            await this.connection.manager.save(translation2);
+
+            // 1 - 4 variants
+            const variantCount = Math.floor(Math.random() * 4) + 1;
+            let variants = [];
+            for (let j = 0; j < variantCount; j++) {
+                const variant = new ProductVariantEntity();
+                const variantName = `${name} variant ${j + 1}`;
+                variant.image = faker.image.imageUrl();
+                variant.price = faker.commerce.price(100, 12000, 0);
+
+                const variantTranslation1 = this.makeProductVariantTranslation('en', variantName);
+                const variantTranslation2 = this.makeProductVariantTranslation('de', variantName);
+                await this.connection.manager.save(variantTranslation1);
+                await this.connection.manager.save(variantTranslation2);
+
+                variant.translations = [variantTranslation1, variantTranslation2];
+                await this.connection.manager.save(variant);
+                console.log(`${j + 1}. created product variant ${variantName}`);
+                variants.push(variant);
+            }
+
+            product.variants = variants;
+            product.translations = [translation1, translation2];
+            await this.connection.manager.save(product);
+            console.log(`${i + 1}. created product & translations for ${translation1.name}`);
+        }
+    }
+
+    async populateCustomersAndAddresses() {
+        const passwordService = new PasswordService();
+
+        for (let i = 0; i < 5; i++) {
+            const customer = new CustomerEntity();
+            customer.firstName = faker.name.firstName();
+            customer.lastName = faker.name.lastName();
+            customer.emailAddress = faker.internet.email(customer.firstName, customer.lastName);
+            customer.phoneNumber = faker.phone.phoneNumber();
+
+            const user = new UserEntity();
+            user.passwordHash = await passwordService.hash('test');
+            user.identifier = customer.emailAddress;
+            user.roles = [Role.Customer];
+
+            await this.connection.manager.save(user);
+
+            const address = new AddressEntity();
+            address.fullName = `${customer.firstName} ${customer.lastName}`;
+            address.streetLine1 = faker.address.streetAddress();
+            address.city = faker.address.city();
+            address.province = faker.address.county();
+            address.postalCode = faker.address.zipCode();
+            address.country = faker.address.countryCode();
+
+            await this.connection.manager.save(address);
+
+            customer.addresses = [address];
+            customer.user = user;
+            await this.connection.manager.save(customer);
+            console.log('created customer, user and address for ' + customer.firstName + ' ' + customer.lastName);
+        }
+    }
+
+    async populateAdministrators() {
+        const passwordService = new PasswordService();
+
+        const user = new UserEntity();
+        user.passwordHash = await passwordService.hash('admin');
+        user.identifier = 'admin';
+        user.roles = [Role.Superadmin];
+
+        await this.connection.manager.save(user);
+
+        const administrator = new AdministratorEntity();
+        administrator.emailAddress = 'admin@test.com';
+        administrator.firstName = 'Super';
+        administrator.lastName = 'Admin';
+        administrator.user = user;
+
+        await this.connection.manager.save(administrator);
+    }
+
+    private makeProductTranslation(
+        langCode: string,
+        name: string,
+        slug: string,
+        description: string,
+    ): ProductTranslationEntity {
+        const productTranslation = new ProductTranslationEntity();
+        productTranslation.languageCode = langCode;
+        productTranslation.name = `${langCode} ${name}`;
+        productTranslation.slug = `${langCode} ${slug}`;
+        productTranslation.description = `${langCode} ${description}`;
+        return productTranslation;
+    }
+
+    private makeProductVariantTranslation(langCode: string, name: string): ProductVariantTranslationEntity {
+        const productVariantTranslation = new ProductVariantTranslationEntity();
+        productVariantTranslation.languageCode = langCode;
+        productVariantTranslation.name = `${langCode} ${name}`;;
+        return productVariantTranslation;
+    }
+}

+ 4 - 112
modules/testing/populate.ts

@@ -1,113 +1,5 @@
-import { Connection, createConnection } from 'typeorm';
-import * as faker from 'faker/locale/en_GB';
-import { UserEntity } from '../core/entity/user/user.entity';
-import { AddressEntity } from '../core/entity/address/address.entity';
-import { ProductEntity } from '../core/entity/product/product.entity';
-import { ProductTranslationEntity } from '../core/entity/product/product-translation.entity';
-import { ProductVariantTranslationEntity } from '../core/entity/product-variant/product-variant-translation.entity';
-import { ProductVariantEntity } from '../core/entity/product-variant/product-variant.entity';
+import { MockDataService } from "./mock-data.service";
 
-populate();
-
-export async function populate() {
-    const connection = await createConnection({
-        type: 'mysql',
-        entities: ['./**/entity/**/*.entity.ts'],
-        synchronize: true,
-        logging: false,
-        host: '192.168.99.100',
-        port: 3306,
-        username: 'root',
-        password: '',
-        database: 'test',
-    });
-
-    await populateUsersAndAddresses(connection);
-    await populateProducts(connection);
-}
-
-async function populateProducts(connection: Connection) {
-    for (let i = 0; i < 5; i++) {
-        const product = new ProductEntity();
-        product.image = faker.image.imageUrl();
-
-        const name = faker.commerce.productName();
-        const slug = name.toLowerCase().replace(/\s+/g, '-');
-        const description = faker.lorem.sentence();
-
-        const translation1 = makeProductTranslation('en', name, slug, description);
-        const translation2 = makeProductTranslation('de', name, slug, description);
-        await connection.manager.save(translation1);
-        await connection.manager.save(translation2);
-
-        // 1 - 4 variants
-        const variantCount = Math.floor(Math.random() * 4) + 1;
-        let variants = [];
-        for (let j = 0; j < variantCount; j++) {
-            const variant = new ProductVariantEntity();
-            const variantName = `${name} variant ${j + 1}`;
-            variant.image = faker.image.imageUrl();
-            variant.price = faker.commerce.price(100, 12000, 0);
-
-            const variantTranslation1 = makeProductVariantTranslation('en', variantName);
-            const variantTranslation2 = makeProductVariantTranslation('de', variantName);
-            await connection.manager.save(variantTranslation1);
-            await connection.manager.save(variantTranslation2);
-
-            variant.translations = [variantTranslation1, variantTranslation2];
-            await connection.manager.save(variant);
-            console.log(`${j + 1}. created product variant ${variantName}`);
-            variants.push(variant);
-        }
-
-        product.variants = variants;
-        product.translations = [translation1, translation2];
-        await connection.manager.save(product);
-        console.log(`${i + 1}. created product & translations for ${translation1.name}`);
-    }
-}
-
-function makeProductTranslation(
-    langCode: string,
-    name: string,
-    slug: string,
-    description: string,
-): ProductTranslationEntity {
-    const productTranslation = new ProductTranslationEntity();
-    productTranslation.languageCode = langCode;
-    productTranslation.name = `${langCode} ${name}`;
-    productTranslation.slug = `${langCode} ${slug}`;
-    productTranslation.description = `${langCode} ${description}`;
-    return productTranslation;
-}
-
-function makeProductVariantTranslation(langCode: string, name: string): ProductVariantTranslationEntity {
-    const productVariantTranslation = new ProductVariantTranslationEntity();
-    productVariantTranslation.languageCode = langCode;
-    productVariantTranslation.name = `${langCode} ${name}`;;
-    return productVariantTranslation;
-}
-
-async function populateUsersAndAddresses(connection: Connection) {
-    for (let i = 0; i < 5; i++) {
-        const user = new UserEntity();
-        user.firstName = faker.name.firstName();
-        user.lastName = faker.name.lastName();
-        user.emailAddress = faker.internet.email(user.firstName, user.lastName);
-        user.phoneNumber = faker.phone.phoneNumber();
-
-        const address = new AddressEntity();
-        address.fullName = `${user.firstName} ${user.lastName}`;
-        address.streetLine1 = faker.address.streetAddress();
-        address.city = faker.address.city();
-        address.province = faker.address.county();
-        address.postalCode = faker.address.zipCode();
-        address.country = faker.address.countryCode();
-
-        await connection.manager.save(address);
-
-        user.addresses = [address];
-        await connection.manager.save(user);
-        console.log('created user and address for ' + user.firstName + ' ' + user.lastName);
-    }
-}
+new MockDataService()
+    .populate()
+    .then(() => process.exit(0));

+ 2 - 0
package.json

@@ -27,6 +27,7 @@
     "@nestjs/testing": "^5.0.0",
     "@nestjs/typeorm": "^5.0.0",
     "apollo-server-express": "^1.3.6",
+    "bcrypt": "^2.0.1",
     "body-parser": "^1.18.3",
     "graphql": "^0.13.2",
     "graphql-tools": "^3.0.2",
@@ -37,6 +38,7 @@
     "typescript": "^2.8.0"
   },
   "devDependencies": {
+    "@types/bcrypt": "^2.0.0",
     "@types/express": "^4.0.39",
     "@types/faker": "^4.1.2",
     "@types/jest": "^21.1.8",

+ 30 - 4
yarn.lock

@@ -70,6 +70,10 @@
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
 
+"@types/bcrypt@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-2.0.0.tgz#74cccef82026341fd786cf2eb9c912c7f9107c55"
+
 "@types/body-parser@*":
   version "1.16.8"
   resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.16.8.tgz#687ec34140624a3bec2b1a8ea9268478ae8f3be3"
@@ -1253,6 +1257,13 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
+bcrypt@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-2.0.1.tgz#229c5afe09379789f918efe86e5e5b682e509f85"
+  dependencies:
+    nan "2.10.0"
+    node-pre-gyp "0.9.1"
+
 big.js@^3.1.3:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
@@ -4684,14 +4695,14 @@ mz@^2.4.0:
     object-assign "^4.0.1"
     thenify-all "^1.0.0"
 
+nan@2.10.0, nan@^2.9.2:
+  version "2.10.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
+
 nan@^2.3.0:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
 
-nan@^2.9.2:
-  version "2.10.0"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
-
 nanomatch@^1.2.9:
   version "1.2.9"
   resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2"
@@ -4782,6 +4793,21 @@ node-notifier@^5.0.2:
     shellwords "^0.1.0"
     which "^1.2.12"
 
+node-pre-gyp@0.9.1:
+  version "0.9.1"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz#f11c07516dd92f87199dbc7e1838eab7cd56c9e0"
+  dependencies:
+    detect-libc "^1.0.2"
+    mkdirp "^0.5.1"
+    needle "^2.2.0"
+    nopt "^4.0.1"
+    npm-packlist "^1.1.6"
+    npmlog "^4.0.2"
+    rc "^1.1.7"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^4"
+
 node-pre-gyp@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz#6e4ef5bb5c5203c6552448828c852c40111aac46"