├── .nvmrc ├── src ├── infrastructure │ ├── common │ │ ├── mapper │ │ │ └── .gitkeep │ │ └── errors │ │ │ └── InfrastructureErrors.ts │ ├── database │ │ ├── orm │ │ │ ├── IOrm.ts │ │ │ └── OnionOrm.ts │ │ ├── enum │ │ │ └── UserRole.ts │ │ ├── repository │ │ │ ├── common │ │ │ │ ├── Query.ts │ │ │ │ ├── UpdateQueryData.ts │ │ │ │ ├── IRepository.ts │ │ │ │ └── UnitOfWork.ts │ │ │ ├── Portal │ │ │ │ └── Warehouse │ │ │ │ │ └── WarehouseRepository.ts │ │ │ ├── Equipment │ │ │ │ └── EquipmentUnitOfWork.ts │ │ │ └── Warehouse │ │ │ │ └── WarehouseWarehouseItemRepository.ts │ │ ├── fixtures │ │ │ ├── factories │ │ │ │ ├── StateFactory.ts │ │ │ │ ├── RateFactory.ts │ │ │ │ ├── RoleFactory.ts │ │ │ │ ├── WarehouseItemFactory.ts │ │ │ │ ├── UserFactory.ts │ │ │ │ ├── EquipmentFactory.ts │ │ │ │ └── WarehouseFactory.ts │ │ │ └── seeds │ │ │ │ ├── RateSeed.ts │ │ │ │ ├── StateSeed.ts │ │ │ │ └── WarehouseSeed.ts │ │ ├── entities │ │ │ ├── Rate.ts │ │ │ ├── Role.ts │ │ │ ├── State.ts │ │ │ ├── WarehouseItem.ts │ │ │ ├── Equipment.ts │ │ │ ├── User.ts │ │ │ └── Warehouse.ts │ │ ├── migrations │ │ │ ├── 1611436650648-create_rate.ts │ │ │ ├── 1611436598116-create_state.ts │ │ │ ├── 1580081950192-create_user.ts │ │ │ ├── 1615066557385-add_equipment_dimensions.ts │ │ │ ├── 1613939591337-create_warehouse.ts │ │ │ ├── 1615066763427-add_warehouse_dimensions_costs.ts │ │ │ ├── 1580567781829-create_equipment.ts │ │ │ ├── 1580148565494-create_role.ts │ │ │ ├── 1613939666376-create_warehouse_item.ts │ │ │ ├── 1611439711157-create_state_rates_rate.ts │ │ │ ├── 1611439711158-create_equipment_state_rate.ts │ │ │ ├── 1613940493612-remove_equipment_state_rate.ts │ │ │ └── 1615067499391-add_warehouse_properties_availability_capacity.ts │ │ ├── mappings │ │ │ ├── Rate │ │ │ │ └── RateEntityToRateDomain.ts │ │ │ ├── Role │ │ │ │ └── RoleEntityToRoleDomain.ts │ │ │ ├── State │ │ │ │ └── StateEntityToStateDomain.ts │ │ │ ├── Warehouse │ │ │ │ ├── WarehouseEntityToWarehouseDomain.ts │ │ │ │ └── WarehouseItemEntityToWarehouseItemDomain.ts │ │ │ ├── Equipment │ │ │ │ └── EquipmentEntityToEquipmentDomain.ts │ │ │ ├── User │ │ │ │ └── UserEntityToUserDomain.ts │ │ │ └── DBMapper.ts │ │ └── cli │ │ │ └── dbReload.ts │ └── InfrastructureModuleSymbols.ts ├── ui │ ├── Portal │ │ ├── User │ │ │ ├── graphql │ │ │ │ ├── inputs │ │ │ │ │ └── .gitkeep │ │ │ │ ├── gql │ │ │ │ │ └── User.graphql │ │ │ │ └── UserQuery.ts │ │ │ └── rest │ │ │ │ └── v1 │ │ │ │ ├── requests │ │ │ │ └── DeleteUserRequestBody.ts │ │ │ │ └── UserController.ts │ │ ├── Warehouse │ │ │ ├── graphql │ │ │ │ └── gql │ │ │ │ │ └── Warehouse.graphql │ │ │ └── WarehouseQuery.ts │ │ ├── Authentication │ │ │ └── rest │ │ │ │ └── v1 │ │ │ │ └── requests │ │ │ │ ├── AuthenticationRequestBody.ts │ │ │ │ └── SignupRequestBody.ts │ │ ├── Equipment │ │ │ ├── graphql │ │ │ │ ├── inputs │ │ │ │ │ └── CalculateEquipmentCostInput.ts │ │ │ │ ├── gql │ │ │ │ │ └── Equipment.graphql │ │ │ │ └── EquipmentQueries.ts │ │ │ └── rest │ │ │ │ └── v1 │ │ │ │ ├── requests │ │ │ │ └── CreateEquipmentRequestBody.ts │ │ │ │ └── EquipmentController.ts │ │ ├── WarehouseItem │ │ │ └── graphql │ │ │ │ ├── inputs │ │ │ │ └── AddWarehouseItemInput.ts │ │ │ │ ├── gql │ │ │ │ └── WarehouseItem.graphql │ │ │ │ └── WarehouseItemMutation.ts │ │ └── common │ │ │ └── graphql │ │ │ ├── PortalSubQuery.ts │ │ │ ├── PortalMutation.ts │ │ │ └── PortalQuery.ts │ ├── common │ │ ├── config │ │ │ ├── application │ │ │ │ ├── apollo │ │ │ │ │ ├── gql │ │ │ │ │ │ ├── schema.graphql │ │ │ │ │ │ ├── Node.graphql │ │ │ │ │ │ ├── Role.graphql │ │ │ │ │ │ └── Directives.graphql │ │ │ │ │ ├── common │ │ │ │ │ │ ├── IResolver.ts │ │ │ │ │ │ └── IApolloContext.ts │ │ │ │ │ ├── types │ │ │ │ │ │ └── Context.ts │ │ │ │ │ ├── consts │ │ │ │ │ │ └── schemaDirectives.ts │ │ │ │ │ ├── auth │ │ │ │ │ │ └── types │ │ │ │ │ │ │ └── GraphQLTokenPayload.ts │ │ │ │ │ ├── schema │ │ │ │ │ │ ├── RootQuery.ts │ │ │ │ │ │ ├── RootMutation.ts │ │ │ │ │ │ ├── RootSubQuery.ts │ │ │ │ │ │ └── RootResolver.ts │ │ │ │ │ ├── plugins │ │ │ │ │ │ └── RequestDidStartPlugin.ts │ │ │ │ │ ├── directives │ │ │ │ │ │ └── IsAuthenticatedDirective.ts │ │ │ │ │ ├── ApolloApplication.ts │ │ │ │ │ └── ApolloContext.ts │ │ │ │ ├── common │ │ │ │ │ ├── IConfig.ts │ │ │ │ │ ├── auth │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ └── Authentication.ts │ │ │ │ │ │ ├── IAuthenticationHandler.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ └── JWTTokenUtil.ts │ │ │ │ │ ├── BaseApplication.ts │ │ │ │ │ └── IApplication.ts │ │ │ │ └── express │ │ │ │ │ ├── auth │ │ │ │ │ ├── types │ │ │ │ │ │ └── TokenPayload.ts │ │ │ │ │ ├── models │ │ │ │ │ │ └── Principal.ts │ │ │ │ │ ├── utils │ │ │ │ │ │ └── getHttpContext.ts │ │ │ │ │ └── middlewares │ │ │ │ │ │ └── IsAuthenticated.ts │ │ │ │ │ └── utils │ │ │ │ │ └── unless.ts │ │ │ ├── logger │ │ │ │ ├── ILogger.ts │ │ │ │ └── BaseLogger.ts │ │ │ ├── errors │ │ │ │ ├── models │ │ │ │ │ └── ErrorResponse.ts │ │ │ │ ├── UserInterfaceError.ts │ │ │ │ └── handlers │ │ │ │ │ └── errorHandler.ts │ │ │ └── consts │ │ │ │ └── variables.ts │ │ ├── models │ │ │ └── User.ts │ │ └── mappings │ │ │ ├── UIMapper.ts │ │ │ └── User │ │ │ └── UserDomainToUserUI.ts │ ├── Administration │ │ ├── User │ │ │ └── graphql │ │ │ │ ├── inputs │ │ │ │ └── RemoveUserInput.ts │ │ │ │ ├── gql │ │ │ │ └── User.graphql │ │ │ │ ├── UserSubQuery.ts │ │ │ │ ├── UserQuery.ts │ │ │ │ └── UserMutation.ts │ │ ├── Warehouse │ │ │ └── graphql │ │ │ │ ├── inputs │ │ │ │ ├── GetWarehouseInput.ts │ │ │ │ ├── CreateWarehouseInput.ts │ │ │ │ └── UpdateWarehouseInput.ts │ │ │ │ ├── gql │ │ │ │ └── Warehouse.graphql │ │ │ │ ├── WarehouseQuery.ts │ │ │ │ ├── WarehouseMutation.ts │ │ │ │ └── WarehouseSubQuery.ts │ │ ├── WarehouseItem │ │ │ └── graphql │ │ │ │ ├── inputs │ │ │ │ ├── GetWarehouseItemInput.ts │ │ │ │ ├── CreateWarehouseItemInput.ts │ │ │ │ └── UpdateWarehouseItemInput.ts │ │ │ │ ├── gql │ │ │ │ └── WarehouseItem.graphql │ │ │ │ ├── WarehouseItemQuery.ts │ │ │ │ └── WarehouseItemSubQuery.ts │ │ ├── Equipment │ │ │ └── graphql │ │ │ │ ├── inputs │ │ │ │ └── CreateEquipmentInput.ts │ │ │ │ ├── EquipmentSubQuery.ts │ │ │ │ ├── gql │ │ │ │ └── Equipment.graphql │ │ │ │ ├── EquipmentQuery.ts │ │ │ │ └── EquipmentMutation.ts │ │ ├── Rate │ │ │ └── graphql │ │ │ │ ├── gql │ │ │ │ └── Rate.graphql │ │ │ │ └── RateQuery.ts │ │ ├── State │ │ │ └── graphql │ │ │ │ ├── gql │ │ │ │ └── State.graphql │ │ │ │ └── StateQuery.ts │ │ └── common │ │ │ └── graphql │ │ │ ├── AdministrationMutation.ts │ │ │ ├── AdministrationSubQuery.ts │ │ │ └── AdministrationQuery.ts │ ├── shared │ │ ├── Authentication │ │ │ └── graphql │ │ │ │ ├── inputs │ │ │ │ └── AuthenticateInput.ts │ │ │ │ ├── gql │ │ │ │ └── Authentication.graphql │ │ │ │ └── AuthenticationMutation.ts │ │ └── common │ │ │ └── graphql │ │ │ ├── SharedQuery.ts │ │ │ ├── SharedSubQuery.ts │ │ │ └── SharedMutation.ts │ └── index.ts ├── core │ ├── common │ │ ├── errors │ │ │ ├── CoreErrors.ts │ │ │ ├── CoreError.ts │ │ │ └── BaseError.ts │ │ └── mapper │ │ │ └── IMapping.ts │ ├── domain │ │ ├── User │ │ │ ├── UserRole.ts │ │ │ └── User.ts │ │ ├── Equipment │ │ │ ├── EquipmentCost.ts │ │ │ └── Equipment.ts │ │ ├── Rate │ │ │ └── Rate.ts │ │ ├── Role │ │ │ └── Role.ts │ │ ├── Authentication │ │ │ └── Authentication.ts │ │ ├── State │ │ │ └── State.ts │ │ └── Warehouse │ │ │ ├── WarehouseItem.ts │ │ │ └── Warehouse.ts │ ├── applicationServices │ │ ├── common │ │ │ ├── IInteractor.ts │ │ │ └── Equipment │ │ │ │ └── interactors │ │ │ │ └── requests │ │ │ │ └── CalculateEquipmentCostInteractorRequest.ts │ │ ├── Rate │ │ │ ├── requests │ │ │ │ └── FetchRateRequest.ts │ │ │ ├── IRateService.ts │ │ │ └── RateService.ts │ │ ├── User │ │ │ ├── requests │ │ │ │ ├── FetchUserRequest.ts │ │ │ │ ├── RemoveUserReuqest.ts │ │ │ │ ├── FetchUserEquipmentRequest.ts │ │ │ │ └── CalculateUserEquipmentCostRequest.ts │ │ │ ├── IUserService.ts │ │ │ ├── IUserEquipmentService.ts │ │ │ └── UserService.ts │ │ ├── State │ │ │ ├── requests │ │ │ │ └── FetchStateRequest.ts │ │ │ ├── IStateService.ts │ │ │ └── StateService.ts │ │ ├── Equipment │ │ │ ├── requests │ │ │ │ ├── FetchEquipmentRequest.ts │ │ │ │ └── CreateEquipmentRequest.ts │ │ │ └── IEquipmentService.ts │ │ ├── Warehouse │ │ │ ├── requests │ │ │ │ ├── FetchWarehouseRequest.ts │ │ │ │ ├── FetchWarehouseStateRequest.ts │ │ │ │ ├── CreateWarehouseRequest.ts │ │ │ │ └── UpdateWarehouseRequest.ts │ │ │ ├── IWarehouseStateService.ts │ │ │ ├── IWarehouseWarehouseItemService.ts │ │ │ ├── IWarehouseService.ts │ │ │ ├── WarehouseStateService.ts │ │ │ └── WarehouseWarehouseItemService.ts │ │ ├── WarehouseItem │ │ │ ├── requests │ │ │ │ ├── FetchWarehouseItemRequest.ts │ │ │ │ ├── FetchWarehouseItemsRequest.ts │ │ │ │ ├── FetchWarehouseItemEquipmentRequest.ts │ │ │ │ ├── FetchWarehouseItemWarehouseRequest.ts │ │ │ │ ├── CreateWarehouseItemRequest.ts │ │ │ │ └── UpdateWarehouseItemRequest.ts │ │ │ ├── IWarehouseItemEquipmentService.ts │ │ │ ├── IWarehouseItemWarehouseService.ts │ │ │ ├── IWarehouseItemService.ts │ │ │ ├── WarehouseItemEquipmentService.ts │ │ │ └── WarehouseItemWarehouseService.ts │ │ ├── Authentication │ │ │ ├── requests │ │ │ │ ├── AuthenticationRequest.ts │ │ │ │ └── SignUpRequest.ts │ │ │ └── IAuthenticationService.ts │ │ └── Portal │ │ │ ├── Warehouse │ │ │ ├── IWarehouseService.ts │ │ │ └── WarehouseService.ts │ │ │ └── WarehouseItem │ │ │ ├── requests │ │ │ └── AddWarehouseItemRequest.ts │ │ │ └── IWarehouseItemService.ts │ └── domainServices │ │ ├── Rate │ │ ├── request │ │ │ └── GetRateRepositoryRequest.ts │ │ └── IRateRepository.ts │ │ ├── Role │ │ ├── request │ │ │ ├── FindRoleRepositoryRequest.ts │ │ │ └── FindRoleByNameRepositoryRequest.ts │ │ └── IRoleRepository.ts │ │ ├── State │ │ ├── request │ │ │ └── GetStateRepositoryRequest.ts │ │ └── IStateRepository.ts │ │ ├── User │ │ ├── request │ │ │ ├── FindUserRepositoryRequest.ts │ │ │ ├── DeleteUserRepositoryRequest.ts │ │ │ ├── FindUserByEmailRepositoryRequest.ts │ │ │ ├── FindUserEquipmentRepositoryRequest.ts │ │ │ ├── DeleteUserUnitOfWorkRepositoryRequest.ts │ │ │ ├── GetUserEquipmentRepositoryRequest.ts │ │ │ ├── AddUserUnitOfWorkRepositoryRequest.ts │ │ │ └── AddUserRepositoryRequest.ts │ │ ├── IUserUnitOfWork.ts │ │ ├── IUserEquipmentRepository.ts │ │ └── IUserRepository.ts │ │ ├── Warehouse │ │ ├── request │ │ │ ├── GetWarehouseRepositoryRequest.ts │ │ │ ├── GetWarehouseStateRepositoryRequest.ts │ │ │ ├── CreateWarehouseRepositoryRequest.ts │ │ │ └── UpdateWarehouseRepositoryRequest.ts │ │ ├── IWarehouseStateRepository.ts │ │ ├── IWarehouseWarehouseItemRepository.ts │ │ └── IWarehouseRepository.ts │ │ ├── Equipment │ │ ├── request │ │ │ ├── FindEquipmentRepositoryRequest.ts │ │ │ ├── AddEquipmentRepositoryRequest.ts │ │ │ └── AddEquipmentUnitOfWorkRepositoryRequest.ts │ │ ├── IEquipmentUnitOfWork.ts │ │ └── IEquipmentRepository.ts │ │ ├── WarehouseItem │ │ ├── requests │ │ │ ├── GetWarehouseItemRepositoryRequest.ts │ │ │ ├── GetWarehouseItemsRepositoryRequest.ts │ │ │ ├── GetWarehouseItemEquipmentRepositoryRequest.ts │ │ │ ├── GetWarehouseItemWarehouseRepositoryRequest.ts │ │ │ ├── CreateWarehouseItemRepositoryRequest.ts │ │ │ └── UpdateWarehouseItemRepositoryRequest.ts │ │ ├── IWarehouseItemEquipmentRepository.ts │ │ ├── IWarehouseItemWarehouseRepository.ts │ │ └── IWarehouseItemRepository.ts │ │ └── Portal │ │ ├── Warehouse │ │ └── IWarehouseRepository.ts │ │ └── WarehouseItem │ │ ├── requests │ │ └── AddWarehouseItemRepositoryRequest.ts │ │ └── IWarehouseItemRepository.ts ├── dependency │ ├── BaseContainer.ts │ ├── BaseModule.ts │ ├── shared │ │ ├── Warehouse │ │ │ └── WarehouseModule.ts │ │ ├── Authentication │ │ │ └── AuthenticationModule.ts │ │ └── User │ │ │ └── UserModule.ts │ ├── Portal │ │ ├── User │ │ │ └── UserModule.ts │ │ ├── common │ │ │ └── PortalModule.ts │ │ ├── Warehouse │ │ │ └── WarehouseModule.ts │ │ ├── Equipment │ │ │ └── EquipmentModule.ts │ │ └── WarehouseItem │ │ │ └── WarehouseItemModule.ts │ ├── Administration │ │ ├── Role │ │ │ └── RoleModule.ts │ │ ├── User │ │ │ └── UserModule.ts │ │ ├── common │ │ │ └── AdministrationModule.ts │ │ ├── Rate │ │ │ └── RateModule.ts │ │ └── State │ │ │ └── StateModule.ts │ └── common │ │ └── CommonModule.ts └── index.ts ├── test ├── config │ ├── db │ │ ├── RollbackException.ts │ │ └── TransactionCreator.ts │ ├── types │ │ └── RunFunction.ts │ ├── mocks │ │ └── mockRepositoryConnectionName.ts │ ├── helpers │ │ ├── prepareTestTransaction.ts │ │ ├── getCurrentConnection.ts │ │ ├── getPostgresConnection.ts │ │ ├── inTransaction.ts │ │ ├── clearTestDB.ts │ │ ├── prepareTestApp.ts │ │ ├── prepareAuthenticationToken.ts │ │ └── prepareTestDB.ts │ └── seeds │ │ ├── UserSeed.ts │ │ └── AuthenticationSeed.ts └── .mocharc.json ├── .eslintignore ├── .env.test ├── onion.diagram.database.png ├── .prettierrc ├── nodemon.json ├── .env.example ├── tsconfig.json ├── CHANGELOG.md ├── stryker.conf.js ├── tsconfig.base.json ├── .gitignore └── ormconfig.sample.js /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.4.0 2 | -------------------------------------------------------------------------------- /src/infrastructure/common/mapper/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/Portal/User/graphql/inputs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/core/common/errors/CoreErrors.ts: -------------------------------------------------------------------------------- 1 | export enum CoreErrors {} 2 | -------------------------------------------------------------------------------- /test/config/db/RollbackException.ts: -------------------------------------------------------------------------------- 1 | export class RollbackException extends Error {} 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/infrastructure/database/migrations 2 | stryker.conf.js 3 | diagram.svg 4 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/gql/schema.graphql: -------------------------------------------------------------------------------- 1 | type Query 2 | type Mutation 3 | -------------------------------------------------------------------------------- /test/config/types/RunFunction.ts: -------------------------------------------------------------------------------- 1 | export type RunFunction = () => Promise | void; 2 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/gql/Node.graphql: -------------------------------------------------------------------------------- 1 | interface Node { 2 | id: ID! 3 | } 4 | -------------------------------------------------------------------------------- /src/infrastructure/database/orm/IOrm.ts: -------------------------------------------------------------------------------- 1 | export interface IOrm { 2 | initialize(): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/gql/Role.graphql: -------------------------------------------------------------------------------- 1 | enum Role { 2 | ADMIN , 3 | MEMBER, 4 | } 5 | -------------------------------------------------------------------------------- /src/ui/common/config/application/common/IConfig.ts: -------------------------------------------------------------------------------- 1 | export interface IConfig { 2 | initialize(): R; 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domain/User/UserRole.ts: -------------------------------------------------------------------------------- 1 | export enum USER_ROLE { 2 | ADMIN = 'ADMIN', 3 | MEMBER = 'MEMBER', 4 | } 5 | -------------------------------------------------------------------------------- /src/ui/Administration/User/graphql/inputs/RemoveUserInput.ts: -------------------------------------------------------------------------------- 1 | export type RemoveUserInput = { 2 | id: number; 3 | }; 4 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | APP_TOKEN_SECRET=abcd 2 | APP_TOKEN_LIFE=2h 3 | NODE_ENV=test 4 | LOG_LEVEL=debug 5 | ORM_CONNECTION=onion_test 6 | -------------------------------------------------------------------------------- /onion.diagram.database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Melzar/onion-architecture-boilerplate/HEAD/onion.diagram.database.png -------------------------------------------------------------------------------- /src/infrastructure/database/enum/UserRole.ts: -------------------------------------------------------------------------------- 1 | export enum USER_ROLE { 2 | ADMIN = 'ADMIN', 3 | MEMBER = 'MEMBER', 4 | } 5 | -------------------------------------------------------------------------------- /src/ui/Portal/User/graphql/gql/User.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | me: User! @isAuthenticated(role: [MEMBER]) 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/gql/Directives.graphql: -------------------------------------------------------------------------------- 1 | directive @isAuthenticated(role: [Role]) on FIELD_DEFINITION 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "trailingComma": "es5", 5 | "printWidth": 80, 6 | } 7 | -------------------------------------------------------------------------------- /src/core/domain/Equipment/EquipmentCost.ts: -------------------------------------------------------------------------------- 1 | export class EquipmentCost { 2 | constructor(public readonly cost: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/inputs/GetWarehouseInput.ts: -------------------------------------------------------------------------------- 1 | export type GetWarehouseInput = { 2 | id: number; 3 | }; 4 | -------------------------------------------------------------------------------- /src/core/domain/Rate/Rate.ts: -------------------------------------------------------------------------------- 1 | export class Rate { 2 | constructor(public readonly id: number, public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domain/Role/Role.ts: -------------------------------------------------------------------------------- 1 | export class Role { 2 | constructor(public readonly id: string, public readonly name: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Portal/User/rest/v1/requests/DeleteUserRequestBody.ts: -------------------------------------------------------------------------------- 1 | export type DeleteUserRequestBody = { 2 | readonly id: number; 3 | }; 4 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/common/IResolver.ts: -------------------------------------------------------------------------------- 1 | export interface IResolver { 2 | readonly resolvers: R; 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/common/config/logger/ILogger.ts: -------------------------------------------------------------------------------- 1 | export interface ILogger { 2 | initialize(): void; 3 | write(message: string): void; 4 | } 5 | -------------------------------------------------------------------------------- /src/core/applicationServices/common/IInteractor.ts: -------------------------------------------------------------------------------- 1 | export interface IInteractor { 2 | execute(...args: A[]): Promise | R; 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domain/Authentication/Authentication.ts: -------------------------------------------------------------------------------- 1 | export class Authentication { 2 | constructor(public readonly token: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Administration/WarehouseItem/graphql/inputs/GetWarehouseItemInput.ts: -------------------------------------------------------------------------------- 1 | export type GetWarehouseItemInput = { 2 | id: number; 3 | }; 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/Rate/requests/FetchRateRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchRateRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/requests/FetchUserRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchUserRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/common/errors/CoreError.ts: -------------------------------------------------------------------------------- 1 | import { BaseError } from 'core/common/errors/BaseError'; 2 | 3 | export class CoreError extends BaseError {} 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/State/requests/FetchStateRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchStateRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/requests/RemoveUserReuqest.ts: -------------------------------------------------------------------------------- 1 | export class RemoveUserRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/shared/Authentication/graphql/inputs/AuthenticateInput.ts: -------------------------------------------------------------------------------- 1 | export type AuthenticateInput = { 2 | email: string; 3 | password: string; 4 | }; 5 | -------------------------------------------------------------------------------- /src/core/domainServices/Rate/request/GetRateRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetRateRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/common/Query.ts: -------------------------------------------------------------------------------- 1 | export type Query = { 2 | [P in keyof T]?: T[P] extends never ? Query : Query; 3 | }; 4 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/inputs/CreateWarehouseInput.ts: -------------------------------------------------------------------------------- 1 | export type CreateWarehouseInput = { 2 | name: string; 3 | stateID?: number; 4 | }; 5 | -------------------------------------------------------------------------------- /src/ui/common/config/application/common/auth/models/Authentication.ts: -------------------------------------------------------------------------------- 1 | export class Authentication { 2 | constructor(public readonly token: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": ["**/*.test.ts", "**/*.spec.ts", ".git", "node_modules"], 3 | "watch": ["src"], 4 | "exec": "yarn start", 5 | "ext": "ts" 6 | } 7 | -------------------------------------------------------------------------------- /src/core/applicationServices/Equipment/requests/FetchEquipmentRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchEquipmentRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/requests/FetchWarehouseRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchWarehouseRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/common/mapper/IMapping.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | export interface IMapping { 4 | configureMapping(mapper: Mapper): void; 5 | } 6 | -------------------------------------------------------------------------------- /src/core/domainServices/Role/request/FindRoleRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class FindRoleRepositoryRequest { 2 | constructor(public readonly id: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/State/request/GetStateRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetStateRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/FindUserRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class FindUserRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Portal/Warehouse/graphql/gql/Warehouse.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | availableWarehouses: [Warehouse!]! @isAuthenticated(role: [MEMBER]) 3 | } 4 | 5 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/DeleteUserRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class DeleteUserRepositoryRequest { 2 | constructor(public readonly id: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Portal/Authentication/rest/v1/requests/AuthenticationRequestBody.ts: -------------------------------------------------------------------------------- 1 | export type AuthenticationRequestBody = { 2 | email: string; 3 | password: string; 4 | }; 5 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/requests/FetchUserEquipmentRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchUserEquipmentRequest { 2 | constructor(public readonly userId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/request/GetWarehouseRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetWarehouseRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Portal/Equipment/graphql/inputs/CalculateEquipmentCostInput.ts: -------------------------------------------------------------------------------- 1 | export type CalculateEquipmentCostInput = { 2 | equipmentID: number; 3 | warehouseID: number; 4 | }; 5 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/requests/FetchWarehouseItemRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchWarehouseItemRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/Equipment/request/FindEquipmentRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class FindEquipmentRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/Role/request/FindRoleByNameRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class FindRoleByNameRepositoryRequest { 2 | constructor(public readonly name: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/FindUserByEmailRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class FindUserByEmailRepositoryRequest { 2 | constructor(public readonly email: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/dependency/BaseContainer.ts: -------------------------------------------------------------------------------- 1 | import { Container } from 'inversify'; 2 | 3 | export abstract class BaseContainer extends Container { 4 | public abstract init(): void; 5 | } 6 | -------------------------------------------------------------------------------- /src/ui/common/config/application/express/auth/types/TokenPayload.ts: -------------------------------------------------------------------------------- 1 | import { User } from 'ui/common/models/User'; 2 | 3 | export type TokenPayload = { 4 | user: User; 5 | }; 6 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/requests/FetchWarehouseStateRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchWarehouseStateRequest { 2 | constructor(public readonly warehouseId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/FindUserEquipmentRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class FindUserEquipmentRepositoryRequest { 2 | constructor(public readonly userId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/requests/FetchWarehouseItemsRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchWarehouseItemsRequest { 2 | constructor(public readonly warehouseId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/DeleteUserUnitOfWorkRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class DeleteUserUnitOfWorkRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/requests/GetWarehouseItemRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetWarehouseItemRepositoryRequest { 2 | constructor(public readonly id: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/inputs/UpdateWarehouseInput.ts: -------------------------------------------------------------------------------- 1 | export type UpdateWarehouseInput = { 2 | warehouseID: number; 3 | stateID?: number; 4 | name?: string; 5 | }; 6 | -------------------------------------------------------------------------------- /src/ui/Portal/WarehouseItem/graphql/inputs/AddWarehouseItemInput.ts: -------------------------------------------------------------------------------- 1 | export type AddWarehouseItemInput = { 2 | equipmentID: number; 3 | warehouseID: number; 4 | name?: string; 5 | }; 6 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/request/GetWarehouseStateRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetWarehouseStateRepositoryRequest { 2 | constructor(public readonly warehouseId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/requests/GetWarehouseItemsRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetWarehouseItemsRepositoryRequest { 2 | constructor(public readonly warehouseId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Administration/Equipment/graphql/inputs/CreateEquipmentInput.ts: -------------------------------------------------------------------------------- 1 | export type CreateEquipmentInput = { 2 | name: string; 3 | width: number; 4 | height: number; 5 | depth: number; 6 | }; 7 | -------------------------------------------------------------------------------- /src/ui/Administration/Rate/graphql/gql/Rate.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | rates: [Rate!]! @isAuthenticated(role: [ADMIN]) 3 | } 4 | 5 | type Rate { 6 | id: ID! 7 | value: Float 8 | } 9 | -------------------------------------------------------------------------------- /src/ui/common/config/errors/models/ErrorResponse.ts: -------------------------------------------------------------------------------- 1 | export class ErrorResponse { 2 | constructor( 3 | public readonly code?: string, 4 | public readonly message?: string 5 | ) {} 6 | } 7 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/requests/CreateWarehouseRequest.ts: -------------------------------------------------------------------------------- 1 | export class CreateWarehouseRequest { 2 | constructor(public readonly name: string, public readonly stateID?: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/ui/Administration/State/graphql/gql/State.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | states: [State!] @isAuthenticated(role: [ADMIN]) 3 | } 4 | 5 | type State { 6 | id: ID! 7 | name: String! 8 | } 9 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/requests/FetchWarehouseItemEquipmentRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchWarehouseItemEquipmentRequest { 2 | constructor(public readonly warehouseItemId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/requests/FetchWarehouseItemWarehouseRequest.ts: -------------------------------------------------------------------------------- 1 | export class FetchWarehouseItemWarehouseRequest { 2 | constructor(public readonly warehouseItemId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/request/CreateWarehouseRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class CreateWarehouseRepositoryRequest { 2 | constructor(public readonly name: string, public readonly stateID?: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/requests/GetWarehouseItemEquipmentRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetWarehouseItemEquipmentRepositoryRequest { 2 | constructor(public readonly warehouseItemID: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/requests/GetWarehouseItemWarehouseRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetWarehouseItemWarehouseRepositoryRequest { 2 | constructor(public readonly warehouseItemId: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/dependency/BaseModule.ts: -------------------------------------------------------------------------------- 1 | import { ContainerModule, interfaces } from 'inversify'; 2 | 3 | export abstract class BaseModule extends ContainerModule { 4 | public abstract init(bind: interfaces.Bind): void; 5 | } 6 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_TOKEN_SECRET=abcd 2 | APP_TOKEN_LIFE=24h 3 | APOLLO_BASE_PATH=/graphql 4 | NODE_ENV=development 5 | SWAGGER_BASE_PATH=/api-docs 6 | SWAGGER_HOST=localhost:3000 7 | LOG_LEVEL=debug 8 | ORM_CONNECTION=default 9 | -------------------------------------------------------------------------------- /src/ui/Administration/WarehouseItem/graphql/inputs/CreateWarehouseItemInput.ts: -------------------------------------------------------------------------------- 1 | export type CreateWarehouseItemInput = { 2 | name: string; 3 | cost?: number; 4 | warehouseID: number; 5 | equipmentID: number; 6 | }; 7 | -------------------------------------------------------------------------------- /test/config/mocks/mockRepositoryConnectionName.ts: -------------------------------------------------------------------------------- 1 | import sinon from 'sinon'; 2 | 3 | export const mockRepositoryConnectionName = (testName?: string) => { 4 | sinon.stub(process.env, 'ORM_CONNECTION').value(testName); 5 | }; 6 | -------------------------------------------------------------------------------- /src/core/applicationServices/Authentication/requests/AuthenticationRequest.ts: -------------------------------------------------------------------------------- 1 | export class AuthenticationRequest { 2 | constructor( 3 | public readonly email: string, 4 | public readonly password: string 5 | ) {} 6 | } 7 | -------------------------------------------------------------------------------- /src/core/domainServices/Portal/Warehouse/IWarehouseRepository.ts: -------------------------------------------------------------------------------- 1 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 2 | 3 | export interface IWarehouseRepository { 4 | getAvailableWarehouses(): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /src/core/applicationServices/Portal/Warehouse/IWarehouseService.ts: -------------------------------------------------------------------------------- 1 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 2 | 3 | export interface IWarehouseService { 4 | fetchAvailableWarehouses(): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /src/ui/common/models/User.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | constructor( 3 | public readonly id: number, 4 | public readonly firstName: string, 5 | public readonly email: string, 6 | public readonly role: string 7 | ) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/core/common/errors/BaseError.ts: -------------------------------------------------------------------------------- 1 | export class BaseError implements Error { 2 | constructor( 3 | public readonly code?: string, 4 | public readonly message: string = '', 5 | public readonly name: string = '' 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/GetUserEquipmentRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class GetUserEquipmentRepositoryRequest { 2 | constructor( 3 | public readonly userId: number, 4 | public readonly equipmentId: number 5 | ) {} 6 | } 7 | -------------------------------------------------------------------------------- /src/ui/Administration/WarehouseItem/graphql/inputs/UpdateWarehouseItemInput.ts: -------------------------------------------------------------------------------- 1 | export type UpdateWarehouseItemInput = { 2 | id: number; 3 | name?: string; 4 | cost?: number; 5 | warehouseID?: number; 6 | equipmentID?: number; 7 | }; 8 | -------------------------------------------------------------------------------- /src/ui/Portal/Equipment/rest/v1/requests/CreateEquipmentRequestBody.ts: -------------------------------------------------------------------------------- 1 | export type CreateEquipmentRequestBody = { 2 | readonly name: string; 3 | readonly width: number; 4 | readonly height: number; 5 | readonly depth: number; 6 | }; 7 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/types/Context.ts: -------------------------------------------------------------------------------- 1 | export type Context = { 2 | viewer?: { 3 | id: number; 4 | name: string; 5 | type?: string; 6 | email: string; 7 | }; 8 | claims?: { 9 | role: string; 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/ui/Portal/Authentication/rest/v1/requests/SignupRequestBody.ts: -------------------------------------------------------------------------------- 1 | export type SignUpRequestBody = { 2 | readonly age: number; 3 | readonly email: string; 4 | readonly firstName: string; 5 | readonly lastName: string; 6 | readonly password: string; 7 | }; 8 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/requests/UpdateWarehouseRequest.ts: -------------------------------------------------------------------------------- 1 | export class UpdateWarehouseRequest { 2 | constructor( 3 | public readonly warehouseID: number, 4 | public readonly stateID?: number, 5 | public readonly name?: string 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/ui/common/config/logger/BaseLogger.ts: -------------------------------------------------------------------------------- 1 | export abstract class BaseLogger { 2 | protected readonly logger: T; 3 | 4 | constructor(logger: T) { 5 | this.logger = logger; 6 | } 7 | 8 | public getLogger(): T { 9 | return this.logger; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/infrastructure/common/errors/InfrastructureErrors.ts: -------------------------------------------------------------------------------- 1 | export enum InfrastructureErrors { 2 | USER_NOT_FOUND, 3 | ROLE_NOT_FOUND, 4 | EQUIPMENT_NOT_FOUND, 5 | RATE_NOT_FOUND, 6 | STATE_NOT_FOUND, 7 | WAREHOUSE_NOT_FOUND, 8 | WAREHOUSE_ITEM_NOT_FOUND, 9 | } 10 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/consts/schemaDirectives.ts: -------------------------------------------------------------------------------- 1 | import { IsAuthenticatedDirective } from 'ui/common/config/application/apollo/directives/IsAuthenticatedDirective'; 2 | 3 | export const schemaDirectives = { 4 | isAuthenticated: IsAuthenticatedDirective, 5 | }; 6 | -------------------------------------------------------------------------------- /src/core/applicationServices/Portal/WarehouseItem/requests/AddWarehouseItemRequest.ts: -------------------------------------------------------------------------------- 1 | export class AddWarehouseItemRequest { 2 | constructor( 3 | public readonly equipmentID: number, 4 | public readonly warehouseID: number, 5 | public readonly name?: string 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/core/applicationServices/common/Equipment/interactors/requests/CalculateEquipmentCostInteractorRequest.ts: -------------------------------------------------------------------------------- 1 | export class CalculateEquipmentCostInteractorRequest { 2 | constructor( 3 | public readonly warehouseId: number, 4 | public readonly equipmentId: number 5 | ) {} 6 | } 7 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/request/UpdateWarehouseRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class UpdateWarehouseRepositoryRequest { 2 | constructor( 3 | public readonly warehouseID: number, 4 | public readonly stateID?: number, 5 | public readonly name?: string 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/auth/types/GraphQLTokenPayload.ts: -------------------------------------------------------------------------------- 1 | export type GraphQLTokenPayload = { 2 | claims?: { 3 | role: string; 4 | }; 5 | viewer: { 6 | id: number; 7 | name: string; 8 | email: string; 9 | type?: string; 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/requests/CalculateUserEquipmentCostRequest.ts: -------------------------------------------------------------------------------- 1 | export class CalculateUserEquipmentCostRequest { 2 | constructor( 3 | public readonly equipmentId: number, 4 | public readonly warehouseId: number, 5 | public readonly userId: number 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/common/IApolloContext.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express'; 2 | 3 | import { Context } from 'ui/common/config/application/apollo/types/Context'; 4 | 5 | export interface IApolloContext { 6 | context: ({ req }: { req: Request }) => Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domain/Equipment/Equipment.ts: -------------------------------------------------------------------------------- 1 | export class Equipment { 2 | constructor( 3 | public readonly id: number, 4 | public readonly width: number, 5 | public readonly height: number, 6 | public readonly depth: number, 7 | public readonly name: string 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/ui/Portal/WarehouseItem/graphql/gql/WarehouseItem.graphql: -------------------------------------------------------------------------------- 1 | extend type Mutation { 2 | addWarehouseItem(input: AddWarehouseItem): WarehouseItem! @isAuthenticated(role: [MEMBER]) 3 | } 4 | 5 | input AddWarehouseItem { 6 | equipmentID: ID! 7 | warehouseID: ID! 8 | name: String 9 | } 10 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/requests/CreateWarehouseItemRequest.ts: -------------------------------------------------------------------------------- 1 | export class CreateWarehouseItemRequest { 2 | constructor( 3 | public readonly name: string, 4 | public readonly warehouseID: number, 5 | public readonly equipmentID: number, 6 | public readonly cost?: number 7 | ) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/core/applicationServices/Rate/IRateService.ts: -------------------------------------------------------------------------------- 1 | import { Rate } from 'core/domain/Rate/Rate'; 2 | import { FetchRateRequest } from 'core/applicationServices/Rate/requests/FetchRateRequest'; 3 | 4 | export interface IRateService { 5 | fetchRate(request: FetchRateRequest): Promise; 6 | fetchRates(): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/requests/CreateWarehouseItemRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class CreateWarehouseItemRepositoryRequest { 2 | constructor( 3 | public readonly name: string, 4 | public readonly cost: number, 5 | public readonly warehouseID: number, 6 | public readonly equipmentID: number 7 | ) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/ui/shared/Authentication/graphql/gql/Authentication.graphql: -------------------------------------------------------------------------------- 1 | extend type Mutation { 2 | authenticate(input: AuthenticateInput): Authentication! 3 | } 4 | 5 | input AuthenticateInput { 6 | email: String! 7 | password: String! 8 | } 9 | 10 | type Authentication { 11 | token: String! 12 | } 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/core/domainServices/Portal/WarehouseItem/requests/AddWarehouseItemRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class AddWarehouseItemRepositoryRequest { 2 | constructor( 3 | public readonly equipmentID: number, 4 | public readonly warehouseID: number, 5 | public readonly cost: number, 6 | public readonly name?: string 7 | ) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/core/applicationServices/Authentication/requests/SignUpRequest.ts: -------------------------------------------------------------------------------- 1 | export class SignUpRequest { 2 | constructor( 3 | public readonly firstName: string, 4 | public readonly email: string, 5 | public readonly lastName: string, 6 | public readonly password: string, 7 | public readonly age: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/core/applicationServices/State/IStateService.ts: -------------------------------------------------------------------------------- 1 | import { State } from 'core/domain/State/State'; 2 | import { FetchStateRequest } from 'core/applicationServices/State/requests/FetchStateRequest'; 3 | 4 | export interface IStateService { 5 | fetchState(request: FetchStateRequest): Promise; 6 | fetchStates(): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domainServices/Rate/IRateRepository.ts: -------------------------------------------------------------------------------- 1 | import { GetRateRepositoryRequest } from 'core/domainServices/Rate/request/GetRateRepositoryRequest'; 2 | import { Rate } from 'core/domain/Rate/Rate'; 3 | 4 | export interface IRateRepository { 5 | getRate(request: GetRateRepositoryRequest): Promise; 6 | getRates(): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/ui/common/config/application/common/BaseApplication.ts: -------------------------------------------------------------------------------- 1 | export abstract class BaseApplication { 2 | protected readonly app: T; 3 | 4 | protected constructor(app: T) { 5 | this.app = app; 6 | } 7 | 8 | public abstract initialize(): void; 9 | 10 | public getApplication(): T { 11 | return this.app; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/config/helpers/prepareTestTransaction.ts: -------------------------------------------------------------------------------- 1 | import { 2 | initializeTransactionalContext, 3 | patchTypeORMRepositoryWithBaseRepository, 4 | } from 'typeorm-transactional-cls-hooked'; 5 | 6 | export const prepareTestTransaction = () => { 7 | initializeTransactionalContext(); 8 | patchTypeORMRepositoryWithBaseRepository(); 9 | }; 10 | -------------------------------------------------------------------------------- /src/core/applicationServices/Equipment/requests/CreateEquipmentRequest.ts: -------------------------------------------------------------------------------- 1 | export class CreateEquipmentRequest { 2 | constructor( 3 | public readonly name: string, 4 | public readonly width: number, 5 | public readonly height: number, 6 | public readonly depth: number, 7 | public readonly userId: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/core/domainServices/Equipment/request/AddEquipmentRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class AddEquipmentRepositoryRequest { 2 | constructor( 3 | public readonly name: string, 4 | public readonly width: number, 5 | public readonly height: number, 6 | public readonly depth: number, 7 | public readonly userId: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/core/domainServices/State/IStateRepository.ts: -------------------------------------------------------------------------------- 1 | import { GetStateRepositoryRequest } from 'core/domainServices/State/request/GetStateRepositoryRequest'; 2 | import { State } from 'core/domain/State/State'; 3 | 4 | export interface IStateRepository { 5 | getState(request: GetStateRepositoryRequest): Promise; 6 | getStates(): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/IWarehouseStateService.ts: -------------------------------------------------------------------------------- 1 | import { State } from 'core/domain/State/State'; 2 | import { FetchWarehouseStateRequest } from 'core/applicationServices/Warehouse/requests/FetchWarehouseStateRequest'; 3 | 4 | export interface IWarehouseStateService { 5 | fetchState: (request: FetchWarehouseStateRequest) => Promise; 6 | } 7 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/requests/UpdateWarehouseItemRequest.ts: -------------------------------------------------------------------------------- 1 | export class UpdateWarehouseItemRequest { 2 | constructor( 3 | public readonly id: number, 4 | public readonly name?: string, 5 | public readonly cost?: number, 6 | public readonly warehouseID?: number, 7 | public readonly equipmentID?: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/core/domain/User/User.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | constructor( 3 | public readonly id: number, 4 | public readonly firstName: string, 5 | public readonly email: string, 6 | public readonly role: string, 7 | public readonly lastName: string, 8 | public readonly password: string, 9 | public readonly age: number 10 | ) {} 11 | } 12 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/AddUserUnitOfWorkRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class AddUserUnitOfWorkRepositoryRequest { 2 | constructor( 3 | public readonly firstName: string, 4 | public readonly email: string, 5 | public readonly lastName: string, 6 | public readonly password: string, 7 | public readonly age: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/common/UpdateQueryData.ts: -------------------------------------------------------------------------------- 1 | export type UpdateQueryData = { 2 | [P in keyof T]?: 3 | | (T[P] extends Array 4 | ? Array> 5 | : T[P] extends ReadonlyArray 6 | ? ReadonlyArray> 7 | : UpdateQueryData) 8 | | (() => string); 9 | }; 10 | -------------------------------------------------------------------------------- /src/core/domain/State/State.ts: -------------------------------------------------------------------------------- 1 | import { Rate } from 'core/domain/Rate/Rate'; 2 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 3 | 4 | export class State { 5 | constructor( 6 | public readonly id: number, 7 | public readonly name: string, 8 | public readonly rates: Rate[], 9 | public readonly warehouses: Warehouse[] 10 | ) {} 11 | } 12 | -------------------------------------------------------------------------------- /src/core/domainServices/Equipment/request/AddEquipmentUnitOfWorkRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class AddEquipmentUnitOfWorkRepositoryRequest { 2 | constructor( 3 | public readonly name: string, 4 | public readonly width: number, 5 | public readonly height: number, 6 | public readonly depth: number, 7 | public readonly userId: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/IWarehouseStateRepository.ts: -------------------------------------------------------------------------------- 1 | import { State } from 'core/domain/State/State'; 2 | import { GetWarehouseStateRepositoryRequest } from 'core/domainServices/Warehouse/request/GetWarehouseStateRepositoryRequest'; 3 | 4 | export interface IWarehouseStateRepository { 5 | getState: (request: GetWarehouseStateRepositoryRequest) => Promise; 6 | } 7 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/requests/UpdateWarehouseItemRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class UpdateWarehouseItemRepositoryRequest { 2 | constructor( 3 | public readonly id: number, 4 | public readonly name?: string, 5 | public readonly cost?: number, 6 | public readonly warehouseID?: number, 7 | public readonly equipmentID?: number 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /test/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "diff": true, 3 | "exit": true, 4 | "recursive": true, 5 | "reporter": "spec", 6 | "require": [ 7 | "dotenv/config", 8 | "ts-node/register", 9 | "tsconfig-paths/register" 10 | ], 11 | "extension": [ 12 | "ts" 13 | ], 14 | "spec": "test/**/*.spec.ts", 15 | "slow": 75, 16 | "timeout": 10000, 17 | "ui": "bdd" 18 | } 19 | -------------------------------------------------------------------------------- /src/core/domainServices/User/request/AddUserRepositoryRequest.ts: -------------------------------------------------------------------------------- 1 | export class AddUserRepositoryRequest { 2 | constructor( 3 | public readonly firstName: string, 4 | public readonly email: string, 5 | public readonly lastName: string, 6 | public readonly password: string, 7 | public readonly age: number, 8 | public readonly roleId: number 9 | ) {} 10 | } 11 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/StateFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | 3 | import Faker from 'faker'; 4 | 5 | import { State } from 'infrastructure/database/entities/State'; 6 | 7 | define(State, (faker: typeof Faker) => { 8 | const state = new State(); 9 | 10 | state.name = faker.address.state(); 11 | 12 | return state; 13 | }); 14 | -------------------------------------------------------------------------------- /src/ui/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description - required by inversify to have metadata for @controller annotation 3 | */ 4 | export { AuthenticationController } from 'ui/Portal/Authentication/rest/v1/AuthenticationController'; 5 | export { EquipmentController } from 'ui/Portal/Equipment/rest/v1/EquipmentController'; 6 | export { UserController } from 'ui/Portal/User/rest/v1/UserController'; 7 | -------------------------------------------------------------------------------- /src/core/applicationServices/Portal/WarehouseItem/IWarehouseItemService.ts: -------------------------------------------------------------------------------- 1 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 2 | import { AddWarehouseItemRequest } from 'core/applicationServices/Portal/WarehouseItem/requests/AddWarehouseItemRequest'; 3 | 4 | export interface IWarehouseItemService { 5 | addWarehouseItem(request: AddWarehouseItemRequest): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/RateFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | 3 | import Faker from 'faker'; 4 | 5 | import { Rate } from 'infrastructure/database/entities/Rate'; 6 | 7 | define(Rate, (faker: typeof Faker) => { 8 | const state = new Rate(); 9 | 10 | state.value = faker.random.number({ min: 0, max: 10, precision: 5 }); 11 | 12 | return state; 13 | }); 14 | -------------------------------------------------------------------------------- /src/ui/common/config/application/common/auth/IAuthenticationHandler.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticationRequest } from 'core/applicationServices/Authentication/requests/AuthenticationRequest'; 2 | import { Authentication } from 'ui/common/config/application/common/auth/models/Authentication'; 3 | 4 | export interface IAuthenticationHandler { 5 | authenticate(request: AuthenticationRequest): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /src/core/domainServices/Equipment/IEquipmentUnitOfWork.ts: -------------------------------------------------------------------------------- 1 | import { AddEquipmentUnitOfWorkRepositoryRequest } from 'core/domainServices/Equipment/request/AddEquipmentUnitOfWorkRepositoryRequest'; 2 | import { Equipment } from 'core/domain/Equipment/Equipment'; 3 | 4 | export interface IEquipmentUnitOfWork { 5 | addEquipment( 6 | request: AddEquipmentUnitOfWorkRepositoryRequest 7 | ): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/ui/common/config/errors/UserInterfaceError.ts: -------------------------------------------------------------------------------- 1 | import { BaseError } from 'core/common/errors/BaseError'; 2 | 3 | export class UserInterfaceError extends BaseError { 4 | constructor( 5 | public readonly status: number, 6 | public readonly code?: string, 7 | public readonly message: string = '', 8 | public readonly name: string = '' 9 | ) { 10 | super(code, message, name); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "core/*": ["src/core/*"], 7 | "dependency/*": ["src/dependency/*"], 8 | "infrastructure/*": ["src/infrastructure/*"], 9 | "ui/*": ["src/ui/*"], 10 | "config/*": ["test/config/*"], 11 | "*": [ 12 | "@types/*" 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ui/Portal/Equipment/graphql/gql/Equipment.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | myEquipment: [Equipment!]! @isAuthenticated(role: [MEMBER]) 3 | calculateEquipmentCost(input: CalculateEquipmentCostInput): EquipmentCost! @isAuthenticated(role: [MEMBER]) 4 | } 5 | 6 | input CalculateEquipmentCostInput { 7 | equipmentID: ID! 8 | warehouseID: ID! 9 | } 10 | 11 | type EquipmentCost { 12 | cost: Float! 13 | } 14 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/IWarehouseWarehouseItemService.ts: -------------------------------------------------------------------------------- 1 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 2 | import { FetchWarehouseItemsRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemsRequest'; 3 | 4 | export interface IWarehouseWarehouseItemService { 5 | fetchWarehouseItems: ( 6 | request: FetchWarehouseItemsRequest 7 | ) => Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/ui/common/config/application/express/utils/unless.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from 'express'; 2 | 3 | // SOURCE: https://stackoverflow.com/a/51981393 4 | export const unless = (path: string, middleware: any) => { 5 | return (req: Request, res: Response, next: NextFunction) => { 6 | if (path === req.path) { 7 | return next(); 8 | } 9 | return middleware(req, res, next); 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/IWarehouseItemEquipmentService.ts: -------------------------------------------------------------------------------- 1 | import { Equipment } from 'core/domain/Equipment/Equipment'; 2 | import { FetchWarehouseItemEquipmentRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemEquipmentRequest'; 3 | 4 | export interface IWarehouseItemEquipmentService { 5 | fetchEquipment( 6 | request: FetchWarehouseItemEquipmentRequest 7 | ): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/IWarehouseItemWarehouseService.ts: -------------------------------------------------------------------------------- 1 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 2 | import { FetchWarehouseItemWarehouseRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemWarehouseRequest'; 3 | 4 | export interface IWarehouseItemWarehouseService { 5 | fetchWarehouse( 6 | request: FetchWarehouseItemWarehouseRequest 7 | ): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/core/domainServices/Portal/WarehouseItem/IWarehouseItemRepository.ts: -------------------------------------------------------------------------------- 1 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 2 | import { AddWarehouseItemRepositoryRequest } from 'core/domainServices/Portal/WarehouseItem/requests/AddWarehouseItemRepositoryRequest'; 3 | 4 | export interface IWarehouseItemRepository { 5 | addWarehouseItem( 6 | request: AddWarehouseItemRepositoryRequest 7 | ): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/core/domain/Warehouse/WarehouseItem.ts: -------------------------------------------------------------------------------- 1 | import { Equipment } from 'core/domain/Equipment/Equipment'; 2 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 3 | 4 | export class WarehouseItem { 5 | constructor( 6 | public readonly id: number, 7 | public readonly name: string, 8 | public readonly cost: number, 9 | public readonly warehouse: Warehouse, 10 | public readonly equipment: Equipment 11 | ) {} 12 | } 13 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/IWarehouseWarehouseItemRepository.ts: -------------------------------------------------------------------------------- 1 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 2 | import { GetWarehouseItemsRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemsRepositoryRequest'; 3 | 4 | export interface IWarehouseWarehouseItemRepository { 5 | getWarehouseItems: ( 6 | request: GetWarehouseItemsRepositoryRequest 7 | ) => Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/ui/Administration/User/graphql/gql/User.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | users: [User!]! @isAuthenticated(role: [ADMIN]) 3 | } 4 | 5 | extend type Mutation { 6 | removeUser(input: RemoveUserInput): Boolean 7 | } 8 | 9 | input RemoveUserInput { 10 | id: ID! 11 | } 12 | 13 | type User { 14 | id: ID! 15 | firstName: String! 16 | email: String! 17 | role: String! 18 | lastName: String! 19 | age: Int! 20 | } 21 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/IWarehouseItemEquipmentRepository.ts: -------------------------------------------------------------------------------- 1 | import { GetWarehouseItemEquipmentRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemEquipmentRepositoryRequest'; 2 | import { Equipment } from 'core/domain/Equipment/Equipment'; 3 | 4 | export interface IWarehouseItemEquipmentRepository { 5 | getEquipment( 6 | request: GetWarehouseItemEquipmentRepositoryRequest 7 | ): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/IWarehouseItemWarehouseRepository.ts: -------------------------------------------------------------------------------- 1 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 2 | import { GetWarehouseItemWarehouseRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemWarehouseRepositoryRequest'; 3 | 4 | export interface IWarehouseItemWarehouseRepository { 5 | getWarehouse( 6 | request: GetWarehouseItemWarehouseRepositoryRequest 7 | ): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/ui/shared/common/graphql/SharedQuery.ts: -------------------------------------------------------------------------------- 1 | import { injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | @injectable() 8 | export class SharedQuery implements IResolver { 9 | readonly resolvers: IResolverObject; 10 | 11 | constructor() { 12 | this.resolvers = {}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ui/Administration/User/graphql/UserSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { IResolverObject } from 'apollo-server-express'; 2 | 3 | import { injectable } from 'inversify'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | @injectable() 8 | export class UserSubQuery implements IResolver { 9 | readonly resolvers: IResolverObject; 10 | 11 | constructor() { 12 | this.resolvers = {}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ui/Portal/common/graphql/PortalSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | @injectable() 8 | export class PortalSubQuery implements IResolver { 9 | readonly resolvers: IResolverObject; 10 | 11 | constructor() { 12 | this.resolvers = {}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ui/shared/common/graphql/SharedSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | @injectable() 8 | export class SharedSubQuery implements IResolver { 9 | readonly resolvers: IResolverObject; 10 | 11 | constructor() { 12 | this.resolvers = {}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/config/helpers/getCurrentConnection.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | createConnection, 4 | getConnection, 5 | getConnectionManager, 6 | } from 'typeorm'; 7 | 8 | export const getCurrentConnection = async (): Promise => { 9 | if (!getConnectionManager().has(process.env.ORM_CONNECTION || '')) { 10 | await createConnection(process.env.ORM_CONNECTION || ''); 11 | } 12 | return getConnection(process.env.ORM_CONNECTION); 13 | }; 14 | -------------------------------------------------------------------------------- /src/ui/Administration/Equipment/graphql/EquipmentSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { IResolverObject } from 'apollo-server-express'; 2 | 3 | import { injectable } from 'inversify'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | @injectable() 8 | export class EquipmentSubQuery implements IResolver { 9 | readonly resolvers: IResolverObject; 10 | 11 | constructor() { 12 | this.resolvers = {}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/RoleFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | import * as Faker from 'faker'; 3 | 4 | import { Role } from 'infrastructure/database/entities/Role'; 5 | import { USER_ROLE } from 'infrastructure/database/enum/UserRole'; 6 | 7 | define(Role, (faker: typeof Faker) => { 8 | const role = new Role(); 9 | 10 | role.name = faker.random.arrayElement([USER_ROLE.ADMIN, USER_ROLE.MEMBER]); 11 | 12 | return role; 13 | }); 14 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/IUserService.ts: -------------------------------------------------------------------------------- 1 | import { RemoveUserRequest } from 'core/applicationServices/User/requests/RemoveUserReuqest'; 2 | import { User } from 'core/domain/User/User'; 3 | import { FetchUserRequest } from 'core/applicationServices/User/requests/FetchUserRequest'; 4 | 5 | export interface IUserService { 6 | fetchUser(request: FetchUserRequest): Promise; 7 | fetchUsers(): Promise; 8 | removeUser(request: RemoveUserRequest): Promise; 9 | } 10 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/Rate.ts: -------------------------------------------------------------------------------- 1 | import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm'; 2 | 3 | import { State } from 'infrastructure/database/entities/State'; 4 | 5 | @Entity() 6 | export class Rate { 7 | @PrimaryGeneratedColumn() 8 | id!: number; 9 | 10 | @Column({ type: 'decimal', precision: 3, scale: 1 }) 11 | value!: number; 12 | 13 | @ManyToMany( 14 | () => State, 15 | state => state.rates 16 | ) 17 | states!: State[]; 18 | } 19 | -------------------------------------------------------------------------------- /src/core/domainServices/Role/IRoleRepository.ts: -------------------------------------------------------------------------------- 1 | import { Role } from 'core/domain/Role/Role'; 2 | import { FindRoleRepositoryRequest } from 'core/domainServices/Role/request/FindRoleRepositoryRequest'; 3 | import { FindRoleByNameRepositoryRequest } from 'core/domainServices/Role/request/FindRoleByNameRepositoryRequest'; 4 | 5 | export interface IRoleRepository { 6 | findRole(request: FindRoleRepositoryRequest): Promise; 7 | findRoleByName(request: FindRoleByNameRepositoryRequest): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /test/config/helpers/getPostgresConnection.ts: -------------------------------------------------------------------------------- 1 | import pgPromise, { IDatabase } from 'pg-promise'; 2 | import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions'; 3 | 4 | export const getPostgresConnection = ({ 5 | database, 6 | port, 7 | username, 8 | password, 9 | }: PostgresConnectionOptions): IDatabase<{}> => { 10 | const pgp = pgPromise(); 11 | 12 | return pgp({ 13 | database, 14 | port, 15 | user: username, 16 | password, 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /src/core/applicationServices/Authentication/IAuthenticationService.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticationRequest } from 'core/applicationServices/Authentication/requests/AuthenticationRequest'; 2 | import { User } from 'core/domain/User/User'; 3 | import { SignUpRequest } from 'core/applicationServices/Authentication/requests/SignUpRequest'; 4 | 5 | export interface IAuthenticationService { 6 | signUp(request: SignUpRequest): Promise; 7 | verifyCredentials(request: AuthenticationRequest): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/dependency/shared/Warehouse/WarehouseModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | 5 | // TODO Remove it if not used in future 6 | export class WarehouseModule extends BaseModule { 7 | constructor() { 8 | super((bind: interfaces.Bind): void => { 9 | this.init(bind); 10 | }); 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-empty-function,no-unused-vars 14 | public init(bind: interfaces.Bind): void {} 15 | } 16 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/seeds/RateSeed.ts: -------------------------------------------------------------------------------- 1 | import { Factory, Seeder, times } from 'typeorm-seeding'; 2 | 3 | import { Rate } from 'infrastructure/database/entities/Rate'; 4 | 5 | export class RateSeed implements Seeder { 6 | async run(factory: Factory): Promise { 7 | await this.prepareRateSeeds(factory); 8 | } 9 | 10 | private async prepareRateSeeds(factory: Factory): Promise { 11 | await times(5, async () => { 12 | await factory(Rate)().create(); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/seeds/StateSeed.ts: -------------------------------------------------------------------------------- 1 | import { Factory, Seeder, times } from 'typeorm-seeding'; 2 | 3 | import { State } from 'infrastructure/database/entities/State'; 4 | 5 | export class StateSeed implements Seeder { 6 | async run(factory: Factory): Promise { 7 | await this.prepareStateSeeds(factory); 8 | } 9 | 10 | private async prepareStateSeeds(factory: Factory): Promise { 11 | await times(5, async () => { 12 | await factory(State)().create(); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/config/helpers/inTransaction.ts: -------------------------------------------------------------------------------- 1 | import { RollbackException } from 'config/db/RollbackException'; 2 | import { RunFunction } from 'config/types/RunFunction'; 3 | import { TransactionCreator } from 'config/db/TransactionCreator'; 4 | 5 | export const inTransaction = (func: RunFunction) => { 6 | return async () => { 7 | try { 8 | await TransactionCreator.run(func); 9 | } catch (e) { 10 | if (e instanceof RollbackException) { 11 | } else { 12 | throw e; 13 | } 14 | } 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /src/core/domainServices/User/IUserUnitOfWork.ts: -------------------------------------------------------------------------------- 1 | import { User } from 'core/domain/User/User'; 2 | import { AddUserUnitOfWorkRepositoryRequest } from 'core/domainServices/User/request/AddUserUnitOfWorkRepositoryRequest'; 3 | import { DeleteUserUnitOfWorkRepositoryRequest } from 'core/domainServices/User/request/DeleteUserUnitOfWorkRepositoryRequest'; 4 | 5 | export interface IUserUnitOfWork { 6 | addUser(request: AddUserUnitOfWorkRepositoryRequest): Promise; 7 | deleteUser(request: DeleteUserUnitOfWorkRepositoryRequest): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/WarehouseItemFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | import Faker from 'faker'; 3 | 4 | import { WarehouseItem } from 'infrastructure/database/entities/WarehouseItem'; 5 | 6 | const RANDOM_NUMBER_SIZE = 100; 7 | 8 | define(WarehouseItem, (faker: typeof Faker) => { 9 | const counter = faker.random.number(RANDOM_NUMBER_SIZE); 10 | const warehouseItem = new WarehouseItem(); 11 | 12 | warehouseItem.name = `Secret Case ${counter}`; 13 | 14 | return warehouseItem; 15 | }); 16 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/seeds/WarehouseSeed.ts: -------------------------------------------------------------------------------- 1 | import { Factory, Seeder, times } from 'typeorm-seeding'; 2 | 3 | import { Warehouse } from 'infrastructure/database/entities/Warehouse'; 4 | 5 | export class WarehouseSeed implements Seeder { 6 | async run(factory: Factory): Promise { 7 | await this.prepareWarehouseSeeds(factory); 8 | } 9 | 10 | private async prepareWarehouseSeeds(factory: Factory): Promise { 11 | await times(5, async () => { 12 | await factory(Warehouse)().create(); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### RELEASE v1.1.0 2 | 3 | Release which includes following features: 4 | 5 | 1. Introduced Unit Of Work Pattern 6 | 2. Maintenance related patches and bugfixes 7 | 8 | ### RELEASE v1.0.0 9 | 10 | Release which includes following features: 11 | 12 | 1. Multiple environment setup 13 | 2. DB Agnostic setup, supports multiple datasource 14 | 3. Infrastructure -> Domain Mapping -> UI Mapping 15 | 4. Migrations, Fixtures, Seeds 16 | 5. Multiple API versions support ( REST implementation ) 17 | 6. Global Error Handling 18 | 7. Test Parallelization 19 | -------------------------------------------------------------------------------- /src/core/applicationServices/Equipment/IEquipmentService.ts: -------------------------------------------------------------------------------- 1 | import { CreateEquipmentRequest } from 'core/applicationServices/Equipment/requests/CreateEquipmentRequest'; 2 | import { Equipment } from 'core/domain/Equipment/Equipment'; 3 | import { FetchEquipmentRequest } from 'core/applicationServices/Equipment/requests/FetchEquipmentRequest'; 4 | 5 | export interface IEquipmentService { 6 | createEquipment(request: CreateEquipmentRequest): Promise; 7 | fetchEquipment(request: FetchEquipmentRequest): Promise; 8 | fetchAllEquipment(): Promise; 9 | } 10 | -------------------------------------------------------------------------------- /src/ui/Administration/Equipment/graphql/gql/Equipment.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | equipment: [Equipment!]! @isAuthenticated(role: [ADMIN]) 3 | } 4 | 5 | extend type Mutation { 6 | createEquipment(input: CreateEquipmentInput): Equipment! @isAuthenticated(role: [ADMIN]) # TODO add assigning equipment to user 7 | } 8 | 9 | input CreateEquipmentInput { 10 | name: String! 11 | width: Int! 12 | height: Int! 13 | depth: Int! 14 | } 15 | 16 | type Equipment { 17 | id: ID! 18 | name: String! 19 | width: Int! 20 | height: Int! 21 | depth: Int! 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1611436650648-create_rate.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createRate1611436650648 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query( 6 | `CREATE TABLE "rate" ("id" SERIAL NOT NULL, "value" numeric(3,1) NOT NULL, CONSTRAINT "PK_2618d0d38af322d152ccc328f33" PRIMARY KEY ("id"))` 7 | ); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`DROP TABLE "rate"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/core/domainServices/User/IUserEquipmentRepository.ts: -------------------------------------------------------------------------------- 1 | import { GetUserEquipmentRepositoryRequest } from 'core/domainServices/User/request/GetUserEquipmentRepositoryRequest'; 2 | import { FindUserEquipmentRepositoryRequest } from 'core/domainServices/User/request/FindUserEquipmentRepositoryRequest'; 3 | import { Equipment } from 'core/domain/Equipment/Equipment'; 4 | 5 | export interface IUserEquipmentRepository { 6 | getEquipment(request: GetUserEquipmentRepositoryRequest): Promise; 7 | findEquipment( 8 | request: FindUserEquipmentRepositoryRequest 9 | ): Promise; 10 | } 11 | -------------------------------------------------------------------------------- /src/core/domainServices/Equipment/IEquipmentRepository.ts: -------------------------------------------------------------------------------- 1 | import { Equipment } from 'core/domain/Equipment/Equipment'; 2 | import { FindEquipmentRepositoryRequest } from 'core/domainServices/Equipment/request/FindEquipmentRepositoryRequest'; 3 | import { AddEquipmentRepositoryRequest } from 'core/domainServices/Equipment/request/AddEquipmentRepositoryRequest'; 4 | 5 | export interface IEquipmentRepository { 6 | addEquipment(request: AddEquipmentRepositoryRequest): Promise; 7 | getEquipment(): Promise; 8 | findEquipment(request: FindEquipmentRepositoryRequest): Promise; 9 | } 10 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1611436598116-create_state.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createState1611436598116 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query( 6 | `CREATE TABLE "state" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_549ffd046ebab1336c3a8030a12" PRIMARY KEY ("id"))` 7 | ); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`DROP TABLE "state"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/Role.ts: -------------------------------------------------------------------------------- 1 | import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; 2 | 3 | import { User } from 'infrastructure/database/entities/User'; 4 | import { USER_ROLE } from 'infrastructure/database/enum/UserRole'; 5 | 6 | @Entity() 7 | export class Role { 8 | @PrimaryGeneratedColumn() 9 | id!: number; 10 | 11 | @Column({ 12 | default: USER_ROLE.MEMBER, 13 | enum: USER_ROLE, 14 | nullable: false, 15 | type: 'enum', 16 | }) 17 | name!: string; 18 | 19 | @OneToMany( 20 | () => User, 21 | user => user.role 22 | ) 23 | user!: User; 24 | } 25 | -------------------------------------------------------------------------------- /src/ui/common/config/consts/variables.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | 3 | dotenv.config(); 4 | 5 | export const APP_TOKEN_SECRET = process.env.APP_TOKEN_SECRET || ''; 6 | export const APP_TOKEN_LIFE = process.env.APP_TOKEN_LIFE || '24h'; 7 | export const PORT = process.env.PORT || '3000'; 8 | export const LOG_LEVEL = process.env.LOG_LEVEL || 'debug'; 9 | export const SWAGGER_BASE_PATH = process.env.SWAGGER_BASE_PATH || ''; 10 | export const APOLLO_BASE_PATH = process.env.APOLLO_BASE_PATH || ''; 11 | export const { SWAGGER_HOST } = process.env; 12 | export const IS_DEVELOPMENT = process.env.NODE_ENV === 'development'; 13 | -------------------------------------------------------------------------------- /src/infrastructure/database/orm/OnionOrm.ts: -------------------------------------------------------------------------------- 1 | import { injectable } from 'inversify'; 2 | import { createConnection } from 'typeorm'; 3 | 4 | import { 5 | initializeTransactionalContext, 6 | patchTypeORMRepositoryWithBaseRepository, 7 | } from 'typeorm-transactional-cls-hooked'; 8 | 9 | import { IOrm } from 'infrastructure/database/orm/IOrm'; 10 | 11 | @injectable() 12 | export class OnionOrm implements IOrm { 13 | public async initialize(): Promise { 14 | await createConnection(process.env.ORM_CONNECTION || ''); 15 | 16 | initializeTransactionalContext(); 17 | patchTypeORMRepositoryWithBaseRepository(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/config/db/TransactionCreator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsolationLevel, 3 | Propagation, 4 | Transactional, 5 | } from 'typeorm-transactional-cls-hooked'; 6 | 7 | import { RollbackException } from 'config/db/RollbackException'; 8 | 9 | import { RunFunction } from 'config/types/RunFunction'; 10 | 11 | export class TransactionCreator { 12 | @Transactional({ 13 | connectionName: () => process.env.ORM_CONNECTION, 14 | propagation: Propagation.NESTED, 15 | isolationLevel: IsolationLevel.SERIALIZABLE, 16 | }) 17 | static async run(func: RunFunction) { 18 | await func(); 19 | throw new RollbackException(`Transaction rollback`); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/IUserEquipmentService.ts: -------------------------------------------------------------------------------- 1 | import { Equipment } from 'core/domain/Equipment/Equipment'; 2 | import { FetchUserEquipmentRequest } from 'core/applicationServices/User/requests/FetchUserEquipmentRequest'; 3 | import { CalculateUserEquipmentCostRequest } from 'core/applicationServices/User/requests/CalculateUserEquipmentCostRequest'; 4 | import { EquipmentCost } from 'core/domain/Equipment/EquipmentCost'; 5 | 6 | export interface IUserEquipmentService { 7 | fetchEquipment(request: FetchUserEquipmentRequest): Promise; 8 | calculateEquipmentCost( 9 | request: CalculateUserEquipmentCostRequest 10 | ): Promise; 11 | } 12 | -------------------------------------------------------------------------------- /stryker.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | mutator: 'typescript', 4 | packageManager: 'yarn', 5 | reporters: ['progress'], 6 | testRunner: 'mocha', 7 | transpilers: [ 8 | 'typescript' 9 | ], 10 | files: [ 11 | "ormconfig.js", 12 | "src/**/*.ts", 13 | "test/**/*.ts", 14 | ".env.test" 15 | ], 16 | fileLogLevel: 'debug', 17 | testFramework: 'mocha', 18 | coverageAnalysis: 'perTest', 19 | tsconfigFile: 'tsconfig.json', 20 | mutate: ['src/**/*.ts'], 21 | mochaOptions: { 22 | spec: [ 'test/**/*.spec.ts' ], 23 | config: 'test/.mocharc.json', 24 | } 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /src/ui/common/mappings/UIMapper.ts: -------------------------------------------------------------------------------- 1 | import { injectable } from 'inversify'; 2 | import { Mapper } from '@wufe/mapper'; 3 | 4 | import { UserDomainToUserUI } from 'ui/common/mappings/User/UserDomainToUserUI'; 5 | 6 | @injectable() 7 | export class UIMapper { 8 | public readonly mapper: Mapper; 9 | 10 | constructor() { 11 | this.mapper = new Mapper().withConfiguration(configuration => 12 | configuration 13 | .shouldIgnoreSourcePropertiesIfNotInDestination(true) 14 | .shouldAutomaticallyMapArrays(true) 15 | ); 16 | 17 | this.initialize(); 18 | } 19 | 20 | private initialize(): void { 21 | UserDomainToUserUI().configureMapping(this.mapper); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/infrastructure/InfrastructureModuleSymbols.ts: -------------------------------------------------------------------------------- 1 | export const INFRASTRUCTURE_IDENTIFIERS = { 2 | DB_MAPPER: Symbol.for('DBMapper'), 3 | }; 4 | 5 | export const DATABASE_IDENTIFIERS = { 6 | ORM: Symbol.for('Orm'), 7 | }; 8 | 9 | export const DATABASE_MAPPING_IDENTIFIERS = { 10 | EQUIPMENT_ENTITY: Symbol.for('EquipmentEntity'), 11 | EQUIPMENT_STATE_RATE_ENTITY: Symbol.for('EquipmentStateRate'), 12 | ROLE_ENTITY: Symbol.for('RoleEntity'), 13 | USER_ENTITY: Symbol.for('UserEntity'), 14 | RATE_ENTITY: Symbol.for('StateEntity'), 15 | STATE_ENTITY: Symbol.for('StateEntity'), 16 | WAREHOUSE_ENTITY: Symbol.for('WarehouseEntity'), 17 | WAREHOUSE_ITEM_ENTITY: Symbol.for('WarehouseItemEntity'), 18 | }; 19 | -------------------------------------------------------------------------------- /src/core/domainServices/User/IUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from 'core/domain/User/User'; 2 | import { FindUserByEmailRepositoryRequest } from 'core/domainServices/User/request/FindUserByEmailRepositoryRequest'; 3 | import { FindUserRepositoryRequest } from 'core/domainServices/User/request/FindUserRepositoryRequest'; 4 | import { AddUserRepositoryRequest } from 'core/domainServices/User/request/AddUserRepositoryRequest'; 5 | 6 | export interface IUserRepository { 7 | addUser(request: AddUserRepositoryRequest): Promise; 8 | findUser(request: FindUserRepositoryRequest): Promise; 9 | findUserByEmail(request: FindUserByEmailRepositoryRequest): Promise; 10 | getUsers(): Promise; 11 | } 12 | -------------------------------------------------------------------------------- /src/ui/Portal/common/graphql/PortalMutation.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class PortalMutation implements IResolver { 10 | readonly resolvers: IResolverObject; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_WAREHOUSE_ITEM_MUTATIONS) 14 | public readonly warehouseItemMutations: IResolver 15 | ) { 16 | this.resolvers = { 17 | ...warehouseItemMutations.resolvers, 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ui/shared/common/graphql/SharedMutation.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 8 | 9 | @injectable() 10 | export class SharedMutation implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(UI_SCHEMA_IDENTIFIERS.AUTHENTICATION_MUTATIONS) 15 | public readonly authenticationMutations: IResolver 16 | ) { 17 | this.resolvers = { 18 | ...this.authenticationMutations.resolvers, 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "allowSyntheticDefaultImports": true, 6 | "resolveJsonModule": true, 7 | "target": "es6", 8 | "module": "commonjs", 9 | "outDir": "dist", 10 | "strict": true, 11 | "sourceMap": true, 12 | "moduleResolution": "node", 13 | "lib": [ 14 | "es5", 15 | "es6", 16 | "dom" 17 | ], 18 | "plugins": [ 19 | { "transform": "@zerollup/ts-transform-paths" } 20 | ], 21 | "esModuleInterop": true 22 | }, 23 | "include": [ 24 | "src/**/*.ts", 25 | "tests/**/*.ts" 26 | ], 27 | "exclude": [ 28 | "node_modules" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/State.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Column, 3 | Entity, 4 | ManyToMany, 5 | OneToMany, 6 | PrimaryGeneratedColumn, 7 | JoinTable, 8 | } from 'typeorm'; 9 | 10 | import { Rate } from 'infrastructure/database/entities/Rate'; 11 | import { Warehouse } from 'infrastructure/database/entities/Warehouse'; 12 | 13 | @Entity() 14 | export class State { 15 | @PrimaryGeneratedColumn() 16 | id!: number; 17 | 18 | @Column() 19 | name!: string; 20 | 21 | @ManyToMany( 22 | () => Rate, 23 | rate => rate.states 24 | ) 25 | @JoinTable() 26 | rates!: Rate[]; 27 | 28 | @OneToMany( 29 | () => Warehouse, 30 | warehouse => warehouse.state 31 | ) 32 | warehouses!: Warehouse[]; 33 | } 34 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/UserFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | import { hashSync } from 'bcrypt'; 3 | import * as Faker from 'faker'; 4 | 5 | import { User } from 'infrastructure/database/entities/User'; 6 | 7 | const SALT = 10; 8 | const RANDOM_NUMBER_SIZE = 100; 9 | 10 | define(User, (faker: typeof Faker) => { 11 | const counter = faker.random.number(RANDOM_NUMBER_SIZE); 12 | const user = new User(); 13 | 14 | user.email = `onion_user_${counter}@example.com`; 15 | user.lastName = faker.name.lastName(counter); 16 | user.firstName = faker.name.firstName(); 17 | user.age = faker.random.number(100); 18 | user.password = hashSync('onion_test_123', SALT); 19 | 20 | return user; 21 | }); 22 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1580081950192-create_user.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createUser1580081950192 implements MigrationInterface { 4 | 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query('CREATE TABLE "user" ("id" SERIAL NOT NULL, "firstName" character varying NOT NULL, "lastName" character varying NOT NULL, "age" integer NOT NULL, "email" character varying NOT NULL, "password" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))'); 7 | } 8 | 9 | public async down(queryRunner: QueryRunner): Promise { 10 | await queryRunner.query('DROP TABLE "user"'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ui/common/mappings/User/UserDomainToUserUI.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | 5 | import { User } from 'core/domain/User/User'; 6 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | 8 | import { User as UserUI } from 'ui/common/models/User'; 9 | import { UI_MAPPINGS_IDENTIFIERS } from 'ui/UiModuleSymbols'; 10 | 11 | export const UserDomainToUserUI = (): IMapping => ({ 12 | configureMapping(mapper: Mapper): void { 13 | mapper.createMap( 14 | { 15 | destination: UI_MAPPINGS_IDENTIFIERS.USER_UI, 16 | source: DOMAIN_MAPPING_IDENTIFIERS.USER_DOMAIN, 17 | }, 18 | User 19 | ); 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /test/config/helpers/clearTestDB.ts: -------------------------------------------------------------------------------- 1 | import { getConnection } from 'typeorm'; 2 | 3 | import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions'; 4 | 5 | import { getPostgresConnection } from 'config/helpers/getPostgresConnection'; 6 | 7 | import { getCurrentConnection } from 'config/helpers/getCurrentConnection'; 8 | 9 | export const clearTestDB = async (testName?: string): Promise => { 10 | const connection = await getCurrentConnection(); 11 | 12 | getConnection(testName).close(); 13 | 14 | const db = await getPostgresConnection( 15 | connection.options as PostgresConnectionOptions 16 | ); 17 | 18 | // db.$pool.end(); 19 | 20 | await db.none('DROP DATABASE $1:name', testName); 21 | }; 22 | -------------------------------------------------------------------------------- /src/ui/common/config/application/express/auth/models/Principal.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify-express-utils'; 2 | 3 | import { User } from 'ui/common/models/User'; 4 | 5 | export class Principal implements interfaces.Principal { 6 | public details: User | undefined; 7 | 8 | public constructor(details: User | undefined) { 9 | this.details = details; 10 | } 11 | 12 | public isAuthenticated(): Promise { 13 | return Promise.resolve(!!this.details); 14 | } 15 | 16 | public isResourceOwner(resourceId: any): Promise { 17 | return Promise.resolve(resourceId === true); 18 | } 19 | 20 | public isInRole(role: string): Promise { 21 | return Promise.resolve(role === this.details?.role); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/EquipmentFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | import * as Faker from 'faker'; 3 | 4 | import { Equipment } from 'infrastructure/database/entities/Equipment'; 5 | 6 | const RANDOM_NUMBER_SIZE = 100; 7 | const RANDOM_DIMENSIONS_SIZE = 1000; 8 | 9 | define(Equipment, (faker: typeof Faker) => { 10 | const counter = faker.random.number(RANDOM_NUMBER_SIZE); 11 | const equipment = new Equipment(); 12 | 13 | equipment.name = `onion_equipment_${counter}`; 14 | equipment.width = faker.random.number(RANDOM_DIMENSIONS_SIZE); 15 | equipment.height = faker.random.number(RANDOM_DIMENSIONS_SIZE); 16 | equipment.depth = faker.random.number(RANDOM_DIMENSIONS_SIZE); 17 | 18 | return equipment; 19 | }); 20 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/IWarehouseService.ts: -------------------------------------------------------------------------------- 1 | import { FetchWarehouseRequest } from 'core/applicationServices/Warehouse/requests/FetchWarehouseRequest'; 2 | import { CreateWarehouseRequest } from 'core/applicationServices/Warehouse/requests/CreateWarehouseRequest'; 3 | import { UpdateWarehouseRequest } from 'core/applicationServices/Warehouse/requests/UpdateWarehouseRequest'; 4 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 5 | 6 | export interface IWarehouseService { 7 | fetchWarehouse: (request: FetchWarehouseRequest) => Promise; 8 | fetchWarehouses: () => Promise; 9 | createWarehouse: (request: CreateWarehouseRequest) => Promise; 10 | updateWarehouse: (request: UpdateWarehouseRequest) => Promise; 11 | } 12 | -------------------------------------------------------------------------------- /src/dependency/Portal/User/UserModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 5 | import { UserQuery } from 'ui/Portal/User/graphql/UserQuery'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | export class UserModule extends BaseModule { 9 | constructor() { 10 | super((bind: interfaces.Bind): void => { 11 | this.init(bind); 12 | }); 13 | } 14 | 15 | public init(bind: interfaces.Bind): void { 16 | this.provideUserQuery(bind); 17 | } 18 | 19 | private provideUserQuery(bind: interfaces.Bind): void { 20 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_USER_QUERIES).to(UserQuery); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/WarehouseItem.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Column, 3 | Entity, 4 | JoinColumn, 5 | ManyToOne, 6 | OneToOne, 7 | PrimaryGeneratedColumn, 8 | } from 'typeorm'; 9 | 10 | import { Equipment } from 'infrastructure/database/entities/Equipment'; 11 | import { Warehouse } from 'infrastructure/database/entities/Warehouse'; 12 | 13 | @Entity() 14 | export class WarehouseItem { 15 | @PrimaryGeneratedColumn() 16 | id!: number; 17 | 18 | @Column() 19 | name!: string; 20 | 21 | @Column({ type: 'decimal', precision: 15, scale: 2 }) 22 | cost!: number; 23 | 24 | @ManyToOne( 25 | () => Warehouse, 26 | warehouse => warehouse.warehouseItems 27 | ) 28 | warehouse!: Warehouse; 29 | 30 | @OneToOne(() => Equipment) 31 | @JoinColumn() 32 | equipment!: Equipment; 33 | } 34 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/Rate/RateEntityToRateDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | import { Rate } from 'core/domain/Rate/Rate'; 5 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 6 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 7 | import { Rate as RateEntity } from 'infrastructure/database/entities/Rate'; 8 | 9 | export const RateEntityToRateDomain = (): IMapping => ({ 10 | configureMapping(mapper: Mapper): void { 11 | mapper.createMap( 12 | { 13 | destination: DOMAIN_MAPPING_IDENTIFIERS.RATE_DOMAIN, 14 | source: DATABASE_MAPPING_IDENTIFIERS.RATE_ENTITY, 15 | }, 16 | Rate 17 | ); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/Role/RoleEntityToRoleDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | import { Role } from 'core/domain/Role/Role'; 5 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 6 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 7 | import { Role as RoleEntity } from 'infrastructure/database/entities/Role'; 8 | 9 | export const RoleEntityToRoleDomain = (): IMapping => ({ 10 | configureMapping(mapper: Mapper): void { 11 | mapper.createMap( 12 | { 13 | destination: DOMAIN_MAPPING_IDENTIFIERS.ROLE_DOMAIN, 14 | source: DATABASE_MAPPING_IDENTIFIERS.ROLE_ENTITY, 15 | }, 16 | Role 17 | ); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /test/config/helpers/prepareTestApp.ts: -------------------------------------------------------------------------------- 1 | import { InversifyExpressServer } from 'inversify-express-utils'; 2 | 3 | import { Application } from 'express'; 4 | 5 | import { ExpressApplication } from 'ui/common/config/application/express/ExpressApplication'; 6 | 7 | import { AppContainer } from 'dependency/AppContainer'; 8 | 9 | import { UI_APPLICATION_IDENTIFIERS } from 'ui/UiModuleSymbols'; 10 | 11 | export const prepareTestApp = async (): Promise => { 12 | const container = new AppContainer(); 13 | 14 | container.init(); 15 | 16 | container 17 | .get(UI_APPLICATION_IDENTIFIERS.EXPRESS_APPLICATION) 18 | .initialize(); 19 | 20 | return container 21 | .get( 22 | UI_APPLICATION_IDENTIFIERS.INVERSIFY_APPLICATION 23 | ) 24 | .build(); 25 | }; 26 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/State/StateEntityToStateDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | import { State } from 'core/domain/State/State'; 5 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 6 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 7 | import { State as StateEntity } from 'infrastructure/database/entities/State'; 8 | 9 | export const StateEntityToStateDomain = (): IMapping => ({ 10 | configureMapping(mapper: Mapper): void { 11 | mapper.createMap( 12 | { 13 | destination: DOMAIN_MAPPING_IDENTIFIERS.STATE_DOMAIN, 14 | source: DATABASE_MAPPING_IDENTIFIERS.STATE_ENTITY, 15 | }, 16 | State 17 | ); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /src/ui/Administration/Rate/graphql/RateQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { IRateService } from 'core/applicationServices/Rate/IRateService'; 7 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 8 | 9 | @injectable() 10 | export class RateQuery implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.RATE_SERVICE) 15 | private readonly rateService: IRateService 16 | ) { 17 | this.resolvers = { 18 | rates: this.rates, 19 | }; 20 | } 21 | 22 | private rates = () => this.rateService.fetchRates(); 23 | } 24 | -------------------------------------------------------------------------------- /src/core/applicationServices/Portal/Warehouse/WarehouseService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 4 | import { IWarehouseService } from 'core/applicationServices/Portal/Warehouse/IWarehouseService'; 5 | import { IWarehouseRepository } from 'core/domainServices/Portal/Warehouse/IWarehouseRepository'; 6 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 7 | 8 | @injectable() 9 | export class WarehouseService implements IWarehouseService { 10 | constructor( 11 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.PORTAL_WAREHOUSE_REPOSITORY) 12 | private readonly warehouseRepository: IWarehouseRepository 13 | ) {} 14 | 15 | fetchAvailableWarehouses(): Promise { 16 | return this.warehouseRepository.getAvailableWarehouses(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ui/Administration/State/graphql/StateQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { IStateService } from 'core/applicationServices/State/IStateService'; 7 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 8 | 9 | @injectable() 10 | export class StateQuery implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.STATE_SERVICE) 15 | private readonly stateService: IStateService 16 | ) { 17 | this.resolvers = { 18 | states: this.states, 19 | }; 20 | } 21 | 22 | private states = () => this.stateService.fetchStates(); 23 | } 24 | -------------------------------------------------------------------------------- /src/core/domain/Warehouse/Warehouse.ts: -------------------------------------------------------------------------------- 1 | import { State } from 'core/domain/State/State'; 2 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 3 | 4 | export class Warehouse { 5 | constructor( 6 | public readonly id: number, 7 | public readonly widthCost: number, 8 | public readonly heightCost: number, 9 | public readonly depthCost: number, 10 | public readonly capacityWidth: number, 11 | public readonly capacityHeight: number, 12 | public readonly capacityDepth: number, 13 | public readonly capacityWidthLoad: number, 14 | public readonly capacityHeightLoad: number, 15 | public readonly capacityDepthLoad: number, 16 | public readonly available: boolean, 17 | public readonly name: string, 18 | public readonly state: State, 19 | public readonly warehouseItems: WarehouseItem[] 20 | ) {} 21 | } 22 | -------------------------------------------------------------------------------- /src/core/domainServices/Warehouse/IWarehouseRepository.ts: -------------------------------------------------------------------------------- 1 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 2 | import { GetWarehouseRepositoryRequest } from 'core/domainServices/Warehouse/request/GetWarehouseRepositoryRequest'; 3 | import { CreateWarehouseRepositoryRequest } from 'core/domainServices/Warehouse/request/CreateWarehouseRepositoryRequest'; 4 | import { UpdateWarehouseRepositoryRequest } from 'core/domainServices/Warehouse/request/UpdateWarehouseRepositoryRequest'; 5 | 6 | export interface IWarehouseRepository { 7 | getWarehouse(request: GetWarehouseRepositoryRequest): Promise; 8 | getWarehouses(): Promise; 9 | createWarehouse( 10 | request: CreateWarehouseRepositoryRequest 11 | ): Promise; 12 | updateWarehouse( 13 | request: UpdateWarehouseRepositoryRequest 14 | ): Promise; 15 | } 16 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/Equipment.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Column, 3 | Entity, 4 | ManyToOne, 5 | OneToOne, 6 | PrimaryGeneratedColumn, 7 | } from 'typeorm'; 8 | 9 | import { User } from 'infrastructure/database/entities/User'; 10 | import { WarehouseItem } from 'infrastructure/database/entities/WarehouseItem'; 11 | 12 | @Entity() 13 | export class Equipment { 14 | @PrimaryGeneratedColumn() 15 | id!: number; 16 | 17 | @Column() 18 | name!: string; 19 | 20 | @Column() 21 | width!: number; 22 | 23 | @Column() 24 | height!: number; 25 | 26 | @Column() 27 | depth!: number; 28 | 29 | @ManyToOne( 30 | () => User, 31 | user => user.equipment 32 | ) 33 | user!: User; 34 | 35 | @OneToOne( 36 | () => WarehouseItem, 37 | warehouseItem => warehouseItem.equipment 38 | ) 39 | warehouseItem!: WarehouseItem; 40 | } 41 | -------------------------------------------------------------------------------- /src/dependency/Administration/Role/RoleModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { RoleRepository } from 'infrastructure/database/repository/Role/RoleRepository'; 5 | import { IRoleRepository } from 'core/domainServices/Role/IRoleRepository'; 6 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | 8 | export class RoleModule extends BaseModule { 9 | constructor() { 10 | super((bind: interfaces.Bind): void => { 11 | this.init(bind); 12 | }); 13 | } 14 | 15 | init(bind: interfaces.Bind): void { 16 | this.provideRoleRepository(bind); 17 | } 18 | 19 | private provideRoleRepository(bind: interfaces.Bind): void { 20 | bind(DOMAIN_REPOSITORY_IDENTIFIERS.ROLE_REPOSITORY).to( 21 | RoleRepository 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/User.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | ManyToOne, 6 | OneToMany, 7 | } from 'typeorm'; 8 | 9 | import { Role } from 'infrastructure/database/entities/Role'; 10 | import { Equipment } from 'infrastructure/database/entities/Equipment'; 11 | 12 | @Entity() 13 | export class User { 14 | @PrimaryGeneratedColumn() 15 | id!: number; 16 | 17 | @Column() 18 | firstName!: string; 19 | 20 | @Column() 21 | lastName!: string; 22 | 23 | @Column() 24 | age!: number; 25 | 26 | @Column() 27 | email!: string; 28 | 29 | @Column() 30 | password!: string; 31 | 32 | @ManyToOne( 33 | () => Role, 34 | role => role.user 35 | ) 36 | role!: Role; 37 | 38 | @OneToMany( 39 | () => Equipment, 40 | equipment => equipment.user 41 | ) 42 | equipment!: Equipment; 43 | } 44 | -------------------------------------------------------------------------------- /src/ui/Administration/User/graphql/UserQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | import { IResolverObject } from 'apollo-server-express'; 3 | 4 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 5 | 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IUserService } from 'core/applicationServices/User/IUserService'; 8 | 9 | @injectable() 10 | export class UserQuery implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_SERVICE) 15 | private readonly userService: IUserService 16 | ) { 17 | this.resolvers = { 18 | users: this.users, 19 | }; 20 | } 21 | 22 | private users = () => { 23 | return this.userService.fetchUsers(); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/Warehouse/WarehouseEntityToWarehouseDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 5 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 6 | import { Warehouse as WarehouseEntity } from 'infrastructure/database/entities/Warehouse'; 7 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 8 | 9 | export const WarehouseEntityToWarehouseDomain = (): IMapping => ({ 10 | configureMapping(mapper: Mapper): void { 11 | mapper.createMap( 12 | { 13 | source: DATABASE_MAPPING_IDENTIFIERS.WAREHOUSE_ENTITY, 14 | destination: DOMAIN_MAPPING_IDENTIFIERS.WAREHOUSE_DOMAIN, 15 | }, 16 | Warehouse 17 | ); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/Equipment/EquipmentEntityToEquipmentDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 5 | import { Equipment } from 'core/domain/Equipment/Equipment'; 6 | 7 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 8 | import { Equipment as EquipmentEntity } from 'infrastructure/database/entities/Equipment'; 9 | 10 | export const EquipmentEntityToEquipmentDomain = (): IMapping => ({ 11 | configureMapping(mapper: Mapper): void { 12 | mapper.createMap( 13 | { 14 | destination: DOMAIN_MAPPING_IDENTIFIERS.EQUIPMENT_DOMAIN, 15 | source: DATABASE_MAPPING_IDENTIFIERS.EQUIPMENT_ENTITY, 16 | }, 17 | Equipment 18 | ); 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /src/ui/Administration/Equipment/graphql/EquipmentQuery.ts: -------------------------------------------------------------------------------- 1 | import { IResolverObject } from 'apollo-server-express'; 2 | 3 | import { inject, injectable } from 'inversify'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { IEquipmentService } from 'core/applicationServices/Equipment/IEquipmentService'; 7 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 8 | 9 | @injectable() 10 | export class EquipmentQuery implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.EQUIPMENT_SERVICE) 15 | private readonly equipmentService: IEquipmentService 16 | ) { 17 | this.resolvers = { 18 | equipment: this.equipment, 19 | }; 20 | } 21 | 22 | private equipment = () => this.equipmentService.fetchAllEquipment(); 23 | } 24 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/User/UserEntityToUserDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | 5 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 6 | 7 | import { User } from 'core/domain/User/User'; 8 | import { User as UserEntity } from 'infrastructure/database/entities/User'; 9 | 10 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 11 | 12 | export const UserEntityToUserDomain = (): IMapping => ({ 13 | configureMapping(mapper: Mapper): void { 14 | mapper 15 | .createMap( 16 | { 17 | destination: DOMAIN_MAPPING_IDENTIFIERS.USER_DOMAIN, 18 | source: DATABASE_MAPPING_IDENTIFIERS.USER_ENTITY, 19 | }, 20 | User 21 | ) 22 | .forMember('role', opt => opt.mapFrom(src => src.role.name)); 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/common/IRepository.ts: -------------------------------------------------------------------------------- 1 | import { Query } from 'infrastructure/database/repository/common/Query'; 2 | 3 | export interface IRepository { 4 | custom(): R; 5 | delete( 6 | condition: string | number | { [key: string]: string | number } | E 7 | ): Promise; 8 | deleteAll(condition: string[] | number[] | E[]): Promise; 9 | find(id: string): Promise; 10 | findAll(): Promise; 11 | findBy(condition: Query): Promise; 12 | findMany(ids: string[]): Promise; 13 | query(query: string, parameters?: any[]): Promise; 14 | remove(entity: E): Promise; 15 | removeAll(entities: E[]): Promise; 16 | save(entity: E): Promise; 17 | saveAll(entities: E[]): Promise; 18 | update(condition: string | number, data: E): Promise; 19 | updateAll(condition: string[] | number[], data: E): Promise; 20 | } 21 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/IWarehouseItemService.ts: -------------------------------------------------------------------------------- 1 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 2 | import { FetchWarehouseItemRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemRequest'; 3 | import { CreateWarehouseItemRequest } from 'core/applicationServices/WarehouseItem/requests/CreateWarehouseItemRequest'; 4 | import { UpdateWarehouseItemRequest } from 'core/applicationServices/WarehouseItem/requests/UpdateWarehouseItemRequest'; 5 | 6 | export interface IWarehouseItemService { 7 | fetchWarehouseItem: ( 8 | request: FetchWarehouseItemRequest 9 | ) => Promise; 10 | fetchWarehouseItems: () => Promise; 11 | createWarehouseItem: ( 12 | request: CreateWarehouseItemRequest 13 | ) => Promise; 14 | updateWarehouseItem: ( 15 | request: UpdateWarehouseItemRequest 16 | ) => Promise; 17 | } 18 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/common/UnitOfWork.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EntityManager, 3 | getRepository, 4 | EntitySchema, 5 | ObjectType, 6 | } from 'typeorm'; 7 | 8 | export abstract class UnitOfWork { 9 | protected getConnectionName(): string | undefined { 10 | return process.env.ORM_CONNECTION; 11 | } 12 | 13 | /** 14 | * @description Had to do it that way due to limitation of Transaction library 15 | * it doesn't patch getManager and similar helpers from typeOrm - only Repository 16 | * @param entityClass - class of any Entity to get repository and it's manager, 17 | * doesn't really matter what kind of entity, for consistency provide same entity as operation 18 | * requires 19 | */ 20 | protected getManager( 21 | entityClass: ObjectType | EntitySchema | string 22 | ): EntityManager { 23 | return getRepository(entityClass, this.getConnectionName()).manager; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ui/common/config/application/common/IApplication.ts: -------------------------------------------------------------------------------- 1 | export interface IApplication { 2 | /** 3 | * Body Parsers 4 | * @Description Apply request related body parsers 5 | */ 6 | initializeBodyParsers(): void; 7 | 8 | /** 9 | * Handlers 10 | * @Description Apply here application handlers 11 | */ 12 | initializeHandlers(): void; 13 | /** 14 | * Loggers 15 | * @Description Apply here application wide loggers 16 | */ 17 | initializeLogging(): void; 18 | 19 | /** 20 | * Plugins 21 | * @Description Apply here any external plugins for app 22 | */ 23 | initializePlugins(): void; 24 | 25 | /** 26 | * Extensions 27 | * @Description Apply here extensions like swagger etc 28 | */ 29 | initializeExtensions(): void; 30 | 31 | /** 32 | * Security 33 | * @Description Apply security related plugins like JWT, request security etc... 34 | */ 35 | initializeSecurity(): void; 36 | } 37 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/Warehouse/WarehouseItemEntityToWarehouseItemDomain.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | 3 | import { IMapping } from 'core/common/mapper/IMapping'; 4 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 5 | import { DATABASE_MAPPING_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 6 | import { WarehouseItem as WarehouseItemEntity } from 'infrastructure/database/entities/WarehouseItem'; 7 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 8 | 9 | export const WarehouseItemEntityToWarehouseItemDomain = (): IMapping => ({ 10 | configureMapping(mapper: Mapper): void { 11 | mapper.createMap( 12 | { 13 | source: DATABASE_MAPPING_IDENTIFIERS.WAREHOUSE_ITEM_ENTITY, 14 | destination: DOMAIN_MAPPING_IDENTIFIERS.WAREHOUSE_ITEM_DOMAIN, 15 | }, 16 | WarehouseItem 17 | ); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1615066557385-add_equipment_dimensions.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class addEquipmentDimensions1615066557385 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query( 6 | `ALTER TABLE "equipment" ADD "width" integer NOT NULL` 7 | ); 8 | await queryRunner.query( 9 | `ALTER TABLE "equipment" ADD "height" integer NOT NULL` 10 | ); 11 | await queryRunner.query( 12 | `ALTER TABLE "equipment" ADD "depth" integer NOT NULL` 13 | ); 14 | } 15 | 16 | public async down(queryRunner: QueryRunner): Promise { 17 | await queryRunner.query(`ALTER TABLE "equipment" DROP COLUMN "depth"`); 18 | await queryRunner.query(`ALTER TABLE "equipment" DROP COLUMN "height"`); 19 | await queryRunner.query(`ALTER TABLE "equipment" DROP COLUMN "width"`); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ui/Administration/WarehouseItem/graphql/gql/WarehouseItem.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | warehouseItem(id: ID): WarehouseItem! @isAuthenticated(role: [ADMIN]) 3 | warehouseItems: [WarehouseItem!]! @isAuthenticated(role: [ADMIN]) 4 | } 5 | 6 | extend type Mutation { 7 | createWarehouseItem(input: CrateWarehouseItemInput): WarehouseItem! @isAuthenticated(role: [ADMIN]) 8 | updateWarehouseItem(input: UpdateWarehouseItemInput): WarehouseItem! @isAuthenticated(role: [ADMIN]) 9 | } 10 | 11 | input CrateWarehouseItemInput { 12 | name: String! 13 | cost: Float 14 | warehouseID: ID! 15 | equipmentID: ID! 16 | } 17 | 18 | input UpdateWarehouseItemInput { 19 | id: ID! 20 | name: String 21 | cost: Float 22 | warehouseID: ID 23 | equipmentID: ID 24 | } 25 | 26 | type WarehouseItem { 27 | id: ID! 28 | name: String! 29 | cost: Float! 30 | equipment: Equipment! 31 | warehouse: Warehouse! 32 | } 33 | -------------------------------------------------------------------------------- /src/ui/Portal/Warehouse/WarehouseQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IWarehouseService } from 'core/applicationServices/Portal/Warehouse/IWarehouseService'; 8 | 9 | @injectable() 10 | export class WarehouseQuery implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.PORTAL_WAREHOUSE_SERVICE) 15 | private readonly warehouseService: IWarehouseService 16 | ) { 17 | this.resolvers = { 18 | availableWarehouses: this.availableWarehouses, 19 | }; 20 | } 21 | 22 | private availableWarehouses = () => 23 | this.warehouseService.fetchAvailableWarehouses(); 24 | } 25 | -------------------------------------------------------------------------------- /src/ui/common/config/application/express/auth/utils/getHttpContext.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify-express-utils'; 2 | import { Request } from 'express'; 3 | 4 | import { Principal } from 'ui/common/config/application/express/auth/models/Principal'; 5 | 6 | /** 7 | * @Description as HttpContext is not properly injected into controller this is kind of 8 | * workaround which is done manually. Watch this issue as it could solve it https://github.com/inversify/inversify-express-utils/pull/253 9 | * Also take a look at this source https://stackoverflow.com/questions/54218295/inject-httpcontext-into-inversifyjs-middleware/54233115#54233115 10 | * and https://github.com/inversify/InversifyJS/issues/673 11 | */ 12 | export const getCurrentUser = (request: Request): Principal => { 13 | const httpContext: interfaces.HttpContext = Reflect.getMetadata( 14 | 'inversify-express-utils:httpcontext', 15 | request 16 | ); 17 | return httpContext.user; 18 | }; 19 | -------------------------------------------------------------------------------- /src/core/domainServices/WarehouseItem/IWarehouseItemRepository.ts: -------------------------------------------------------------------------------- 1 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 2 | import { GetWarehouseItemRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemRepositoryRequest'; 3 | import { CreateWarehouseItemRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/CreateWarehouseItemRepositoryRequest'; 4 | import { UpdateWarehouseItemRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/UpdateWarehouseItemRepositoryRequest'; 5 | 6 | export interface IWarehouseItemRepository { 7 | getWarehouseItem( 8 | request: GetWarehouseItemRepositoryRequest 9 | ): Promise; 10 | getWarehouseItems(): Promise; 11 | createWarehouseItem( 12 | request: CreateWarehouseItemRepositoryRequest 13 | ): Promise; 14 | updateWarehouseItem( 15 | request: UpdateWarehouseItemRepositoryRequest 16 | ): Promise; 17 | } 18 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1613939591337-create_warehouse.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createWarehouse1613939591337 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query( 6 | `CREATE TABLE "warehouse" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "stateId" integer, CONSTRAINT "PK_965abf9f99ae8c5983ae74ebde8" PRIMARY KEY ("id"))` 7 | ); 8 | await queryRunner.query( 9 | `ALTER TABLE "warehouse" ADD CONSTRAINT "FK_23423cf338d68b4927704b6be0e" FOREIGN KEY ("stateId") REFERENCES "state"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 10 | ); 11 | } 12 | 13 | public async down(queryRunner: QueryRunner): Promise { 14 | await queryRunner.query( 15 | `ALTER TABLE "warehouse" DROP CONSTRAINT "FK_23423cf338d68b4927704b6be0e"` 16 | ); 17 | await queryRunner.query(`DROP TABLE "warehouse"`); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/schema/RootQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolvers } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class RootQuery implements IResolver { 10 | readonly resolvers: IResolvers; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_QUERIES) 14 | public readonly administrationQueries: IResolver, 15 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_QUERIES) 16 | public readonly portalQueries: IResolver, 17 | @inject(UI_SCHEMA_IDENTIFIERS.SHARED_QUERIES) 18 | public readonly sharedQueries: IResolver 19 | ) { 20 | this.resolvers = { 21 | ...this.administrationQueries.resolvers, 22 | ...this.portalQueries.resolvers, 23 | ...this.sharedQueries.resolvers, 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ui/Portal/common/graphql/PortalQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class PortalQuery implements IResolver { 10 | readonly resolvers: IResolverObject; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_USER_QUERIES) 14 | public readonly userQueries: IResolver, 15 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_EQUIPMENT_QUERIES) 16 | public readonly equipmentQueries: IResolver, 17 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_WAREHOUSE_QUERIES) 18 | public readonly warehouseQueries: IResolver 19 | ) { 20 | this.resolvers = { 21 | ...this.userQueries.resolvers, 22 | ...this.equipmentQueries.resolvers, 23 | ...this.warehouseQueries.resolvers, 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/schema/RootMutation.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolvers } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class RootMutation implements IResolver { 10 | readonly resolvers: IResolvers; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_MUTATIONS) 14 | public readonly administrationMutations: IResolver, 15 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_MUTATIONS) 16 | public readonly portalMutations: IResolver, 17 | @inject(UI_SCHEMA_IDENTIFIERS.SHARED_MUTATIONS) 18 | public readonly sharedMutations: IResolver 19 | ) { 20 | this.resolvers = { 21 | ...this.administrationMutations.resolvers, 22 | ...this.portalMutations.resolvers, 23 | ...this.sharedMutations.resolvers, 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/config/seeds/UserSeed.ts: -------------------------------------------------------------------------------- 1 | import { Factory, Seeder, times } from 'typeorm-seeding'; 2 | 3 | import { Role } from 'infrastructure/database/entities/Role'; 4 | import { USER_ROLE } from 'infrastructure/database/enum/UserRole'; 5 | import { User } from 'infrastructure/database/entities/User'; 6 | import { Equipment } from 'infrastructure/database/entities/Equipment'; 7 | 8 | export class UserSeed implements Seeder { 9 | async run(factory: Factory): Promise { 10 | await this.prepareUserSeedToDelete(factory); 11 | } 12 | 13 | private async prepareUserSeedToDelete(factory: Factory): Promise { 14 | const memberRole = await factory(Role)().create({ 15 | name: USER_ROLE.MEMBER, 16 | }); 17 | 18 | const user = await factory(User)().create({ 19 | email: 'onion_member_test_delete@example.com', 20 | role: memberRole, 21 | }); 22 | 23 | await times(5, async () => { 24 | await factory(Equipment)().create({ 25 | user, 26 | }); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/schema/RootSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolvers } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class RootSubQuery implements IResolver { 10 | readonly resolvers: IResolvers; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_SUBQUERIES) 14 | public readonly administrationSubQueries: IResolver, 15 | @inject(UI_SCHEMA_IDENTIFIERS.PORTAL_SUBQUERIES) 16 | public readonly portalSubQueries: IResolver, 17 | @inject(UI_SCHEMA_IDENTIFIERS.SHARED_SUBQUERIES) 18 | public readonly sharedSubQueries: IResolver 19 | ) { 20 | this.resolvers = { 21 | ...this.administrationSubQueries.resolvers, 22 | ...this.portalSubQueries.resolvers, 23 | ...this.sharedSubQueries.resolvers, 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/plugins/RequestDidStartPlugin.ts: -------------------------------------------------------------------------------- 1 | import { ForbiddenError } from 'apollo-server-express'; 2 | import { 3 | ApolloServerPlugin, 4 | GraphQLRequestListener, 5 | } from 'apollo-server-plugin-base'; 6 | 7 | import { injectable } from 'inversify'; 8 | 9 | import { Context } from 'ui/common/config/application/apollo/types/Context'; 10 | 11 | // https://github.com/apollographql/apollo-server/issues/1709#issuecomment-575086351 12 | @injectable() 13 | export class RequestDidStartPlugin implements ApolloServerPlugin { 14 | requestDidStart(): GraphQLRequestListener | void { 15 | return { 16 | willSendResponse({ response, errors }) { 17 | if (response && response.http) { 18 | if ( 19 | errors && 20 | errors.find(err => err.originalError instanceof ForbiddenError) 21 | ) { 22 | response.data = undefined; 23 | response.http.status = 401; 24 | } 25 | } 26 | }, 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1615066763427-add_warehouse_dimensions_costs.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class addWarehouseDimensionsCosts1615066763427 4 | implements MigrationInterface { 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query( 7 | `ALTER TABLE "warehouse" ADD "widthCost" numeric(10,2) NOT NULL DEFAULT '0'` 8 | ); 9 | await queryRunner.query( 10 | `ALTER TABLE "warehouse" ADD "heightCost" numeric(10,2) NOT NULL DEFAULT '0'` 11 | ); 12 | await queryRunner.query( 13 | `ALTER TABLE "warehouse" ADD "depthCost" numeric(10,2) NOT NULL DEFAULT '0'` 14 | ); 15 | } 16 | 17 | public async down(queryRunner: QueryRunner): Promise { 18 | await queryRunner.query(`ALTER TABLE "warehouse" DROP COLUMN "depthCost"`); 19 | await queryRunner.query(`ALTER TABLE "warehouse" DROP COLUMN "heightCost"`); 20 | await queryRunner.query(`ALTER TABLE "warehouse" DROP COLUMN "widthCost"`); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/config/helpers/prepareAuthenticationToken.ts: -------------------------------------------------------------------------------- 1 | import { getConnection } from 'typeorm'; 2 | 3 | import { User } from 'infrastructure/database/entities/User'; 4 | 5 | import { JWTTokenUtil } from '../../../src/ui/common/config/application/common/auth/utils/JWTTokenUtil'; 6 | 7 | export const prepareAuthenticationToken = async ( 8 | email: string, 9 | dbName?: string 10 | ): Promise => { 11 | const jwtTokenUtil = new JWTTokenUtil(); 12 | 13 | const user = await getConnection(dbName) 14 | .getRepository(User) 15 | .createQueryBuilder() 16 | .leftJoinAndSelect('User.role', 'Role') 17 | .where('User.email = :email', { email }) 18 | .getOne(); 19 | 20 | if (!user) { 21 | return ''; 22 | } 23 | 24 | return jwtTokenUtil.generateToken( 25 | { 26 | id: user.id, 27 | firstName: user.firstName, 28 | email: user.email, 29 | role: user.role.name, 30 | }, 31 | process.env.APP_TOKEN_SECRET || '', 32 | process.env.APP_TOKEN_LIFE || '', 33 | 'user' 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/core/applicationServices/Rate/RateService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IRateRepository } from 'core/domainServices/Rate/IRateRepository'; 4 | import { IRateService } from 'core/applicationServices/Rate/IRateService'; 5 | import { FetchRateRequest } from 'core/applicationServices/Rate/requests/FetchRateRequest'; 6 | import { GetRateRepositoryRequest } from 'core/domainServices/Rate/request/GetRateRepositoryRequest'; 7 | import { Rate } from 'core/domain/Rate/Rate'; 8 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 9 | 10 | @injectable() 11 | export class RateService implements IRateService { 12 | constructor( 13 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.RATE_REPOSITORY) 14 | private readonly rateRepository: IRateRepository 15 | ) {} 16 | 17 | fetchRate({ id }: FetchRateRequest): Promise { 18 | return this.rateRepository.getRate(new GetRateRepositoryRequest(id)); 19 | } 20 | 21 | fetchRates(): Promise { 22 | return this.rateRepository.getRates(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1580567781829-create_equipment.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createEquipment1580567781829 implements MigrationInterface { 4 | 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query( 7 | `CREATE TABLE "equipment" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "userId" integer, CONSTRAINT "PK_0722e1b9d6eb19f5874c1678740" PRIMARY KEY ("id"))`, 8 | undefined 9 | ); 10 | await queryRunner.query( 11 | `ALTER TABLE "equipment" ADD CONSTRAINT "FK_520c92e8181b4020f047e20e7c3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, 12 | undefined 13 | ); 14 | } 15 | 16 | public async down(queryRunner: QueryRunner): Promise { 17 | await queryRunner.query( 18 | `ALTER TABLE "equipment" DROP CONSTRAINT "FK_520c92e8181b4020f047e20e7c3"`, 19 | undefined 20 | ); 21 | await queryRunner.query(`DROP TABLE "equipment"`, undefined); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/applicationServices/State/StateService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IStateService } from 'core/applicationServices/State/IStateService'; 4 | import { IStateRepository } from 'core/domainServices/State/IStateRepository'; 5 | import { FetchStateRequest } from 'core/applicationServices/State/requests/FetchStateRequest'; 6 | import { GetStateRepositoryRequest } from 'core/domainServices/State/request/GetStateRepositoryRequest'; 7 | import { State } from 'core/domain/State/State'; 8 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 9 | 10 | @injectable() 11 | export class StateService implements IStateService { 12 | constructor( 13 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.STATE_REPOSITORY) 14 | private readonly stateRepository: IStateRepository 15 | ) {} 16 | 17 | fetchState({ id }: FetchStateRequest): Promise { 18 | return this.stateRepository.getState(new GetStateRepositoryRequest(id)); 19 | } 20 | 21 | fetchStates(): Promise { 22 | return this.stateRepository.getStates(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ui/Portal/User/graphql/UserQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IUserService } from 'core/applicationServices/User/IUserService'; 8 | import { FetchUserRequest } from 'core/applicationServices/User/requests/FetchUserRequest'; 9 | import { Context } from 'ui/common/config/application/apollo/types/Context'; 10 | 11 | @injectable() 12 | export class UserQuery implements IResolver { 13 | readonly resolvers: IResolverObject; 14 | 15 | constructor( 16 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_SERVICE) 17 | private readonly userService: IUserService 18 | ) { 19 | this.resolvers = { 20 | me: this.me, 21 | }; 22 | } 23 | 24 | private me = (_root: unknown, _args: unknown, { viewer }: Context) => { 25 | return this.userService.fetchUser(new FetchUserRequest(viewer!.id)); 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/schema/RootResolver.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolvers } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_APPLICATION_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class RootResolver implements IResolver { 10 | readonly resolvers: IResolvers; 11 | 12 | constructor( 13 | @inject(UI_APPLICATION_IDENTIFIERS.SCHEMA_QUERIES) 14 | public readonly queries: IResolver, 15 | @inject(UI_APPLICATION_IDENTIFIERS.SCHEMA_MUTATIONS) 16 | public readonly mutations: IResolver, 17 | @inject(UI_APPLICATION_IDENTIFIERS.SCHEMA_SUBQUERIES) 18 | public readonly subqueries: IResolver 19 | ) { 20 | this.resolvers = { 21 | ...this.subqueries.resolvers, 22 | Query: { 23 | ...this.queries.resolvers, 24 | }, 25 | Mutation: { 26 | ...this.mutations.resolvers, 27 | }, 28 | Node: { 29 | __resolveType: () => null, 30 | }, 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/directives/IsAuthenticatedDirective.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql'; 2 | 3 | import { ForbiddenError } from 'apollo-server-errors'; 4 | 5 | import { BaseDirective } from 'ui/common/config/application/apollo/directives/BaseDirective'; 6 | 7 | type IsAuthenticatedArguments = { 8 | role: string; // TODO Change to enum 9 | }; 10 | 11 | export class IsAuthenticatedDirective extends BaseDirective { 12 | protected resolveField( 13 | resolve: GraphQLFieldResolver, 14 | args: [any, { [argName: string]: any }, any, GraphQLResolveInfo], 15 | directiveArgs: IsAuthenticatedArguments 16 | ): Promise { 17 | const [, , { viewer, claims }] = args; 18 | 19 | if (!viewer) { 20 | throw new ForbiddenError('FORBIDDEN ACCESS'); 21 | } 22 | 23 | const { role } = directiveArgs; 24 | 25 | if (!role) { 26 | return resolve.apply(this, args); 27 | } 28 | 29 | if (!claims || !role.includes(claims.role)) { 30 | throw new ForbiddenError('FORBIDDEN ACCESS'); // TODO Apply error enums at least 31 | } 32 | 33 | return resolve.apply(this, args); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ui/Administration/User/graphql/UserMutation.ts: -------------------------------------------------------------------------------- 1 | import { IResolverObject } from 'apollo-server-express'; 2 | 3 | import { inject, injectable } from 'inversify'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { RemoveUserInput } from 'ui/Administration/User/graphql/inputs/RemoveUserInput'; 7 | 8 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 9 | import { IUserService } from 'core/applicationServices/User/IUserService'; 10 | import { RemoveUserRequest } from 'core/applicationServices/User/requests/RemoveUserReuqest'; 11 | 12 | @injectable() 13 | export class UserMutation implements IResolver { 14 | readonly resolvers: IResolverObject; 15 | 16 | constructor( 17 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_SERVICE) 18 | private readonly userService: IUserService 19 | ) { 20 | this.resolvers = { 21 | removeUser: this.removeUser, 22 | }; 23 | } 24 | 25 | private removeUser = ( 26 | _root: unknown, 27 | { input: { id } }: { input: RemoveUserInput } 28 | ) => { 29 | return this.userService.removeUser(new RemoveUserRequest(id)); 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/WarehouseStateService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { State } from 'core/domain/State/State'; 4 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 5 | import { IWarehouseStateService } from 'core/applicationServices/Warehouse/IWarehouseStateService'; 6 | import { IWarehouseStateRepository } from 'core/domainServices/Warehouse/IWarehouseStateRepository'; 7 | import { FetchWarehouseStateRequest } from 'core/applicationServices/Warehouse/requests/FetchWarehouseStateRequest'; 8 | import { GetWarehouseStateRepositoryRequest } from 'core/domainServices/Warehouse/request/GetWarehouseStateRepositoryRequest'; 9 | 10 | @injectable() 11 | export class WarehouseStateService implements IWarehouseStateService { 12 | constructor( 13 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.WAREHOUSE_STATE_REPOSITORY) 14 | private readonly warehouseStateRepository: IWarehouseStateRepository 15 | ) {} 16 | 17 | fetchState({ warehouseId }: FetchWarehouseStateRequest): Promise { 18 | return this.warehouseStateRepository.getState( 19 | new GetWarehouseStateRepositoryRequest(warehouseId) 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/gql/Warehouse.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | warehouses: [Warehouse!]! @isAuthenticated(role: [ADMIN]) 3 | warehouse(id: ID!): Warehouse! @isAuthenticated(role: [ADMIN]) 4 | } 5 | 6 | extend type Mutation { 7 | createWarehouse(input: CreateWarehouseInput): Warehouse! @isAuthenticated(role: [ADMIN]) # TODO Add option to pass more props related to warehouse 8 | updateWarehouse(input: UpdateWarehouseInput): Warehouse! @isAuthenticated(role: [ADMIN]) # TODO Add option to pass more props related to warehouse 9 | } 10 | 11 | input UpdateWarehouseInput { 12 | warehouseID: ID! 13 | stateID: ID 14 | name: String 15 | } 16 | 17 | input CreateWarehouseInput { 18 | name: String! 19 | stateID: ID 20 | } 21 | 22 | type Warehouse { 23 | id: ID! 24 | name: String! 25 | widthCost: Float! 26 | heightCost: Float! 27 | depthCost: Float! 28 | capacityWidth: Int!, 29 | capacityHeight: Int!, 30 | capacityDepth: Int!, 31 | capacityWidthLoad: Int!, 32 | capacityHeightLoad: Int!, 33 | capacityDepthLoad: Int!, 34 | available: Boolean!, 35 | state: State! 36 | warehouseItems: [WarehouseItem!]! 37 | } 38 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1580148565494-create_role.ts: -------------------------------------------------------------------------------- 1 | import {MigrationInterface, QueryRunner} from "typeorm"; 2 | 3 | export class createRole1580148565494 implements MigrationInterface { 4 | 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query(`CREATE TYPE "role_name_enum" AS ENUM('admin', 'member')`); 7 | await queryRunner.query(`CREATE TABLE "role" ("id" SERIAL NOT NULL, "name" "role_name_enum" NOT NULL DEFAULT 'member', CONSTRAINT "PK_b36bcfe02fc8de3c57a8b2391c2" PRIMARY KEY ("id"))`); 8 | await queryRunner.query(`ALTER TABLE "user" ADD "roleId" integer`); 9 | await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_c28e52f758e7bbc53828db92194" FOREIGN KEY ("roleId") REFERENCES "role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); 10 | } 11 | 12 | public async down(queryRunner: QueryRunner): Promise { 13 | await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_c28e52f758e7bbc53828db92194"`); 14 | await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "roleId"`); 15 | await queryRunner.query(`DROP TABLE "role"`); 16 | await queryRunner.query(`DROP TYPE "role_name_enum"`); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/ui/Administration/common/graphql/AdministrationMutation.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | 7 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 8 | 9 | @injectable() 10 | export class AdministrationMutation implements IResolver { 11 | readonly resolvers: IResolverObject; 12 | 13 | constructor( 14 | @inject(UI_SCHEMA_IDENTIFIERS.EQUIPMENT_MUTATIONS) 15 | public readonly equipmentMutations: IResolver, 16 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_USER_MUTATIONS) 17 | public readonly userMutations: IResolver, 18 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_WAREHOUSE_MUTATIONS) 19 | public readonly warehouseMutations: IResolver, 20 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_WAREHOUSE_ITEM_MUTATIONS) 21 | public readonly warehouseItemMutations: IResolver 22 | ) { 23 | this.resolvers = { 24 | ...this.userMutations.resolvers, 25 | ...this.equipmentMutations.resolvers, 26 | ...this.warehouseMutations.resolvers, 27 | ...this.warehouseItemMutations.resolvers, 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ui/Administration/common/graphql/AdministrationSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class AdministrationSubQuery implements IResolver { 10 | readonly resolvers: IResolverObject; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_USER_SUBQUERIES) 14 | public readonly userSubQueries: IResolver, 15 | @inject(UI_SCHEMA_IDENTIFIERS.EQUIPMENT_SUBQUERIES) 16 | public readonly equipmentSubQueries: IResolver, 17 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_WAREHOUSE_SUBQUERIES) 18 | public readonly warehouseSubQueries: IResolver, 19 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_WAREHOUSE_ITEM_SUBQUERIES) 20 | public readonly warehouseItemSubQueries: IResolver 21 | ) { 22 | this.resolvers = { 23 | ...this.userSubQueries.resolvers, 24 | ...this.equipmentSubQueries.resolvers, 25 | ...this.warehouseSubQueries.resolvers, 26 | ...this.warehouseItemSubQueries.resolvers, 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/config/seeds/AuthenticationSeed.ts: -------------------------------------------------------------------------------- 1 | import { Factory, Seeder } from 'typeorm-seeding'; 2 | 3 | import { Role } from 'infrastructure/database/entities/Role'; 4 | import { USER_ROLE } from 'infrastructure/database/enum/UserRole'; 5 | import { User } from 'infrastructure/database/entities/User'; 6 | 7 | export class AuthenticationSeed implements Seeder { 8 | async run(factory: Factory): Promise { 9 | await this.prepareAuthenticationMemberUserSeed(factory); 10 | await this.prepareAuthenticationAdminUserSeed(factory); 11 | } 12 | 13 | private async prepareAuthenticationMemberUserSeed( 14 | factory: Factory 15 | ): Promise { 16 | const memberRole = await factory(Role)().create({ 17 | name: USER_ROLE.MEMBER, 18 | }); 19 | 20 | await factory(User)().create({ 21 | email: 'onion_member_test@example.com', 22 | role: memberRole, 23 | }); 24 | } 25 | 26 | private async prepareAuthenticationAdminUserSeed( 27 | factory: Factory 28 | ): Promise { 29 | const adminRole = await factory(Role)().create({ 30 | name: USER_ROLE.ADMIN, 31 | }); 32 | 33 | await factory(User)().create({ 34 | email: 'onion_admin_test@example.com', 35 | role: adminRole, 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ui/common/config/application/common/auth/utils/JWTTokenUtil.ts: -------------------------------------------------------------------------------- 1 | import { IncomingHttpHeaders } from 'http'; 2 | 3 | import jwt, { Secret } from 'jsonwebtoken'; 4 | import { injectable } from 'inversify'; 5 | 6 | import { APP_TOKEN_SECRET } from 'ui/common/config/consts/variables'; 7 | 8 | @injectable() 9 | export class JWTTokenUtil { 10 | private readonly AUTH_HEADER = 'authorization'; 11 | 12 | private readonly SCHEME = 'bearer'; 13 | 14 | private readonly MATCHER = /(\S+)\s+(\S+)/; 15 | 16 | generateToken( 17 | payload: any, 18 | secret: Secret, 19 | expiresIn: string | number, 20 | payloadKey?: string 21 | ): string { 22 | return jwt.sign(payloadKey ? { [payloadKey]: payload } : payload, secret, { 23 | expiresIn, 24 | }); 25 | } 26 | 27 | decodeToken(token: string): R { 28 | try { 29 | return jwt.verify(token, APP_TOKEN_SECRET) as R; 30 | } catch { 31 | return null as R; 32 | } 33 | } 34 | 35 | getTokenFromHeaders(headers: IncomingHttpHeaders): string | null { 36 | const authHeader = headers[this.AUTH_HEADER]; 37 | if (!authHeader) { 38 | return null; 39 | } 40 | const matches = authHeader.match(this.MATCHER); 41 | 42 | return matches && matches[2]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/infrastructure/database/fixtures/factories/WarehouseFactory.ts: -------------------------------------------------------------------------------- 1 | import { define } from 'typeorm-seeding'; 2 | import Faker from 'faker'; 3 | 4 | import { Warehouse } from 'infrastructure/database/entities/Warehouse'; 5 | 6 | const RANDOM_NUMBER_SIZE = 100; 7 | const RANDOM_DIMENSIONS_SIZE = 1000000; 8 | const RANDOM_COST = { 9 | min: 1, 10 | max: 1000, 11 | precision: 0.01, 12 | }; 13 | 14 | define(Warehouse, (faker: typeof Faker) => { 15 | const counter = faker.random.number(RANDOM_NUMBER_SIZE); 16 | const warehouse = new Warehouse(); 17 | 18 | warehouse.name = `Joe & Son Storage Facility ${counter}`; 19 | warehouse.capacityWidth = faker.random.number(RANDOM_DIMENSIONS_SIZE); 20 | warehouse.capacityDepth = faker.random.number(RANDOM_DIMENSIONS_SIZE); 21 | warehouse.capacityHeight = faker.random.number(RANDOM_DIMENSIONS_SIZE); 22 | warehouse.capacityWidthLoad = faker.random.number(RANDOM_DIMENSIONS_SIZE); 23 | warehouse.capacityHeightLoad = faker.random.number(RANDOM_DIMENSIONS_SIZE); 24 | warehouse.capacityDepthLoad = faker.random.number(RANDOM_DIMENSIONS_SIZE); 25 | warehouse.widthCost = faker.random.number(RANDOM_COST); 26 | warehouse.depthCost = faker.random.number(RANDOM_COST); 27 | warehouse.heightCost = faker.random.number(RANDOM_COST); 28 | 29 | return warehouse; 30 | }); 31 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/WarehouseQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IWarehouseService } from 'core/applicationServices/Warehouse/IWarehouseService'; 8 | import { GetWarehouseInput } from 'ui/Administration/Warehouse/graphql/inputs/GetWarehouseInput'; 9 | import { FetchWarehouseRequest } from 'core/applicationServices/Warehouse/requests/FetchWarehouseRequest'; 10 | 11 | @injectable() 12 | export class WarehouseQuery implements IResolver { 13 | readonly resolvers: IResolverObject; 14 | 15 | constructor( 16 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_SERVICE) 17 | private readonly warehouseService: IWarehouseService 18 | ) { 19 | this.resolvers = { 20 | warehouses: this.warehouses, 21 | warehouse: this.warehouse, 22 | }; 23 | } 24 | 25 | private warehouses = () => this.warehouseService.fetchWarehouses(); 26 | 27 | private warehouse = (_root: unknown, { id }: GetWarehouseInput) => 28 | this.warehouseService.fetchWarehouse(new FetchWarehouseRequest(id)); 29 | } 30 | -------------------------------------------------------------------------------- /src/ui/Portal/User/rest/v1/UserController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseHttpController, 3 | controller, 4 | httpDelete, 5 | requestBody, 6 | } from 'inversify-express-utils'; 7 | import { inject } from 'inversify'; 8 | 9 | import { OK } from 'http-status-codes'; 10 | 11 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 12 | import { IUserService } from 'core/applicationServices/User/IUserService'; 13 | import { USER_ROLE } from 'core/domain/User/UserRole'; 14 | import { RemoveUserRequest } from 'core/applicationServices/User/requests/RemoveUserReuqest'; 15 | 16 | import { isAuthenticated } from 'ui/common/config/application/express/auth/middlewares/IsAuthenticated'; 17 | import { DeleteUserRequestBody } from 'ui/Portal/User/rest/v1/requests/DeleteUserRequestBody'; 18 | 19 | @controller('/v1/user') 20 | export class UserController extends BaseHttpController { 21 | constructor( 22 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_SERVICE) 23 | private readonly userService: IUserService 24 | ) { 25 | super(); 26 | } 27 | 28 | @httpDelete('/', isAuthenticated({ role: USER_ROLE.MEMBER })) 29 | public async delete(@requestBody() { id }: DeleteUserRequestBody) { 30 | await this.userService.removeUser(new RemoveUserRequest(id)); 31 | 32 | return this.json(OK); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | src/@types/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # intellij 64 | .idea 65 | *.iml 66 | 67 | # dist 68 | dist 69 | 70 | ormconfig.js 71 | 72 | # stryker temp files 73 | .stryker-tmp 74 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | 3 | import { InversifyExpressServer } from 'inversify-express-utils'; 4 | 5 | import { AppContainer } from 'dependency/AppContainer'; 6 | 7 | import { ExpressApplication } from 'ui/common/config/application/express/ExpressApplication'; 8 | import { ApolloApplication } from 'ui/common/config/application/apollo/ApolloApplication'; 9 | import { UI_APPLICATION_IDENTIFIERS } from 'ui/UiModuleSymbols'; 10 | import { DATABASE_IDENTIFIERS } from 'infrastructure/InfrastructureModuleSymbols'; 11 | import { PORT } from 'ui/common/config/consts/variables'; 12 | import { IOrm } from 'infrastructure/database/orm/IOrm'; 13 | 14 | (async () => { 15 | const appContainer = new AppContainer(); 16 | appContainer.init(); 17 | appContainer 18 | .get(UI_APPLICATION_IDENTIFIERS.EXPRESS_APPLICATION) 19 | .initialize(); 20 | appContainer 21 | .get(UI_APPLICATION_IDENTIFIERS.APOLLO_APPLICATION) 22 | .initialize(); 23 | await appContainer.get(DATABASE_IDENTIFIERS.ORM).initialize(); 24 | appContainer 25 | .get( 26 | UI_APPLICATION_IDENTIFIERS.INVERSIFY_APPLICATION 27 | ) 28 | .build() 29 | // eslint-disable-next-line no-console 30 | .listen(PORT, () => console.log(`Server listening on ${PORT}`)); 31 | })(); 32 | -------------------------------------------------------------------------------- /ormconfig.sample.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | name: 'default', 4 | type: 'postgres', 5 | url: '', 6 | host: 'localhost', 7 | port: 5433, 8 | username: 'postgres', 9 | password: 'root', 10 | database: 'onion_dev', 11 | logging: true, 12 | migrationsRun: true, 13 | entities: [`${__dirname}/src/infrastructure/database/entities/**/*.ts`], 14 | migrations: [`${__dirname}/src/infrastructure/database/migrations/**/*.ts`], 15 | subscribers: [ 16 | `${__dirname}/src/infrastructure/database/subscribers/**/*.ts`, 17 | ], 18 | seeds: ['src/infrastructure/database/fixtures/seeds/*.ts'], 19 | factories: ['src/infrastructure/database/fixtures/factories/*.ts'], 20 | cli: { 21 | entitiesDir: 'src/infrastructure/database/entities', 22 | migrationsDir: 'src/infrastructure/database/migrations', 23 | subscribersDir: 'src/infrastructure/database/subscribers', 24 | }, 25 | }, 26 | { 27 | name: 'onion_test', 28 | type: 'postgres', 29 | host: 'localhost', 30 | port: 5433, 31 | username: 'postgres', 32 | password: 'root', 33 | database: 'onion_test', 34 | logging: false, 35 | entities: [`${__dirname}/src/infrastructure/database/entities/**/*.ts`], 36 | migrations: [`${__dirname}/src/infrastructure/database/migrations/**/*.ts`], 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /src/ui/shared/Authentication/graphql/AuthenticationMutation.ts: -------------------------------------------------------------------------------- 1 | import { IResolverObject } from 'apollo-server-express'; 2 | 3 | import { inject, injectable } from 'inversify'; 4 | 5 | import { UI_APPLICATION_IDENTIFIERS } from 'ui/UiModuleSymbols'; 6 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 7 | import { IAuthenticationHandler } from 'ui/common/config/application/common/auth/IAuthenticationHandler'; 8 | 9 | import { AuthenticationRequest } from 'core/applicationServices/Authentication/requests/AuthenticationRequest'; 10 | import { AuthenticateInput } from 'ui/shared/Authentication/graphql/inputs/AuthenticateInput'; 11 | 12 | @injectable() 13 | export class AuthenticationMutation implements IResolver { 14 | readonly resolvers: IResolverObject; 15 | 16 | constructor( 17 | @inject(UI_APPLICATION_IDENTIFIERS.GRAPHQL_JWT_AUTHENTICATION_HANDLER) 18 | private readonly authenticationHandler: IAuthenticationHandler 19 | ) { 20 | this.resolvers = { 21 | authenticate: this.authenticate, 22 | }; 23 | } 24 | 25 | private authenticate = ( 26 | _root: unknown, 27 | { input: { email, password } }: { input: AuthenticateInput } 28 | ) => { 29 | return this.authenticationHandler.authenticate( 30 | new AuthenticationRequest(email, password) 31 | ); 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/dependency/Portal/common/PortalModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 5 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 6 | import { PortalQuery } from 'ui/Portal/common/graphql/PortalQuery'; 7 | import { PortalMutation } from 'ui/Portal/common/graphql/PortalMutation'; 8 | import { PortalSubQuery } from 'ui/Portal/common/graphql/PortalSubQuery'; 9 | 10 | export class PortalModule extends BaseModule { 11 | constructor() { 12 | super((bind: interfaces.Bind): void => { 13 | this.init(bind); 14 | }); 15 | } 16 | 17 | init(bind: interfaces.Bind): void { 18 | this.providePortalQuery(bind); 19 | this.providePortalMutation(bind); 20 | this.providePortalSubQuery(bind); 21 | } 22 | 23 | private providePortalQuery(bind: interfaces.Bind): void { 24 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_QUERIES).to(PortalQuery); 25 | } 26 | 27 | private providePortalMutation(bind: interfaces.Bind): void { 28 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_MUTATIONS).to(PortalMutation); 29 | } 30 | 31 | private providePortalSubQuery(bind: interfaces.Bind): void { 32 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_SUBQUERIES).to(PortalSubQuery); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/core/applicationServices/Warehouse/WarehouseWarehouseItemService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IWarehouseWarehouseItemService } from 'core/applicationServices/Warehouse/IWarehouseWarehouseItemService'; 4 | import { FetchWarehouseItemsRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemsRequest'; 5 | import { IWarehouseWarehouseItemRepository } from 'core/domainServices/Warehouse/IWarehouseWarehouseItemRepository'; 6 | import { GetWarehouseItemsRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemsRepositoryRequest'; 7 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 8 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 9 | 10 | @injectable() 11 | export class WarehouseWarehouseItemService 12 | implements IWarehouseWarehouseItemService { 13 | constructor( 14 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.WAREHOUSE_WAREHOUSE_ITEM_REPOSITORY) 15 | private readonly warehouseWarehouseItemRepository: IWarehouseWarehouseItemRepository 16 | ) {} 17 | 18 | fetchWarehouseItems({ 19 | warehouseId, 20 | }: FetchWarehouseItemsRequest): Promise { 21 | return this.warehouseWarehouseItemRepository.getWarehouseItems( 22 | new GetWarehouseItemsRepositoryRequest(warehouseId) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/dependency/common/CommonModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { DBMapper } from 'infrastructure/database/mappings/DBMapper'; 5 | 6 | import { 7 | DATABASE_IDENTIFIERS, 8 | INFRASTRUCTURE_IDENTIFIERS, 9 | } from 'infrastructure/InfrastructureModuleSymbols'; 10 | import { IOrm } from 'infrastructure/database/orm/IOrm'; 11 | import { OnionOrm } from 'infrastructure/database/orm/OnionOrm'; 12 | 13 | import { UIMapper } from 'ui/common/mappings/UIMapper'; 14 | import { UI_IDENTIFIERS } from 'ui/UiModuleSymbols'; 15 | 16 | export class CommonModule extends BaseModule { 17 | constructor() { 18 | super((bind: interfaces.Bind): void => { 19 | this.init(bind); 20 | }); 21 | } 22 | 23 | public init(bind: interfaces.Bind): void { 24 | this.provideOrm(bind); 25 | 26 | this.provideDBMapper(bind); 27 | this.provideUIMapper(bind); 28 | } 29 | 30 | private provideUIMapper(bind: interfaces.Bind): void { 31 | bind(UI_IDENTIFIERS.UI_MAPPER).to(UIMapper); 32 | } 33 | 34 | private provideDBMapper(bind: interfaces.Bind): void { 35 | bind(INFRASTRUCTURE_IDENTIFIERS.DB_MAPPER).to(DBMapper); 36 | } 37 | 38 | private provideOrm(bind: interfaces.Bind): void { 39 | bind(DATABASE_IDENTIFIERS.ORM).to(OnionOrm); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/infrastructure/database/cli/dbReload.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | 3 | import * as yargs from 'yargs'; 4 | 5 | import { Connection, createConnection, ConnectionOptions } from 'typeorm'; 6 | 7 | const { 8 | type, 9 | host, 10 | port, 11 | username, 12 | password, 13 | database, 14 | logging, 15 | migrations, 16 | // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires 17 | } = require(join(process.cwd(), 'ormconfig.js'))[0]; 18 | 19 | yargs 20 | .command({ 21 | command: 'reload', 22 | describe: 'Reload Database', 23 | handler: () => { 24 | try { 25 | const connectionOptions: ConnectionOptions = { 26 | database, 27 | host, 28 | logging, 29 | migrations, 30 | password, 31 | port, 32 | type, 33 | username, 34 | }; 35 | createConnection(connectionOptions).then( 36 | async (connection: Connection) => { 37 | await connection.dropDatabase(); 38 | await connection.runMigrations(); 39 | process.exit(0); 40 | } 41 | ); 42 | } catch (error) { 43 | // eslint-disable-next-line no-console 44 | console.error('Error when reloading database', error); 45 | process.exit(1); 46 | } 47 | }, 48 | }) 49 | .parse(); 50 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/WarehouseItemEquipmentService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 4 | import { IWarehouseItemEquipmentService } from 'core/applicationServices/WarehouseItem/IWarehouseItemEquipmentService'; 5 | import { IWarehouseItemEquipmentRepository } from 'core/domainServices/WarehouseItem/IWarehouseItemEquipmentRepository'; 6 | import { Equipment } from 'core/domain/Equipment/Equipment'; 7 | import { FetchWarehouseItemEquipmentRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemEquipmentRequest'; 8 | import { GetWarehouseItemEquipmentRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemEquipmentRepositoryRequest'; 9 | 10 | @injectable() 11 | export class WarehouseItemEquipmentService 12 | implements IWarehouseItemEquipmentService { 13 | constructor( 14 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.WAREHOUSE_ITEM_EQUIPMENT_REPOSITORY) 15 | private readonly warehouseItemEquipmentRepository: IWarehouseItemEquipmentRepository 16 | ) {} 17 | 18 | fetchEquipment({ 19 | warehouseItemId, 20 | }: FetchWarehouseItemEquipmentRequest): Promise { 21 | return this.warehouseItemEquipmentRepository.getEquipment( 22 | new GetWarehouseItemEquipmentRepositoryRequest(warehouseItemId) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/core/applicationServices/WarehouseItem/WarehouseItemWarehouseService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 4 | import { IWarehouseItemWarehouseService } from 'core/applicationServices/WarehouseItem/IWarehouseItemWarehouseService'; 5 | import { IWarehouseItemWarehouseRepository } from 'core/domainServices/WarehouseItem/IWarehouseItemWarehouseRepository'; 6 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 7 | import { FetchWarehouseItemWarehouseRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemWarehouseRequest'; 8 | import { GetWarehouseItemWarehouseRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemWarehouseRepositoryRequest'; 9 | 10 | @injectable() 11 | export class WarehouseItemWarehouseService 12 | implements IWarehouseItemWarehouseService { 13 | constructor( 14 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.WAREHOUSE_ITEM_WAREHOUSE_REPOSITORY) 15 | private readonly warehouseItemWarehouseRepository: IWarehouseItemWarehouseRepository 16 | ) {} 17 | 18 | fetchWarehouse({ 19 | warehouseItemId, 20 | }: FetchWarehouseItemWarehouseRequest): Promise { 21 | return this.warehouseItemWarehouseRepository.getWarehouse( 22 | new GetWarehouseItemWarehouseRepositoryRequest(warehouseItemId) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/config/helpers/prepareTestDB.ts: -------------------------------------------------------------------------------- 1 | import { Connection } from 'typeorm'; 2 | import { createConnection } from 'typeorm-seeding'; 3 | 4 | import 'infrastructure/database/fixtures/factories/RoleFactory'; 5 | import 'infrastructure/database/fixtures/factories/UserFactory'; 6 | import 'infrastructure/database/fixtures/factories/EquipmentFactory'; 7 | 8 | import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions'; 9 | 10 | import { prepareTestTransaction } from 'config/helpers/prepareTestTransaction'; 11 | import { getPostgresConnection } from 'config/helpers/getPostgresConnection'; 12 | import { getCurrentConnection } from 'config/helpers/getCurrentConnection'; 13 | 14 | prepareTestTransaction(); 15 | 16 | export const prepareTestDB = async (testName?: string): Promise => { 17 | const connection = await getCurrentConnection(); 18 | 19 | const options = connection.options as PostgresConnectionOptions; 20 | 21 | const db = await getPostgresConnection( 22 | connection.options as PostgresConnectionOptions 23 | ); 24 | 25 | await db.none('CREATE DATABASE $1:name', testName); 26 | db.$pool.end(); 27 | 28 | const dbConnecton = await createConnection({ 29 | ...options, 30 | name: testName, 31 | database: testName, 32 | }); 33 | 34 | await dbConnecton.dropDatabase(); 35 | await dbConnecton.runMigrations(); 36 | return dbConnecton; 37 | }; 38 | -------------------------------------------------------------------------------- /src/ui/common/config/application/express/auth/middlewares/IsAuthenticated.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | 3 | import { FORBIDDEN, getStatusText, UNAUTHORIZED } from 'http-status-codes'; 4 | 5 | import { getCurrentUser } from 'ui/common/config/application/express/auth/utils/getHttpContext'; 6 | import { UserInterfaceError } from 'ui/common/config/errors/UserInterfaceError'; 7 | 8 | export const isAuthenticated = (config?: { role: string }) => async ( 9 | req: express.Request, 10 | res: express.Response, 11 | next: express.NextFunction 12 | ): Promise => { 13 | const user = getCurrentUser(req); 14 | 15 | if (!user) { 16 | next( 17 | new UserInterfaceError( 18 | UNAUTHORIZED, 19 | getStatusText(UNAUTHORIZED).toUpperCase() 20 | ) 21 | ); 22 | return; 23 | } 24 | 25 | const isAuthenticatedUser = await user.isAuthenticated(); 26 | 27 | if (!isAuthenticatedUser) { 28 | next( 29 | new UserInterfaceError( 30 | UNAUTHORIZED, 31 | getStatusText(UNAUTHORIZED).toUpperCase() 32 | ) 33 | ); 34 | return; 35 | } 36 | if (config) { 37 | const isInRole = await user.isInRole(config.role); 38 | if (!isInRole) { 39 | next( 40 | new UserInterfaceError( 41 | FORBIDDEN, 42 | getStatusText(FORBIDDEN).toUpperCase() 43 | ) 44 | ); 45 | return; 46 | } 47 | } 48 | next(); 49 | }; 50 | -------------------------------------------------------------------------------- /src/ui/Administration/WarehouseItem/graphql/WarehouseItemQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 6 | import { IWarehouseItemService } from 'core/applicationServices/WarehouseItem/IWarehouseItemService'; 7 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 8 | import { GetWarehouseInput } from 'ui/Administration/Warehouse/graphql/inputs/GetWarehouseInput'; 9 | import { FetchWarehouseItemRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemRequest'; 10 | 11 | @injectable() 12 | export class WarehouseItemQuery implements IResolver { 13 | readonly resolvers: IResolverObject; 14 | 15 | constructor( 16 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_ITEM_SERVICE) 17 | private readonly warehouseItemService: IWarehouseItemService 18 | ) { 19 | this.resolvers = { 20 | warehouseItem: this.warehouseItem, 21 | warehouseItems: this.warehouseItems, 22 | }; 23 | } 24 | 25 | private warehouseItem = (_root: unknown, { id }: GetWarehouseInput) => 26 | this.warehouseItemService.fetchWarehouseItem( 27 | new FetchWarehouseItemRequest(id) 28 | ); 29 | 30 | private warehouseItems = () => 31 | this.warehouseItemService.fetchWarehouseItems(); 32 | } 33 | -------------------------------------------------------------------------------- /src/dependency/Administration/User/UserModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UserQuery } from 'ui/Administration/User/graphql/UserQuery'; 7 | import { UserMutation } from 'ui/Administration/User/graphql/UserMutation'; 8 | import { UserSubQuery } from 'ui/Administration/User/graphql/UserSubQuery'; 9 | 10 | export class UserModule extends BaseModule { 11 | constructor() { 12 | super((bind: interfaces.Bind): void => { 13 | this.init(bind); 14 | }); 15 | } 16 | 17 | public init(bind: interfaces.Bind): void { 18 | this.provideUserQuery(bind); 19 | this.provideUserMutation(bind); 20 | this.provideUserSubquery(bind); 21 | } 22 | 23 | private provideUserQuery(bind: interfaces.Bind): void { 24 | bind(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_USER_QUERIES).to( 25 | UserQuery 26 | ); 27 | } 28 | 29 | private provideUserMutation(bind: interfaces.Bind): void { 30 | bind(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_USER_MUTATIONS).to( 31 | UserMutation 32 | ); 33 | } 34 | 35 | private provideUserSubquery(bind: interfaces.Bind): void { 36 | bind(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_USER_SUBQUERIES).to( 37 | UserSubQuery 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ui/Portal/WarehouseItem/graphql/WarehouseItemMutation.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IWarehouseItemService } from 'core/applicationServices/Portal/WarehouseItem/IWarehouseItemService'; 8 | import { AddWarehouseItemInput } from 'ui/Portal/WarehouseItem/graphql/inputs/AddWarehouseItemInput'; 9 | import { AddWarehouseItemRequest } from 'core/applicationServices/Portal/WarehouseItem/requests/AddWarehouseItemRequest'; 10 | 11 | @injectable() 12 | export class WarehouseItemMutation implements IResolver { 13 | readonly resolvers: IResolverObject; 14 | 15 | constructor( 16 | @inject( 17 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.PORTAL_WAREHOUSE_ITEM_SERVICE 18 | ) 19 | private readonly warehouseItemService: IWarehouseItemService 20 | ) { 21 | this.resolvers = { 22 | addWarehouseItem: this.addWarehouseItem, 23 | }; 24 | } 25 | 26 | private addWarehouseItem = ( 27 | _root: unknown, 28 | { 29 | input: { equipmentID, warehouseID, name }, 30 | }: { input: AddWarehouseItemInput } 31 | ) => 32 | this.warehouseItemService.addWarehouseItem( 33 | new AddWarehouseItemRequest(equipmentID, warehouseID, name) 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/ui/Administration/Equipment/graphql/EquipmentMutation.ts: -------------------------------------------------------------------------------- 1 | import { IResolverObject } from 'apollo-server-express'; 2 | 3 | import { inject, injectable } from 'inversify'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { CreateEquipmentInput } from 'ui/Administration/Equipment/graphql/inputs/CreateEquipmentInput'; 7 | import { Context } from 'ui/common/config/application/apollo/types/Context'; 8 | 9 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 10 | import { IEquipmentService } from 'core/applicationServices/Equipment/IEquipmentService'; 11 | import { CreateEquipmentRequest } from 'core/applicationServices/Equipment/requests/CreateEquipmentRequest'; 12 | 13 | @injectable() 14 | export class EquipmentMutation implements IResolver { 15 | readonly resolvers: IResolverObject; 16 | 17 | constructor( 18 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.EQUIPMENT_SERVICE) 19 | private readonly equipmentService: IEquipmentService 20 | ) { 21 | this.resolvers = { 22 | createEquipment: this.createEquipment, 23 | }; 24 | } 25 | 26 | private createEquipment = ( 27 | _root: unknown, 28 | { input: { name, depth, height, width } }: { input: CreateEquipmentInput }, 29 | { viewer }: Context 30 | ) => 31 | this.equipmentService.createEquipment( 32 | new CreateEquipmentRequest(name, width, height, depth, viewer!.id) 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/infrastructure/database/entities/Warehouse.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Column, 3 | Entity, 4 | ManyToOne, 5 | OneToMany, 6 | PrimaryGeneratedColumn, 7 | } from 'typeorm'; 8 | 9 | import { State } from 'infrastructure/database/entities/State'; 10 | import { WarehouseItem } from 'infrastructure/database/entities/WarehouseItem'; 11 | 12 | @Entity() 13 | export class Warehouse { 14 | @PrimaryGeneratedColumn() 15 | id!: number; 16 | 17 | @Column() 18 | name!: string; 19 | 20 | @Column({ type: 'decimal', precision: 10, scale: 2, default: 0 }) 21 | widthCost!: number; 22 | 23 | @Column({ type: 'decimal', precision: 10, scale: 2, default: 0 }) 24 | heightCost!: number; 25 | 26 | @Column({ type: 'decimal', precision: 10, scale: 2, default: 0 }) 27 | depthCost!: number; 28 | 29 | @Column({ default: false }) 30 | available!: boolean; 31 | 32 | @Column() 33 | capacityWidth!: number; 34 | 35 | @Column() 36 | capacityHeight!: number; 37 | 38 | @Column() 39 | capacityDepth!: number; 40 | 41 | @Column({ default: 0 }) 42 | capacityWidthLoad!: number; 43 | 44 | @Column({ default: 0 }) 45 | capacityHeightLoad!: number; 46 | 47 | @Column({ default: 0 }) 48 | capacityDepthLoad!: number; 49 | 50 | @OneToMany( 51 | () => WarehouseItem, 52 | warehouseItem => warehouseItem.warehouse 53 | ) 54 | warehouseItems!: WarehouseItem[]; 55 | 56 | @ManyToOne( 57 | () => State, 58 | state => state.warehouses 59 | ) 60 | state!: State; 61 | } 62 | -------------------------------------------------------------------------------- /src/ui/Administration/common/graphql/AdministrationQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 7 | 8 | @injectable() 9 | export class AdministrationQuery implements IResolver { 10 | readonly resolvers: IResolverObject; 11 | 12 | constructor( 13 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_USER_QUERIES) 14 | public readonly userQueries: IResolver, 15 | @inject(UI_SCHEMA_IDENTIFIERS.EQUIPMENT_QUERIES) 16 | public readonly equipmentQueries: IResolver, 17 | @inject(UI_SCHEMA_IDENTIFIERS.STATE_QUERIES) 18 | public readonly stateQueries: IResolver, 19 | @inject(UI_SCHEMA_IDENTIFIERS.RATE_QUERIES) 20 | public readonly rateQueries: IResolver, 21 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_WAREHOUSE_QUERIES) 22 | public readonly warehouseQueries: IResolver, 23 | @inject(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_WAREHOUSE_ITEM_QUERIES) 24 | public readonly warehouseItemsQueries: IResolver 25 | ) { 26 | this.resolvers = { 27 | ...this.userQueries.resolvers, 28 | ...this.equipmentQueries.resolvers, 29 | ...this.rateQueries.resolvers, 30 | ...this.stateQueries.resolvers, 31 | ...this.warehouseQueries.resolvers, 32 | ...this.warehouseItemsQueries.resolvers, 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1613939666376-create_warehouse_item.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createWarehouseItem1613939666376 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query( 6 | `CREATE TABLE "warehouse_item" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "cost" numeric(15,2) NOT NULL, "warehouseId" integer, "equipmentId" integer, CONSTRAINT "REL_b80de85d800854755dcae18a8d" UNIQUE ("equipmentId"), CONSTRAINT "PK_9db3c002318afa54d84094100b5" PRIMARY KEY ("id"))` 7 | ); 8 | await queryRunner.query( 9 | `ALTER TABLE "warehouse_item" ADD CONSTRAINT "FK_d09a54d4ec841657b62ccbb4776" FOREIGN KEY ("warehouseId") REFERENCES "warehouse"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 10 | ); 11 | await queryRunner.query( 12 | `ALTER TABLE "warehouse_item" ADD CONSTRAINT "FK_b80de85d800854755dcae18a8d1" FOREIGN KEY ("equipmentId") REFERENCES "equipment"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 13 | ); 14 | } 15 | 16 | public async down(queryRunner: QueryRunner): Promise { 17 | await queryRunner.query( 18 | `ALTER TABLE "warehouse_item" DROP CONSTRAINT "FK_b80de85d800854755dcae18a8d1"` 19 | ); 20 | await queryRunner.query( 21 | `ALTER TABLE "warehouse_item" DROP CONSTRAINT "FK_d09a54d4ec841657b62ccbb4776"` 22 | ); 23 | await queryRunner.query(`DROP TABLE "warehouse_item"`); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/dependency/shared/Authentication/AuthenticationModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | 5 | import { IAuthenticationService } from 'core/applicationServices/Authentication/IAuthenticationService'; 6 | import { AuthenticationService } from 'core/applicationServices/Authentication/AuthenticationService'; 7 | 8 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 9 | 10 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 11 | import { AuthenticationMutation } from 'ui/shared/Authentication/graphql/AuthenticationMutation'; 12 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 13 | 14 | export class AuthenticationModule extends BaseModule { 15 | constructor() { 16 | super((bind: interfaces.Bind): void => { 17 | this.init(bind); 18 | }); 19 | } 20 | 21 | public init(bind: interfaces.Bind): void { 22 | this.provideAuthenticationService(bind); 23 | 24 | this.provideAuthenticationMutation(bind); 25 | } 26 | 27 | private provideAuthenticationService(bind: interfaces.Bind): void { 28 | bind( 29 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.AUTHENTICATION_SERVICE 30 | ).to(AuthenticationService); 31 | } 32 | 33 | private provideAuthenticationMutation(bind: interfaces.Bind): void { 34 | bind(UI_SCHEMA_IDENTIFIERS.AUTHENTICATION_MUTATIONS).to( 35 | AuthenticationMutation 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ui/common/config/errors/handlers/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import { Application, NextFunction, Request, Response } from 'express'; 2 | 3 | import { 4 | INTERNAL_SERVER_ERROR, 5 | NOT_FOUND, 6 | UNPROCESSABLE_ENTITY, 7 | } from 'http-status-codes'; 8 | 9 | import { BaseError } from 'core/common/errors/BaseError'; 10 | import { CoreError } from 'core/common/errors/CoreError'; 11 | import { ErrorResponse } from 'ui/common/config/errors/models/ErrorResponse'; 12 | import { UserInterfaceError } from 'ui/common/config/errors/UserInterfaceError'; 13 | 14 | export const errorHandler = (app: Application) => 15 | app.use( 16 | (error: BaseError, req: Request, res: Response, next: NextFunction) => { 17 | next(); 18 | switch (error.constructor) { 19 | case UserInterfaceError: 20 | return res 21 | .status((error as UserInterfaceError).status) 22 | .json(new ErrorResponse(error.code, error.message)); 23 | case CoreError: 24 | return res 25 | .status(UNPROCESSABLE_ENTITY) 26 | .json(new ErrorResponse(error.code, error.message)); 27 | case BaseError: 28 | return res 29 | .status(NOT_FOUND) 30 | .json(new ErrorResponse(error.code, error.message)); 31 | default: 32 | return res 33 | .status(INTERNAL_SERVER_ERROR) 34 | .json( 35 | new ErrorResponse(INTERNAL_SERVER_ERROR.toString(), error.message) 36 | ); 37 | } 38 | } 39 | ); 40 | -------------------------------------------------------------------------------- /src/dependency/Administration/common/AdministrationModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 5 | import { AdministrationQuery } from 'ui/Administration/common/graphql/AdministrationQuery'; 6 | import { AdministrationMutation } from 'ui/Administration/common/graphql/AdministrationMutation'; 7 | import { AdministrationSubQuery } from 'ui/Administration/common/graphql/AdministrationSubQuery'; 8 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 9 | 10 | export class AdministrationModule extends BaseModule { 11 | constructor() { 12 | super((bind: interfaces.Bind): void => { 13 | this.init(bind); 14 | }); 15 | } 16 | 17 | init(bind: interfaces.Bind): void { 18 | this.provideAdministrationQuery(bind); 19 | this.provideAdministrationMutation(bind); 20 | this.provideAdministrationSubQuery(bind); 21 | } 22 | 23 | private provideAdministrationQuery(bind: interfaces.Bind): void { 24 | bind(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_QUERIES).to( 25 | AdministrationQuery 26 | ); 27 | } 28 | 29 | private provideAdministrationMutation(bind: interfaces.Bind): void { 30 | bind(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_MUTATIONS).to( 31 | AdministrationMutation 32 | ); 33 | } 34 | 35 | private provideAdministrationSubQuery(bind: interfaces.Bind): void { 36 | bind(UI_SCHEMA_IDENTIFIERS.ADMINISTRATION_SUBQUERIES).to( 37 | AdministrationSubQuery 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/Portal/Warehouse/WarehouseRepository.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | import { EntityRepository } from 'typeorm'; 3 | 4 | import { Warehouse as WarehouseEntity } from 'infrastructure/database/entities/Warehouse'; 5 | import { Repository } from 'infrastructure/database/repository/common/Repository'; 6 | import { 7 | DATABASE_MAPPING_IDENTIFIERS, 8 | INFRASTRUCTURE_IDENTIFIERS, 9 | } from 'infrastructure/InfrastructureModuleSymbols'; 10 | import { DBMapper } from 'infrastructure/database/mappings/DBMapper'; 11 | import { IWarehouseRepository } from 'core/domainServices/Portal/Warehouse/IWarehouseRepository'; 12 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 13 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 14 | 15 | @injectable() 16 | @EntityRepository(WarehouseEntity) 17 | export class WarehouseRepository extends Repository 18 | implements IWarehouseRepository { 19 | constructor( 20 | @inject(INFRASTRUCTURE_IDENTIFIERS.DB_MAPPER) 21 | private readonly dbMapper: DBMapper 22 | ) { 23 | super(WarehouseEntity); 24 | } 25 | 26 | async getAvailableWarehouses(): Promise { 27 | const warehouses = await this.findAll(); // TODO add query statement to fetch only warehouses which capacity is below 100% 28 | 29 | return this.dbMapper.mapper.map( 30 | { 31 | destination: DOMAIN_MAPPING_IDENTIFIERS.WAREHOUSE_DOMAIN, 32 | source: DATABASE_MAPPING_IDENTIFIERS.WAREHOUSE_ENTITY, 33 | }, 34 | warehouses 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/ApolloApplication.ts: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from 'apollo-server-express'; 2 | 3 | import { inject, injectable } from 'inversify'; 4 | 5 | import express from 'express'; 6 | 7 | import { BaseApplication } from 'ui/common/config/application/common/BaseApplication'; 8 | import { IApplication } from 'ui/common/config/application/common/IApplication'; 9 | 10 | import { UI_APPLICATION_IDENTIFIERS } from 'ui/UiModuleSymbols'; 11 | import { APOLLO_BASE_PATH } from 'ui/common/config/consts/variables'; 12 | 13 | @injectable() 14 | export class ApolloApplication extends BaseApplication 15 | implements IApplication { 16 | private readonly expressApp: express.Application; 17 | 18 | constructor( 19 | @inject(UI_APPLICATION_IDENTIFIERS.APOLLO_SERVER) app: ApolloServer, 20 | @inject(UI_APPLICATION_IDENTIFIERS.EXPRESS) expressApp: express.Application 21 | ) { 22 | super(app); 23 | this.expressApp = expressApp; 24 | } 25 | 26 | initialize(): void { 27 | this.initializeExtensions(); 28 | } 29 | 30 | initializeBodyParsers(): void { 31 | Error('NOT IMPLEMENTED'); 32 | } 33 | 34 | initializeExtensions(): void { 35 | this.app.applyMiddleware({ 36 | app: this.expressApp, 37 | path: APOLLO_BASE_PATH, 38 | cors: true, 39 | }); 40 | } 41 | 42 | initializeHandlers(): void { 43 | Error('NOT IMPLEMENTED'); 44 | } 45 | 46 | initializeLogging(): void { 47 | Error('NOT IMPLEMENTED'); 48 | } 49 | 50 | initializePlugins(): void { 51 | Error('NOT IMPLEMENTED'); 52 | } 53 | 54 | initializeSecurity(): void { 55 | Error('NOT IMPLEMENTED'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/ui/common/config/application/apollo/ApolloContext.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express'; 2 | 3 | import { inject, injectable } from 'inversify'; 4 | 5 | import { IApolloContext } from 'ui/common/config/application/apollo/common/IApolloContext'; 6 | import { Context } from 'ui/common/config/application/apollo/types/Context'; 7 | import { JWTTokenUtil } from 'ui/common/config/application/common/auth/utils/JWTTokenUtil'; 8 | import { UI_APPLICATION_IDENTIFIERS } from 'ui/UiModuleSymbols'; 9 | import { GraphQLTokenPayload } from 'ui/common/config/application/apollo/auth/types/GraphQLTokenPayload'; 10 | 11 | @injectable() 12 | export class ApolloContext implements IApolloContext { 13 | constructor( 14 | @inject(UI_APPLICATION_IDENTIFIERS.JWT_TOKEN_UTIL) 15 | public readonly jwtTokenUtil: JWTTokenUtil 16 | ) {} 17 | 18 | context = async ({ req }: { req: Request }): Promise => { 19 | const token = (req.headers.authorization || '').replace('Bearer ', ''); 20 | 21 | // https://github.com/apollographql/apollo-server/issues/3039 22 | // https://github.com/apollographql/apollo-server/issues/1709 23 | // https://github.com/apollographql/apollo-server/issues/1709#issuecomment-495793375 24 | // As long as first issue won't be resolved we won't be able to do it differently 25 | // without integrating express in this flow 26 | 27 | try { 28 | const { viewer, claims } = this.jwtTokenUtil.decodeToken< 29 | GraphQLTokenPayload 30 | >(token); 31 | return { 32 | claims, 33 | viewer, 34 | }; 35 | } catch { 36 | // If there is an error and directive is applied directive will throw Forbidden error 37 | return {}; 38 | } 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/dependency/Administration/Rate/RateModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { RateRepository } from 'infrastructure/database/repository/Rate/RateRepository'; 5 | import { 6 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS, 7 | DOMAIN_REPOSITORY_IDENTIFIERS, 8 | } from 'core/CoreModuleSymbols'; 9 | import { IRateRepository } from 'core/domainServices/Rate/IRateRepository'; 10 | import { RateService } from 'core/applicationServices/Rate/RateService'; 11 | import { IRateService } from 'core/applicationServices/Rate/IRateService'; 12 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 13 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 14 | import { RateQuery } from 'ui/Administration/Rate/graphql/RateQuery'; 15 | 16 | export class RateModule extends BaseModule { 17 | constructor() { 18 | super((bind: interfaces.Bind): void => { 19 | this.init(bind); 20 | }); 21 | } 22 | 23 | init(bind: interfaces.Bind): void { 24 | this.provideRateRepository(bind); 25 | 26 | this.provideRateService(bind); 27 | 28 | this.provideRateQuery(bind); 29 | } 30 | 31 | private provideRateRepository(bind: interfaces.Bind): void { 32 | bind(DOMAIN_REPOSITORY_IDENTIFIERS.RATE_REPOSITORY).to( 33 | RateRepository 34 | ); 35 | } 36 | 37 | private provideRateService(bind: interfaces.Bind): void { 38 | bind(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.RATE_SERVICE).to( 39 | RateService 40 | ); 41 | } 42 | 43 | private provideRateQuery(bind: interfaces.Bind): void { 44 | bind(UI_SCHEMA_IDENTIFIERS.RATE_QUERIES).to(RateQuery); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/core/applicationServices/User/UserService.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { 4 | DOMAIN_REPOSITORY_IDENTIFIERS, 5 | DOMAIN_UNIT_OF_WORK_IDENTIFIERS, 6 | } from 'core/CoreModuleSymbols'; 7 | 8 | import { IUserService } from 'core/applicationServices/User/IUserService'; 9 | import { IUserUnitOfWork } from 'core/domainServices/User/IUserUnitOfWork'; 10 | import { IUserRepository } from 'core/domainServices/User/IUserRepository'; 11 | 12 | import { RemoveUserRequest } from 'core/applicationServices/User/requests/RemoveUserReuqest'; 13 | import { DeleteUserUnitOfWorkRepositoryRequest } from 'core/domainServices/User/request/DeleteUserUnitOfWorkRepositoryRequest'; 14 | import { FetchUserRequest } from 'core/applicationServices/User/requests/FetchUserRequest'; 15 | import { FindUserRepositoryRequest } from 'core/domainServices/User/request/FindUserRepositoryRequest'; 16 | import { User } from 'core/domain/User/User'; 17 | 18 | @injectable() 19 | export class UserService implements IUserService { 20 | constructor( 21 | @inject(DOMAIN_UNIT_OF_WORK_IDENTIFIERS.USER_UNIT_OF_WORK) 22 | private readonly userUnitOfWork: IUserUnitOfWork, 23 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.USER_REPOSITORY) 24 | private readonly userRepository: IUserRepository 25 | ) {} 26 | 27 | removeUser({ id }: RemoveUserRequest): Promise { 28 | return this.userUnitOfWork.deleteUser( 29 | new DeleteUserUnitOfWorkRepositoryRequest(id) 30 | ); 31 | } 32 | 33 | fetchUser({ id }: FetchUserRequest): Promise { 34 | return this.userRepository.findUser(new FindUserRepositoryRequest(id)); 35 | } 36 | 37 | fetchUsers(): Promise { 38 | return this.userRepository.getUsers(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/dependency/Administration/State/StateModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { StateRepository } from 'infrastructure/database/repository/State/StateRepository'; 5 | import { 6 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS, 7 | DOMAIN_REPOSITORY_IDENTIFIERS, 8 | } from 'core/CoreModuleSymbols'; 9 | import { IStateRepository } from 'core/domainServices/State/IStateRepository'; 10 | import { StateService } from 'core/applicationServices/State/StateService'; 11 | import { IStateService } from 'core/applicationServices/State/IStateService'; 12 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 13 | import { StateQuery } from 'ui/Administration/State/graphql/StateQuery'; 14 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 15 | 16 | export class StateModule extends BaseModule { 17 | constructor() { 18 | super((bind: interfaces.Bind): void => { 19 | this.init(bind); 20 | }); 21 | } 22 | 23 | init(bind: interfaces.Bind): void { 24 | this.provideStateRepository(bind); 25 | 26 | this.provideStateService(bind); 27 | 28 | this.provideStateQuery(bind); 29 | } 30 | 31 | private provideStateRepository(bind: interfaces.Bind): void { 32 | bind(DOMAIN_REPOSITORY_IDENTIFIERS.STATE_REPOSITORY).to( 33 | StateRepository 34 | ); 35 | } 36 | 37 | private provideStateService(bind: interfaces.Bind): void { 38 | bind( 39 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.STATE_SERVICE 40 | ).to(StateService); 41 | } 42 | 43 | private provideStateQuery(bind: interfaces.Bind): void { 44 | bind(UI_SCHEMA_IDENTIFIERS.STATE_QUERIES).to(StateQuery); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/dependency/shared/User/UserModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { IUserService } from 'core/applicationServices/User/IUserService'; 5 | import { UserService } from 'core/applicationServices/User/UserService'; 6 | import { IUserRepository } from 'core/domainServices/User/IUserRepository'; 7 | 8 | import { 9 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS, 10 | DOMAIN_REPOSITORY_IDENTIFIERS, 11 | DOMAIN_UNIT_OF_WORK_IDENTIFIERS, 12 | } from 'core/CoreModuleSymbols'; 13 | 14 | import { UserRepository } from 'infrastructure/database/repository/User/UserRepository'; 15 | import { UserUnitOfWork } from 'infrastructure/database/repository/User/UserUnitOfWork'; 16 | import { IUserUnitOfWork } from 'core/domainServices/User/IUserUnitOfWork'; 17 | 18 | export class UserModule extends BaseModule { 19 | constructor() { 20 | super((bind: interfaces.Bind): void => { 21 | this.init(bind); 22 | }); 23 | } 24 | 25 | public init(bind: interfaces.Bind): void { 26 | this.provideUserRepository(bind); 27 | this.provideUserUnitOfWork(bind); 28 | 29 | this.provideUserService(bind); 30 | } 31 | 32 | private provideUserRepository(bind: interfaces.Bind): void { 33 | bind(DOMAIN_REPOSITORY_IDENTIFIERS.USER_REPOSITORY).to( 34 | UserRepository 35 | ); 36 | } 37 | 38 | private provideUserUnitOfWork(bind: interfaces.Bind): void { 39 | bind(DOMAIN_UNIT_OF_WORK_IDENTIFIERS.USER_UNIT_OF_WORK).to( 40 | UserUnitOfWork 41 | ); 42 | } 43 | 44 | private provideUserService(bind: interfaces.Bind): void { 45 | bind(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_SERVICE).to( 46 | UserService 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1611439711157-create_state_rates_rate.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createStateRatesRate1611487349958 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query( 6 | `CREATE TABLE "state_rates_rate" ("stateId" integer NOT NULL, "rateId" integer NOT NULL, CONSTRAINT "PK_1235b8c6cd27b19ab43b2bf64ee" PRIMARY KEY ("stateId", "rateId"))` 7 | ); 8 | await queryRunner.query( 9 | `CREATE INDEX "IDX_29847b774096ece14e79dc6d8c" ON "state_rates_rate" ("stateId") ` 10 | ); 11 | await queryRunner.query( 12 | `CREATE INDEX "IDX_5a281a1ad4fd97934063da48c0" ON "state_rates_rate" ("rateId") ` 13 | ); 14 | await queryRunner.query( 15 | `ALTER TABLE "state_rates_rate" ADD CONSTRAINT "FK_29847b774096ece14e79dc6d8c7" FOREIGN KEY ("stateId") REFERENCES "state"("id") ON DELETE CASCADE ON UPDATE NO ACTION` 16 | ); 17 | await queryRunner.query( 18 | `ALTER TABLE "state_rates_rate" ADD CONSTRAINT "FK_5a281a1ad4fd97934063da48c0d" FOREIGN KEY ("rateId") REFERENCES "rate"("id") ON DELETE CASCADE ON UPDATE NO ACTION` 19 | ); 20 | } 21 | 22 | public async down(queryRunner: QueryRunner): Promise { 23 | await queryRunner.query( 24 | `ALTER TABLE "state_rates_rate" DROP CONSTRAINT "FK_5a281a1ad4fd97934063da48c0d"` 25 | ); 26 | await queryRunner.query( 27 | `ALTER TABLE "state_rates_rate" DROP CONSTRAINT "FK_29847b774096ece14e79dc6d8c7"` 28 | ); 29 | await queryRunner.query(`DROP INDEX "IDX_5a281a1ad4fd97934063da48c0"`); 30 | await queryRunner.query(`DROP INDEX "IDX_29847b774096ece14e79dc6d8c"`); 31 | await queryRunner.query(`DROP TABLE "state_rates_rate"`); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1611439711158-create_equipment_state_rate.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class createEquipmentStateRate1611439711158 4 | implements MigrationInterface { 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query( 7 | `CREATE TABLE "equipment_state_rate" ("stateId" integer NOT NULL, "equipmentId" integer NOT NULL, "rateId" integer NOT NULL, CONSTRAINT "PK_dab17f51a838eb9bb10c0604c29" PRIMARY KEY ("stateId", "equipmentId", "rateId"))` 8 | ); 9 | await queryRunner.query( 10 | `ALTER TABLE "equipment_state_rate" ADD CONSTRAINT "FK_8b120a96a437cbe83b5aed17c71" FOREIGN KEY ("stateId") REFERENCES "state"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 11 | ); 12 | await queryRunner.query( 13 | `ALTER TABLE "equipment_state_rate" ADD CONSTRAINT "FK_3a17f1311eb3963a16a6b170245" FOREIGN KEY ("equipmentId") REFERENCES "equipment"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 14 | ); 15 | await queryRunner.query( 16 | `ALTER TABLE "equipment_state_rate" ADD CONSTRAINT "FK_8c0672ca02121391adae5eaf432" FOREIGN KEY ("rateId") REFERENCES "rate"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 17 | ); 18 | } 19 | 20 | public async down(queryRunner: QueryRunner): Promise { 21 | await queryRunner.query( 22 | `ALTER TABLE "equipment_state_rate" DROP CONSTRAINT "FK_8c0672ca02121391adae5eaf432"` 23 | ); 24 | await queryRunner.query( 25 | `ALTER TABLE "equipment_state_rate" DROP CONSTRAINT "FK_3a17f1311eb3963a16a6b170245"` 26 | ); 27 | await queryRunner.query( 28 | `ALTER TABLE "equipment_state_rate" DROP CONSTRAINT "FK_8b120a96a437cbe83b5aed17c71"` 29 | ); 30 | await queryRunner.query(`DROP TABLE "equipment_state_rate"`); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1613940493612-remove_equipment_state_rate.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class removeEquipmentStateRate1613940493612 4 | implements MigrationInterface { 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query( 7 | `ALTER TABLE "equipment_state_rate" DROP CONSTRAINT "FK_8c0672ca02121391adae5eaf432"` 8 | ); 9 | await queryRunner.query( 10 | `ALTER TABLE "equipment_state_rate" DROP CONSTRAINT "FK_3a17f1311eb3963a16a6b170245"` 11 | ); 12 | await queryRunner.query( 13 | `ALTER TABLE "equipment_state_rate" DROP CONSTRAINT "FK_8b120a96a437cbe83b5aed17c71"` 14 | ); 15 | await queryRunner.query(`DROP TABLE "equipment_state_rate"`); 16 | } 17 | 18 | public async down(queryRunner: QueryRunner): Promise { 19 | await queryRunner.query( 20 | `CREATE TABLE "equipment_state_rate" ("stateId" integer NOT NULL, "equipmentId" integer NOT NULL, "rateId" integer NOT NULL, CONSTRAINT "PK_dab17f51a838eb9bb10c0604c29" PRIMARY KEY ("stateId", "equipmentId", "rateId"))` 21 | ); 22 | await queryRunner.query( 23 | `ALTER TABLE "equipment_state_rate" ADD CONSTRAINT "FK_8b120a96a437cbe83b5aed17c71" FOREIGN KEY ("stateId") REFERENCES "state"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 24 | ); 25 | await queryRunner.query( 26 | `ALTER TABLE "equipment_state_rate" ADD CONSTRAINT "FK_3a17f1311eb3963a16a6b170245" FOREIGN KEY ("equipmentId") REFERENCES "equipment"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 27 | ); 28 | await queryRunner.query( 29 | `ALTER TABLE "equipment_state_rate" ADD CONSTRAINT "FK_8c0672ca02121391adae5eaf432" FOREIGN KEY ("rateId") REFERENCES "rate"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/WarehouseMutation.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 6 | import { IWarehouseService } from 'core/applicationServices/Warehouse/IWarehouseService'; 7 | import { CreateWarehouseRequest } from 'core/applicationServices/Warehouse/requests/CreateWarehouseRequest'; 8 | import { UpdateWarehouseRequest } from 'core/applicationServices/Warehouse/requests/UpdateWarehouseRequest'; 9 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 10 | import { CreateWarehouseInput } from 'ui/Administration/Warehouse/graphql/inputs/CreateWarehouseInput'; 11 | import { UpdateWarehouseInput } from 'ui/Administration/Warehouse/graphql/inputs/UpdateWarehouseInput'; 12 | 13 | @injectable() 14 | export class WarehouseMutation implements IResolver { 15 | readonly resolvers: IResolverObject; 16 | 17 | constructor( 18 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_SERVICE) 19 | private readonly warehouseService: IWarehouseService 20 | ) { 21 | this.resolvers = { 22 | createWarehouse: this.createWarehouse, 23 | updateWarehouse: this.updateWarehouse, 24 | }; 25 | } 26 | 27 | private createWarehouse = ( 28 | _root: unknown, 29 | { input: { name, stateID } }: { input: CreateWarehouseInput } 30 | ) => 31 | this.warehouseService.createWarehouse( 32 | new CreateWarehouseRequest(name, stateID) 33 | ); 34 | 35 | private updateWarehouse = ( 36 | _root: unknown, 37 | { input: { warehouseID, stateID, name } }: { input: UpdateWarehouseInput } 38 | ) => 39 | this.warehouseService.updateWarehouse( 40 | new UpdateWarehouseRequest(warehouseID, stateID, name) 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/infrastructure/database/mappings/DBMapper.ts: -------------------------------------------------------------------------------- 1 | import { Mapper } from '@wufe/mapper'; 2 | import { injectable } from 'inversify'; 3 | 4 | import { UserEntityToUserDomain } from 'infrastructure/database/mappings/User/UserEntityToUserDomain'; 5 | import { EquipmentEntityToEquipmentDomain } from 'infrastructure/database/mappings/Equipment/EquipmentEntityToEquipmentDomain'; 6 | import { RoleEntityToRoleDomain } from 'infrastructure/database/mappings/Role/RoleEntityToRoleDomain'; 7 | import { RateEntityToRateDomain } from 'infrastructure/database/mappings/Rate/RateEntityToRateDomain'; 8 | import { StateEntityToStateDomain } from 'infrastructure/database/mappings/State/StateEntityToStateDomain'; 9 | import { WarehouseEntityToWarehouseDomain } from 'infrastructure/database/mappings/Warehouse/WarehouseEntityToWarehouseDomain'; 10 | import { WarehouseItemEntityToWarehouseItemDomain } from 'infrastructure/database/mappings/Warehouse/WarehouseItemEntityToWarehouseItemDomain'; 11 | 12 | @injectable() 13 | export class DBMapper { 14 | public readonly mapper: Mapper; 15 | 16 | constructor() { 17 | this.mapper = new Mapper().withConfiguration(configuration => 18 | configuration 19 | .shouldIgnoreSourcePropertiesIfNotInDestination(true) 20 | .shouldAutomaticallyMapArrays(true) 21 | ); 22 | 23 | this.initialize(); 24 | } 25 | 26 | private initialize(): void { 27 | UserEntityToUserDomain().configureMapping(this.mapper); 28 | EquipmentEntityToEquipmentDomain().configureMapping(this.mapper); 29 | RoleEntityToRoleDomain().configureMapping(this.mapper); 30 | RateEntityToRateDomain().configureMapping(this.mapper); 31 | StateEntityToStateDomain().configureMapping(this.mapper); 32 | WarehouseEntityToWarehouseDomain().configureMapping(this.mapper); 33 | WarehouseItemEntityToWarehouseItemDomain().configureMapping(this.mapper); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/Equipment/EquipmentUnitOfWork.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { DOMAIN_REPOSITORY_IDENTIFIERS } from 'core/CoreModuleSymbols'; 4 | 5 | import { IEquipmentRepository } from 'core/domainServices/Equipment/IEquipmentRepository'; 6 | import { IUserRepository } from 'core/domainServices/User/IUserRepository'; 7 | import { IEquipmentUnitOfWork } from 'core/domainServices/Equipment/IEquipmentUnitOfWork'; 8 | 9 | import { AddEquipmentRepositoryRequest } from 'core/domainServices/Equipment/request/AddEquipmentRepositoryRequest'; 10 | import { AddEquipmentUnitOfWorkRepositoryRequest } from 'core/domainServices/Equipment/request/AddEquipmentUnitOfWorkRepositoryRequest'; 11 | import { FindUserRepositoryRequest } from 'core/domainServices/User/request/FindUserRepositoryRequest'; 12 | import { Equipment } from 'core/domain/Equipment/Equipment'; 13 | 14 | import { UnitOfWork } from 'infrastructure/database/repository/common/UnitOfWork'; 15 | 16 | @injectable() 17 | export class EquipmentUnitOfWork extends UnitOfWork 18 | implements IEquipmentUnitOfWork { 19 | constructor( 20 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.EQUIPMENT_REPOSITORY) 21 | private readonly equipmentRepository: IEquipmentRepository, 22 | @inject(DOMAIN_REPOSITORY_IDENTIFIERS.USER_REPOSITORY) 23 | private readonly userRepository: IUserRepository 24 | ) { 25 | super(); 26 | } 27 | 28 | async addEquipment({ 29 | userId, 30 | name, 31 | width, 32 | height, 33 | depth, 34 | }: AddEquipmentUnitOfWorkRepositoryRequest): Promise { 35 | const { id } = await this.userRepository.findUser( 36 | new FindUserRepositoryRequest(userId) 37 | ); 38 | 39 | return this.equipmentRepository.addEquipment( 40 | new AddEquipmentRepositoryRequest(name, width, height, depth, id) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/infrastructure/database/repository/Warehouse/WarehouseWarehouseItemRepository.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | import { EntityRepository } from 'typeorm'; 3 | 4 | import { WarehouseItem as WarehouseItemEntity } from 'infrastructure/database/entities/WarehouseItem'; 5 | import { Repository } from 'infrastructure/database/repository/common/Repository'; 6 | import { 7 | DATABASE_MAPPING_IDENTIFIERS, 8 | INFRASTRUCTURE_IDENTIFIERS, 9 | } from 'infrastructure/InfrastructureModuleSymbols'; 10 | import { DBMapper } from 'infrastructure/database/mappings/DBMapper'; 11 | import { IWarehouseWarehouseItemRepository } from 'core/domainServices/Warehouse/IWarehouseWarehouseItemRepository'; 12 | import { GetWarehouseItemsRepositoryRequest } from 'core/domainServices/WarehouseItem/requests/GetWarehouseItemsRepositoryRequest'; 13 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 14 | import { DOMAIN_MAPPING_IDENTIFIERS } from 'core/CoreModuleSymbols'; 15 | 16 | @injectable() 17 | @EntityRepository(WarehouseItemEntity) 18 | export class WarehouseWarehouseItemRepository 19 | extends Repository 20 | implements IWarehouseWarehouseItemRepository { 21 | constructor( 22 | @inject(INFRASTRUCTURE_IDENTIFIERS.DB_MAPPER) 23 | private readonly dbMapper: DBMapper 24 | ) { 25 | super(WarehouseItemEntity); 26 | } 27 | 28 | async getWarehouseItems({ 29 | warehouseId, 30 | }: GetWarehouseItemsRepositoryRequest): Promise { 31 | const warehouseItems = await this.findBy({ 32 | warehouse: { id: warehouseId }, 33 | }); 34 | 35 | return this.dbMapper.mapper.map( 36 | { 37 | destination: DOMAIN_MAPPING_IDENTIFIERS.WAREHOUSE_ITEM_DOMAIN, 38 | source: DATABASE_MAPPING_IDENTIFIERS.WAREHOUSE_ITEM_ENTITY, 39 | }, 40 | warehouseItems 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/dependency/Portal/Warehouse/WarehouseModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { IWarehouseRepository } from 'core/domainServices/Portal/Warehouse/IWarehouseRepository'; 4 | import { IWarehouseService } from 'core/applicationServices/Portal/Warehouse/IWarehouseService'; 5 | import { WarehouseService } from 'core/applicationServices/Portal/Warehouse/WarehouseService'; 6 | import { BaseModule } from 'dependency/BaseModule'; 7 | import { 8 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS, 9 | DOMAIN_REPOSITORY_IDENTIFIERS, 10 | } from 'core/CoreModuleSymbols'; 11 | import { WarehouseRepository } from 'infrastructure/database/repository/Portal/Warehouse/WarehouseRepository'; 12 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 13 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 14 | import { WarehouseQuery } from 'ui/Portal/Warehouse/WarehouseQuery'; 15 | 16 | export class WarehouseModule extends BaseModule { 17 | constructor() { 18 | super((bind: interfaces.Bind): void => { 19 | this.init(bind); 20 | }); 21 | } 22 | 23 | public init(bind: interfaces.Bind): void { 24 | this.provideWarehouseRepository(bind); 25 | 26 | this.provideWarehouseService(bind); 27 | 28 | this.provideWarehouseQuery(bind); 29 | } 30 | 31 | private provideWarehouseRepository(bind: interfaces.Bind): void { 32 | bind( 33 | DOMAIN_REPOSITORY_IDENTIFIERS.PORTAL_WAREHOUSE_REPOSITORY 34 | ).to(WarehouseRepository); 35 | } 36 | 37 | private provideWarehouseService(bind: interfaces.Bind): void { 38 | bind( 39 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.PORTAL_WAREHOUSE_SERVICE 40 | ).to(WarehouseService); 41 | } 42 | 43 | private provideWarehouseQuery(bind: interfaces.Bind): void { 44 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_WAREHOUSE_QUERIES).to( 45 | WarehouseQuery 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/dependency/Portal/Equipment/EquipmentModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 5 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 6 | import { EquipmentQuery } from 'ui/Portal/Equipment/graphql/EquipmentQueries'; 7 | import { IUserEquipmentService } from 'core/applicationServices/User/IUserEquipmentService'; 8 | import { 9 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS, 10 | DOMAIN_REPOSITORY_IDENTIFIERS, 11 | } from 'core/CoreModuleSymbols'; 12 | import { UserEquipmentService } from 'core/applicationServices/User/UserEquipmentService'; 13 | import { UserEquipmentRepository } from 'infrastructure/database/repository/User/UserEquipmentRepository'; 14 | import { IUserEquipmentRepository } from 'core/domainServices/User/IUserEquipmentRepository'; 15 | 16 | export class EquipmentModule extends BaseModule { 17 | constructor() { 18 | super((bind: interfaces.Bind): void => { 19 | this.init(bind); 20 | }); 21 | } 22 | 23 | public init(bind: interfaces.Bind): void { 24 | this.provideUserEquipmentRepository(bind); 25 | this.provideUserEquipmentService(bind); 26 | 27 | this.provideEquipmentQuery(bind); 28 | } 29 | 30 | private provideUserEquipmentRepository(bind: interfaces.Bind): void { 31 | bind( 32 | DOMAIN_REPOSITORY_IDENTIFIERS.USER_EQUIPMENT_REPOSITORY 33 | ).to(UserEquipmentRepository); 34 | } 35 | 36 | private provideUserEquipmentService(bind: interfaces.Bind): void { 37 | bind( 38 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_EQUIPMENT_SERVICE 39 | ).to(UserEquipmentService); 40 | } 41 | 42 | private provideEquipmentQuery(bind: interfaces.Bind): void { 43 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_EQUIPMENT_QUERIES).to( 44 | EquipmentQuery 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ui/Portal/Equipment/rest/v1/EquipmentController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseHttpController, 3 | controller, 4 | httpPost, 5 | request, 6 | requestBody, 7 | results, 8 | } from 'inversify-express-utils'; 9 | import { inject } from 'inversify'; 10 | 11 | import { Request } from 'express'; 12 | 13 | import { OK } from 'http-status-codes'; 14 | 15 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 16 | import { IEquipmentService } from 'core/applicationServices/Equipment/IEquipmentService'; 17 | import { CreateEquipmentRequest } from 'core/applicationServices/Equipment/requests/CreateEquipmentRequest'; 18 | import { USER_ROLE } from 'core/domain/User/UserRole'; 19 | 20 | import { CreateEquipmentRequestBody } from 'ui/Portal/Equipment/rest/v1/requests/CreateEquipmentRequestBody'; 21 | import { isAuthenticated } from 'ui/common/config/application/express/auth/middlewares/IsAuthenticated'; 22 | import { getCurrentUser } from 'ui/common/config/application/express/auth/utils/getHttpContext'; 23 | 24 | @controller('/v1/equipment') 25 | export class EquipmentController extends BaseHttpController { 26 | constructor( 27 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.EQUIPMENT_SERVICE) 28 | private readonly equipmentService: IEquipmentService 29 | ) { 30 | super(); 31 | } 32 | 33 | @httpPost('/', isAuthenticated({ role: USER_ROLE.MEMBER })) 34 | public async create( 35 | @requestBody() 36 | { name, width, height, depth }: CreateEquipmentRequestBody, 37 | @request() 38 | req: Request 39 | ): Promise { 40 | const currentUser = getCurrentUser(req); 41 | 42 | const createdEquipment = await this.equipmentService.createEquipment( 43 | new CreateEquipmentRequest( 44 | name, 45 | width, 46 | height, 47 | depth, 48 | currentUser.details!.id 49 | ) 50 | ); 51 | return this.json(createdEquipment, OK); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/ui/Portal/Equipment/graphql/EquipmentQueries.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { Context } from 'ui/common/config/application/apollo/types/Context'; 7 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 8 | import { IUserEquipmentService } from 'core/applicationServices/User/IUserEquipmentService'; 9 | import { FetchUserEquipmentRequest } from 'core/applicationServices/User/requests/FetchUserEquipmentRequest'; 10 | import { CalculateUserEquipmentCostRequest } from 'core/applicationServices/User/requests/CalculateUserEquipmentCostRequest'; 11 | import { CalculateEquipmentCostInput } from 'ui/Portal/Equipment/graphql/inputs/CalculateEquipmentCostInput'; 12 | 13 | @injectable() 14 | export class EquipmentQuery implements IResolver { 15 | readonly resolvers: IResolverObject; 16 | 17 | constructor( 18 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.USER_EQUIPMENT_SERVICE) 19 | private readonly userEquipmentService: IUserEquipmentService 20 | ) { 21 | this.resolvers = { 22 | myEquipment: this.myEquipment, 23 | calculateEquipmentCost: this.calculateEquipmentCost, 24 | }; 25 | } 26 | 27 | private myEquipment = (_root: unknown, _args: unknown, { viewer }: Context) => 28 | this.userEquipmentService.fetchEquipment( 29 | new FetchUserEquipmentRequest(viewer!.id) 30 | ); 31 | 32 | private calculateEquipmentCost = ( 33 | _root: unknown, 34 | { 35 | input: { equipmentID, warehouseID }, 36 | }: { input: CalculateEquipmentCostInput }, 37 | { viewer }: Context 38 | ) => 39 | this.userEquipmentService.calculateEquipmentCost( 40 | new CalculateUserEquipmentCostRequest( 41 | equipmentID, 42 | warehouseID, 43 | viewer!.id 44 | ) 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/ui/Administration/Warehouse/graphql/WarehouseSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IWarehouseService } from 'core/applicationServices/Warehouse/IWarehouseService'; 8 | import { IStateService } from 'core/applicationServices/State/IStateService'; 9 | import { IWarehouseWarehouseItemService } from 'core/applicationServices/Warehouse/IWarehouseWarehouseItemService'; 10 | import { Warehouse } from 'core/domain/Warehouse/Warehouse'; 11 | import { FetchStateRequest } from 'core/applicationServices/State/requests/FetchStateRequest'; 12 | import { FetchWarehouseItemsRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemsRequest'; 13 | 14 | @injectable() 15 | export class WarehouseSubQuery implements IResolver { 16 | readonly resolvers: IResolverObject; 17 | 18 | constructor( 19 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_SERVICE) 20 | private readonly warehouseService: IWarehouseService, 21 | @inject( 22 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_WAREHOUSE_ITEM_SERVICE 23 | ) 24 | private readonly warehouseWarehouseItemService: IWarehouseWarehouseItemService, 25 | @inject(DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.STATE_SERVICE) 26 | private readonly stateService: IStateService 27 | ) { 28 | this.resolvers = { 29 | Warehouse: { 30 | state: this.state, 31 | warehouseItems: this.warehouseItems, 32 | }, 33 | }; 34 | } 35 | 36 | private state = ({ id }: Warehouse) => 37 | this.stateService.fetchState(new FetchStateRequest(id)); 38 | 39 | private warehouseItems = ({ id }: Warehouse) => 40 | this.warehouseWarehouseItemService.fetchWarehouseItems( 41 | new FetchWarehouseItemsRequest(id) 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/infrastructure/database/migrations/1615067499391-add_warehouse_properties_availability_capacity.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class addWarehousePropertiesAvailabilityCapacity1615067499391 4 | implements MigrationInterface { 5 | public async up(queryRunner: QueryRunner): Promise { 6 | await queryRunner.query( 7 | `ALTER TABLE "warehouse" ADD "available" boolean NOT NULL DEFAULT false` 8 | ); 9 | await queryRunner.query( 10 | `ALTER TABLE "warehouse" ADD "capacityWidth" integer NOT NULL` 11 | ); 12 | await queryRunner.query( 13 | `ALTER TABLE "warehouse" ADD "capacityHeight" integer NOT NULL` 14 | ); 15 | await queryRunner.query( 16 | `ALTER TABLE "warehouse" ADD "capacityDepth" integer NOT NULL` 17 | ); 18 | await queryRunner.query( 19 | `ALTER TABLE "warehouse" ADD "capacityWidthLoad" integer NOT NULL DEFAULT '0'` 20 | ); 21 | await queryRunner.query( 22 | `ALTER TABLE "warehouse" ADD "capacityHeightLoad" integer NOT NULL DEFAULT '0'` 23 | ); 24 | await queryRunner.query( 25 | `ALTER TABLE "warehouse" ADD "capacityDepthLoad" integer NOT NULL DEFAULT '0'` 26 | ); 27 | } 28 | 29 | public async down(queryRunner: QueryRunner): Promise { 30 | await queryRunner.query( 31 | `ALTER TABLE "warehouse" DROP COLUMN "capacityDepthLoad"` 32 | ); 33 | await queryRunner.query( 34 | `ALTER TABLE "warehouse" DROP COLUMN "capacityHeightLoad"` 35 | ); 36 | await queryRunner.query( 37 | `ALTER TABLE "warehouse" DROP COLUMN "capacityWidthLoad"` 38 | ); 39 | await queryRunner.query( 40 | `ALTER TABLE "warehouse" DROP COLUMN "capacityDepth"` 41 | ); 42 | await queryRunner.query( 43 | `ALTER TABLE "warehouse" DROP COLUMN "capacityHeight"` 44 | ); 45 | await queryRunner.query( 46 | `ALTER TABLE "warehouse" DROP COLUMN "capacityWidth"` 47 | ); 48 | await queryRunner.query(`ALTER TABLE "warehouse" DROP COLUMN "available"`); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/ui/Administration/WarehouseItem/graphql/WarehouseItemSubQuery.ts: -------------------------------------------------------------------------------- 1 | import { inject, injectable } from 'inversify'; 2 | 3 | import { IResolverObject } from 'apollo-server-express'; 4 | 5 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 6 | import { DOMAIN_APPLICATION_SERVICE_IDENTIFIERS } from 'core/CoreModuleSymbols'; 7 | import { IWarehouseItemEquipmentService } from 'core/applicationServices/WarehouseItem/IWarehouseItemEquipmentService'; 8 | import { IWarehouseItemWarehouseService } from 'core/applicationServices/WarehouseItem/IWarehouseItemWarehouseService'; 9 | import { WarehouseItem } from 'core/domain/Warehouse/WarehouseItem'; 10 | import { FetchWarehouseItemEquipmentRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemEquipmentRequest'; 11 | import { FetchWarehouseItemWarehouseRequest } from 'core/applicationServices/WarehouseItem/requests/FetchWarehouseItemWarehouseRequest'; 12 | 13 | @injectable() 14 | export class WarehouseItemSubQuery implements IResolver { 15 | readonly resolvers: IResolverObject; 16 | 17 | constructor( 18 | @inject( 19 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_ITEM_EQUIPMENT_SERVICE 20 | ) 21 | private readonly warehouseItemEquipmentService: IWarehouseItemEquipmentService, 22 | @inject( 23 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.WAREHOUSE_ITEM_WAREHOUSE_SERVICE 24 | ) 25 | private readonly warehouseItemWarehouseService: IWarehouseItemWarehouseService 26 | ) { 27 | this.resolvers = { 28 | WarehouseItem: { 29 | equipment: this.equipment, 30 | warehouse: this.warehouse, 31 | }, 32 | }; 33 | } 34 | 35 | private equipment = ({ id }: WarehouseItem) => 36 | this.warehouseItemEquipmentService.fetchEquipment( 37 | new FetchWarehouseItemEquipmentRequest(id) 38 | ); 39 | 40 | private warehouse = ({ id }: WarehouseItem) => 41 | this.warehouseItemWarehouseService.fetchWarehouse( 42 | new FetchWarehouseItemWarehouseRequest(id) 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/dependency/Portal/WarehouseItem/WarehouseItemModule.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from 'inversify'; 2 | 3 | import { BaseModule } from 'dependency/BaseModule'; 4 | import { 5 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS, 6 | DOMAIN_REPOSITORY_IDENTIFIERS, 7 | } from 'core/CoreModuleSymbols'; 8 | import { WarehouseItemRepository } from 'infrastructure/database/repository/Portal/WarehouseItem/WarehouseItemRepository'; 9 | import { IWarehouseItemRepository } from 'core/domainServices/Portal/WarehouseItem/IWarehouseItemRepository'; 10 | import { IWarehouseItemService } from 'core/applicationServices/Portal/WarehouseItem/IWarehouseItemService'; 11 | import { WarehouseItemService } from 'core/applicationServices/Portal/WarehouseItem/WarehouseItemService'; 12 | import { IResolver } from 'ui/common/config/application/apollo/common/IResolver'; 13 | import { UI_SCHEMA_IDENTIFIERS } from 'ui/UiModuleSymbols'; 14 | import { WarehouseItemMutation } from 'ui/Portal/WarehouseItem/graphql/WarehouseItemMutation'; 15 | 16 | export class WarehouseItemModule extends BaseModule { 17 | constructor() { 18 | super((bind: interfaces.Bind): void => { 19 | this.init(bind); 20 | }); 21 | } 22 | 23 | public init(bind: interfaces.Bind): void { 24 | this.provideWarehouseItemRepository(bind); 25 | 26 | this.provideWarehouseItemService(bind); 27 | 28 | this.provideWarehouseItemMutation(bind); 29 | } 30 | 31 | private provideWarehouseItemRepository(bind: interfaces.Bind) { 32 | bind( 33 | DOMAIN_REPOSITORY_IDENTIFIERS.PORTAL_WAREHOUSE_ITEM_REPOSITORY 34 | ).to(WarehouseItemRepository); 35 | } 36 | 37 | private provideWarehouseItemService(bind: interfaces.Bind) { 38 | bind( 39 | DOMAIN_APPLICATION_SERVICE_IDENTIFIERS.PORTAL_WAREHOUSE_ITEM_SERVICE 40 | ).to(WarehouseItemService); 41 | } 42 | 43 | private provideWarehouseItemMutation(bind: interfaces.Bind): void { 44 | bind(UI_SCHEMA_IDENTIFIERS.PORTAL_WAREHOUSE_ITEM_MUTATIONS).to( 45 | WarehouseItemMutation 46 | ); 47 | } 48 | } 49 | --------------------------------------------------------------------------------