seperated backend and frontend
This commit is contained in:
56
backend/.gitignore
vendored
Normal file
56
backend/.gitignore
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
/build
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# temp directory
|
||||
.temp
|
||||
.tmp
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
4
backend/.prettierrc
Normal file
4
backend/.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
17
backend/Dockerfile
Normal file
17
backend/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
# Copy everything from root
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
CMD ["node", "dist/server.js"]
|
||||
98
backend/README.md
Normal file
98
backend/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||
|
||||
## Project setup
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Compile and run the project
|
||||
|
||||
```bash
|
||||
# development
|
||||
$ npm run start
|
||||
|
||||
# watch mode
|
||||
$ npm run start:dev
|
||||
|
||||
# production mode
|
||||
$ npm run start:prod
|
||||
```
|
||||
|
||||
## Run tests
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
$ npm run test
|
||||
|
||||
# e2e tests
|
||||
$ npm run test:e2e
|
||||
|
||||
# test coverage
|
||||
$ npm run test:cov
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
|
||||
|
||||
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
|
||||
|
||||
```bash
|
||||
$ npm install -g @nestjs/mau
|
||||
$ mau deploy
|
||||
```
|
||||
|
||||
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
|
||||
|
||||
## Resources
|
||||
|
||||
Check out a few resources that may come in handy when working with NestJS:
|
||||
|
||||
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
|
||||
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
|
||||
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
|
||||
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
|
||||
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
|
||||
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
|
||||
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
|
||||
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
||||
35
backend/eslint.config.mjs
Normal file
35
backend/eslint.config.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
// @ts-check
|
||||
import eslint from '@eslint/js';
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ['eslint.config.mjs'],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
},
|
||||
sourceType: 'commonjs',
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
"prettier/prettier": ["error", { endOfLine: "auto" }],
|
||||
},
|
||||
},
|
||||
);
|
||||
8
backend/nest-cli.json
Normal file
8
backend/nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
||||
11463
backend/package-lock.json
generated
Normal file
11463
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
81
backend/package.json
Normal file
81
backend/package.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/server": "^5.1.0",
|
||||
"@as-integrations/express5": "^1.1.2",
|
||||
"@nestjs/apollo": "^13.2.1",
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/graphql": "^13.2.0",
|
||||
"@nestjs/mongoose": "^11.0.3",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"graphql": "^16.12.0",
|
||||
"graphql-fields": "^2.0.3",
|
||||
"mongoose": "^8.19.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
"@nestjs/schematics": "^11.0.0",
|
||||
"@nestjs/testing": "^11.0.1",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/graphql": "^14.2.3",
|
||||
"@types/graphql-fields": "^1.3.9",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-prettier": "^5.2.2",
|
||||
"globals": "^16.0.0",
|
||||
"jest": "^30.0.0",
|
||||
"prettier": "^3.4.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^7.0.0",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-loader": "^9.5.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.20.0"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
22
backend/src/app.controller.spec.ts
Normal file
22
backend/src/app.controller.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
describe('root', () => {
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.getHello()).toBe('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
12
backend/src/app.controller.ts
Normal file
12
backend/src/app.controller.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) { }
|
||||
|
||||
@Get()
|
||||
getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
}
|
||||
33
backend/src/app.module.ts
Normal file
33
backend/src/app.module.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { PeopleModule } from './people/people.module';
|
||||
import { BuildService } from './build/build.service';
|
||||
import { BuildModule } from './build/build.module';
|
||||
import { BuildPartsModule } from './build-parts/build-parts.module';
|
||||
import { BuildAreaModule } from './build-area/build-area.module';
|
||||
import { UserTypesModule } from './user-types/user-types.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
||||
driver: ApolloDriver,
|
||||
autoSchemaFile: true,
|
||||
playground: true,
|
||||
}),
|
||||
MongooseModule.forRoot(process.env.MONGODB_URI || "mongodb://evyosdbuser:evyosdbpassword@10.10.2.13:27017/evyosdb"),
|
||||
UsersModule,
|
||||
PeopleModule,
|
||||
BuildModule,
|
||||
BuildPartsModule,
|
||||
BuildAreaModule,
|
||||
UserTypesModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService, BuildService],
|
||||
})
|
||||
export class AppModule { }
|
||||
8
backend/src/app.service.ts
Normal file
8
backend/src/app.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
11
backend/src/build-area/build-area.module.ts
Normal file
11
backend/src/build-area/build-area.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BuildAreaResolver } from './build-area.resolver';
|
||||
import { BuildAreaService } from './build-area.service';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { BuildArea, BuildAreaSchema } from '@/models/build-area.model';
|
||||
|
||||
@Module({
|
||||
imports: [MongooseModule.forFeature([{ name: BuildArea.name, schema: BuildAreaSchema }])],
|
||||
providers: [BuildAreaResolver, BuildAreaService]
|
||||
})
|
||||
export class BuildAreaModule { }
|
||||
18
backend/src/build-area/build-area.resolver.spec.ts
Normal file
18
backend/src/build-area/build-area.resolver.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { BuildAreaResolver } from './build-area.resolver';
|
||||
|
||||
describe('BuildAreaResolver', () => {
|
||||
let resolver: BuildAreaResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [BuildAreaResolver],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<BuildAreaResolver>(BuildAreaResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
28
backend/src/build-area/build-area.resolver.ts
Normal file
28
backend/src/build-area/build-area.resolver.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql';
|
||||
import { Types } from 'mongoose';
|
||||
import { BuildArea } from '@/models/build-area.model';
|
||||
import { CreateBuildAreaInput } from './dto/create-build-area.input';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { BuildAreaService } from './build-area.service';
|
||||
import type { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
@Resolver()
|
||||
export class BuildAreaResolver {
|
||||
|
||||
constructor(private readonly buildAreaService: BuildAreaService) { }
|
||||
|
||||
@Query(() => [BuildArea], { name: 'BuildAreas' })
|
||||
async getBuildAreas(@Info() info: GraphQLResolveInfo): Promise<BuildArea[]> {
|
||||
const fields = graphqlFields(info); const projection = this.buildAreaService.buildProjection(fields); return this.buildAreaService.findAll(projection);
|
||||
}
|
||||
|
||||
@Query(() => BuildArea, { name: 'BuildArea', nullable: true })
|
||||
async getBuildArea(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<BuildArea | null> {
|
||||
const fields = graphqlFields(info); const projection = this.buildAreaService.buildProjection(fields); return this.buildAreaService.findById(new Types.ObjectId(id), projection);
|
||||
}
|
||||
|
||||
@Mutation(() => BuildArea, { name: 'createBuildArea' })
|
||||
async createBuildArea(@Args('input') input: CreateBuildAreaInput): Promise<BuildArea> {
|
||||
return this.buildAreaService.create(input);
|
||||
}
|
||||
}
|
||||
18
backend/src/build-area/build-area.service.spec.ts
Normal file
18
backend/src/build-area/build-area.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { BuildAreaService } from './build-area.service';
|
||||
|
||||
describe('BuildAreaService', () => {
|
||||
let service: BuildAreaService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [BuildAreaService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<BuildAreaService>(BuildAreaService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
33
backend/src/build-area/build-area.service.ts
Normal file
33
backend/src/build-area/build-area.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Types, Model } from 'mongoose';
|
||||
import { BuildArea, BuildAreaDocument } from '@/models/build-area.model';
|
||||
import { CreateBuildAreaInput } from './dto/create-build-area.input';
|
||||
|
||||
@Injectable()
|
||||
export class BuildAreaService {
|
||||
|
||||
constructor(@InjectModel(BuildArea.name) private readonly buildAreaModel: Model<BuildAreaDocument>) { }
|
||||
|
||||
async findAll(projection?: any): Promise<BuildAreaDocument[]> {
|
||||
return this.buildAreaModel.find({}, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec();
|
||||
}
|
||||
|
||||
async findById(id: Types.ObjectId, projection?: any): Promise<BuildAreaDocument | null> {
|
||||
return this.buildAreaModel.findById(id, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec();
|
||||
}
|
||||
|
||||
async create(input: CreateBuildAreaInput): Promise<BuildAreaDocument> {
|
||||
const buildArea = new this.buildAreaModel(input);
|
||||
return buildArea.save();
|
||||
}
|
||||
|
||||
buildProjection(fields: Record<string, any>): any {
|
||||
const projection: any = {};
|
||||
for (const key in fields) {
|
||||
if (key === 'buildArea' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`buildArea.${subField}`] = 1 } }
|
||||
else { projection[key] = 1 }
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
7
backend/src/build-area/dto/create-build-area.input.ts
Normal file
7
backend/src/build-area/dto/create-build-area.input.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { InputType } from "@nestjs/graphql";
|
||||
|
||||
|
||||
@InputType()
|
||||
export class CreateBuildAreaInput {
|
||||
|
||||
}
|
||||
11
backend/src/build-parts/build-parts.module.ts
Normal file
11
backend/src/build-parts/build-parts.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BuildPartsService } from './build-parts.service';
|
||||
import { BuildPartsResolver } from './build-parts.resolver';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { BuildParts, BuildPartsSchema } from '@/models/build-parts.model';
|
||||
|
||||
@Module({
|
||||
imports: [MongooseModule.forFeature([{ name: BuildParts.name, schema: BuildPartsSchema }])],
|
||||
providers: [BuildPartsService, BuildPartsResolver]
|
||||
})
|
||||
export class BuildPartsModule { }
|
||||
18
backend/src/build-parts/build-parts.resolver.spec.ts
Normal file
18
backend/src/build-parts/build-parts.resolver.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { BuildPartsResolver } from './build-parts.resolver';
|
||||
|
||||
describe('BuildPartsResolver', () => {
|
||||
let resolver: BuildPartsResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [BuildPartsResolver],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<BuildPartsResolver>(BuildPartsResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
28
backend/src/build-parts/build-parts.resolver.ts
Normal file
28
backend/src/build-parts/build-parts.resolver.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql';
|
||||
import { Types } from 'mongoose';
|
||||
import { BuildParts } from '@/models/build-parts.model';
|
||||
import { CreateBuildPartsInput } from './dto/create-build-part.input';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import type { GraphQLResolveInfo } from 'graphql';
|
||||
import { BuildPartsService } from './build-parts.service';
|
||||
|
||||
@Resolver()
|
||||
export class BuildPartsResolver {
|
||||
|
||||
constructor(private readonly buildPartsService: BuildPartsService) { }
|
||||
|
||||
@Query(() => [BuildParts], { name: 'BuildParts' })
|
||||
async getBuildParts(@Info() info: GraphQLResolveInfo): Promise<BuildParts[]> {
|
||||
const fields = graphqlFields(info); const projection = this.buildPartsService.buildProjection(fields); return this.buildPartsService.findAll(projection);
|
||||
}
|
||||
|
||||
@Query(() => BuildParts, { name: 'BuildParts', nullable: true })
|
||||
async getBuildPart(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<BuildParts | null> {
|
||||
const fields = graphqlFields(info); const projection = this.buildPartsService.buildProjection(fields); return this.buildPartsService.findById(new Types.ObjectId(id), projection);
|
||||
}
|
||||
|
||||
@Mutation(() => BuildParts, { name: 'createBuildPart' })
|
||||
async createBuildPart(@Args('input') input: CreateBuildPartsInput): Promise<BuildParts> {
|
||||
return this.buildPartsService.create(input);
|
||||
}
|
||||
}
|
||||
18
backend/src/build-parts/build-parts.service.spec.ts
Normal file
18
backend/src/build-parts/build-parts.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { BuildPartsService } from './build-parts.service';
|
||||
|
||||
describe('BuildPartsService', () => {
|
||||
let service: BuildPartsService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [BuildPartsService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<BuildPartsService>(BuildPartsService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
33
backend/src/build-parts/build-parts.service.ts
Normal file
33
backend/src/build-parts/build-parts.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Types, Model } from 'mongoose';
|
||||
import { BuildParts, BuildPartsDocument } from '@/models/build-parts.model';
|
||||
import { CreateBuildPartsInput } from './dto/create-build-part.input';
|
||||
|
||||
@Injectable()
|
||||
export class BuildPartsService {
|
||||
|
||||
constructor(@InjectModel(BuildParts.name) private readonly buildPartsModel: Model<BuildPartsDocument>) { }
|
||||
|
||||
async findAll(projection?: any): Promise<BuildPartsDocument[]> {
|
||||
return this.buildPartsModel.find({}, projection, { lean: false }).populate({ path: 'buildParts', select: projection?.buildParts }).exec();
|
||||
}
|
||||
|
||||
async findById(id: Types.ObjectId, projection?: any): Promise<BuildPartsDocument | null> {
|
||||
return this.buildPartsModel.findById(id, projection, { lean: false }).populate({ path: 'buildParts', select: projection?.buildParts }).exec();
|
||||
}
|
||||
|
||||
async create(input: CreateBuildPartsInput): Promise<BuildPartsDocument> {
|
||||
const buildParts = new this.buildPartsModel(input);
|
||||
return buildParts.save();
|
||||
}
|
||||
|
||||
buildProjection(fields: Record<string, any>): any {
|
||||
const projection: any = {};
|
||||
for (const key in fields) {
|
||||
if (key === 'buildParts' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`buildParts.${subField}`] = 1 } }
|
||||
else { projection[key] = 1 }
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
7
backend/src/build-parts/dto/create-build-part.input.ts
Normal file
7
backend/src/build-parts/dto/create-build-part.input.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { InputType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
|
||||
@InputType()
|
||||
export class CreateBuildPartsInput {
|
||||
|
||||
}
|
||||
7
backend/src/build/build.module.ts
Normal file
7
backend/src/build/build.module.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BuildResolver } from './build.resolver';
|
||||
|
||||
@Module({
|
||||
providers: [BuildResolver]
|
||||
})
|
||||
export class BuildModule {}
|
||||
18
backend/src/build/build.resolver.spec.ts
Normal file
18
backend/src/build/build.resolver.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { BuildResolver } from './build.resolver';
|
||||
|
||||
describe('BuildResolver', () => {
|
||||
let resolver: BuildResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [BuildResolver],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<BuildResolver>(BuildResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
27
backend/src/build/build.resolver.ts
Normal file
27
backend/src/build/build.resolver.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql';
|
||||
import { Types } from 'mongoose';
|
||||
import { Build } from '@/models/build.model';
|
||||
import { CreateBuildInput } from './dto/create-build.input';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { BuildService } from './build.service';
|
||||
import type { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
@Resolver()
|
||||
export class BuildResolver {
|
||||
constructor(private readonly buildService: BuildService) { }
|
||||
|
||||
@Query(() => [Build], { name: 'Builds' })
|
||||
async getBuilds(@Info() info: GraphQLResolveInfo): Promise<Build[]> {
|
||||
const fields = graphqlFields(info); const projection = this.buildService.buildProjection(fields); return this.buildService.findAll(projection);
|
||||
}
|
||||
|
||||
@Query(() => Build, { name: 'Build', nullable: true })
|
||||
async getBuild(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<Build | null> {
|
||||
const fields = graphqlFields(info); const projection = this.buildService.buildProjection(fields); return this.buildService.findById(new Types.ObjectId(id), projection);
|
||||
}
|
||||
|
||||
@Mutation(() => Build, { name: 'createBuild' })
|
||||
async createBuild(@Args('input') input: CreateBuildInput): Promise<Build> {
|
||||
return this.buildService.create(input);
|
||||
}
|
||||
}
|
||||
18
backend/src/build/build.service.spec.ts
Normal file
18
backend/src/build/build.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { BuildService } from './build.service';
|
||||
|
||||
describe('BuildService', () => {
|
||||
let service: BuildService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [BuildService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<BuildService>(BuildService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
33
backend/src/build/build.service.ts
Normal file
33
backend/src/build/build.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Types, Model } from 'mongoose';
|
||||
import { Build, BuildDocument } from '@/models/build.model';
|
||||
import { CreateBuildInput } from './dto/create-build.input';
|
||||
|
||||
@Injectable()
|
||||
export class BuildService {
|
||||
|
||||
constructor(@InjectModel(Build.name) private readonly buildModel: Model<BuildDocument>) { }
|
||||
|
||||
async findAll(projection?: any): Promise<BuildDocument[]> {
|
||||
return this.buildModel.find({}, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec();
|
||||
}
|
||||
|
||||
async findById(id: Types.ObjectId, projection?: any): Promise<BuildDocument | null> {
|
||||
return this.buildModel.findById(id, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec();
|
||||
}
|
||||
|
||||
async create(input: CreateBuildInput): Promise<BuildDocument> {
|
||||
const buildArea = new this.buildModel(input);
|
||||
return buildArea.save();
|
||||
}
|
||||
|
||||
buildProjection(fields: Record<string, any>): any {
|
||||
const projection: any = {};
|
||||
for (const key in fields) {
|
||||
if (key === 'buildArea' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`buildArea.${subField}`] = 1 } }
|
||||
else { projection[key] = 1 }
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
8
backend/src/build/dto/create-build.input.ts
Normal file
8
backend/src/build/dto/create-build.input.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { InputType } from "@nestjs/graphql";
|
||||
|
||||
|
||||
@InputType()
|
||||
export class CreateBuildInput {
|
||||
|
||||
|
||||
}
|
||||
8
backend/src/main.ts
Normal file
8
backend/src/main.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(process.env.PORT ?? 3001);
|
||||
}
|
||||
bootstrap();
|
||||
49
backend/src/models/base.model.ts
Normal file
49
backend/src/models/base.model.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Prop } from '@nestjs/mongoose';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
export class Base {
|
||||
@Prop({ default: randomUUID, unique: true })
|
||||
uuid: string;
|
||||
|
||||
@Prop({ default: () => new Date(Date.now()) })
|
||||
expiryStarts: Date;
|
||||
|
||||
@Prop({ default: () => new Date('2099-12-31') })
|
||||
expiryEnds: Date;
|
||||
|
||||
@Prop({ default: false })
|
||||
isConfirmed: boolean;
|
||||
|
||||
@Prop({ default: false })
|
||||
deleted: boolean;
|
||||
|
||||
@Prop({ default: true })
|
||||
active: boolean;
|
||||
|
||||
@Prop({ default: randomUUID })
|
||||
crypUuId: string;
|
||||
|
||||
@Prop({ default: randomUUID })
|
||||
createdCredentialsToken: string;
|
||||
|
||||
@Prop({ default: randomUUID })
|
||||
updatedCredentialsToken: string;
|
||||
|
||||
@Prop({ default: randomUUID })
|
||||
confirmedCredentialsToken: string;
|
||||
|
||||
@Prop({ default: false })
|
||||
isNotificationSend: boolean;
|
||||
|
||||
@Prop({ default: false })
|
||||
isEmailSend: boolean;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
refInt: number;
|
||||
|
||||
@Prop({ default: randomUUID })
|
||||
refId: string;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
replicationId: number;
|
||||
}
|
||||
47
backend/src/models/build-address.model.ts
Normal file
47
backend/src/models/build-address.model.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { ObjectType, Field, ID, Float } from '@nestjs/graphql';
|
||||
import { Base } from '@/models/base.model';
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class BuildAddress extends Base {
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
buildNumber: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
doorNumber: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
floorNumber: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
commentAddress: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
letterAddress: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
shortLetterAddress: string;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
latitude: number;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
longitude: number;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: 'Street', required: true })
|
||||
street: Types.ObjectId;
|
||||
}
|
||||
|
||||
export type BuildAddressDocument = BuildAddress & Document;
|
||||
export const BuildAddressSchema = SchemaFactory.createForClass(BuildAddress);
|
||||
36
backend/src/models/build-area.model.ts
Normal file
36
backend/src/models/build-area.model.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { ObjectType, Field, ID, Float } from '@nestjs/graphql';
|
||||
import { Base } from '@/models/base.model';
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class BuildArea extends Base {
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: 'Build', required: true })
|
||||
build: Types.ObjectId;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
area: number;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
size: number;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
type: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
typeToken: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
description: string;
|
||||
}
|
||||
|
||||
export type BuildAreaDocument = BuildArea & Document;
|
||||
export const BuildAreaSchema = SchemaFactory.createForClass(BuildArea);
|
||||
60
backend/src/models/build-parts.model.ts
Normal file
60
backend/src/models/build-parts.model.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { ObjectType, Field, ID, Float } from '@nestjs/graphql';
|
||||
import { Base } from '@/models/base.model';
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class BuildParts extends Base {
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: 'Build', required: true })
|
||||
buildId: Types.ObjectId;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
addressGovCode: string;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
no: number;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
level: number;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
code: string;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
grossSize: number;
|
||||
|
||||
@Field(() => Float)
|
||||
@Prop({ required: true })
|
||||
netSize: number;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
defaultAccessory: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
humanLivability: boolean;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
key: string;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: 'ApiEnumDropdown', required: true })
|
||||
directionId: Types.ObjectId;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: 'ApiEnumDropdown', required: true })
|
||||
typeId: Types.ObjectId;
|
||||
}
|
||||
|
||||
export type BuildPartsDocument = BuildParts & Document;
|
||||
export const BuildPartsSchema = SchemaFactory.createForClass(BuildParts);
|
||||
146
backend/src/models/build.model.ts
Normal file
146
backend/src/models/build.model.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { ObjectType, Field, ID, Int } from '@nestjs/graphql';
|
||||
import { Base } from '@/models/base.model';
|
||||
import { Person } from '@/models/person.model';
|
||||
import { Company } from '@/models/company.model';
|
||||
|
||||
@ObjectType()
|
||||
export class BuildIban {
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
iban: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
startDate: Date;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, default: new Date('2900-01-01T03:00:00+03:00') })
|
||||
stopDate: Date;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, default: 'TR0000000000000' })
|
||||
bankCode: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, default: '????' })
|
||||
xcomment: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class BuildResponsible {
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: Company.name, required: true })
|
||||
company: Types.ObjectId;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: Person.name, required: true })
|
||||
person: Types.ObjectId;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class BuildInfo {
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
govAddressCode: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
buildName: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
buildNo: string;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
maxFloor: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
undergroundFloor: number;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
buildDate: Date;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
decisionPeriodDate: Date;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
taxNo: string;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
liftCount: number;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
heatingSystem: boolean;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
coolingSystem: boolean;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
hotWaterSystem: boolean;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
blockServiceManCount: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
securityServiceManCount: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
garageCount: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Prop({ required: true })
|
||||
managementRoomId: number;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class Build extends Base {
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: 'BuildType', required: true })
|
||||
buildType: Types.ObjectId;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, unique: true })
|
||||
collectionToken: string;
|
||||
|
||||
@Field(() => BuildInfo)
|
||||
@Prop({ type: BuildInfo, required: true })
|
||||
info: BuildInfo;
|
||||
|
||||
@Field(() => ID, { nullable: true })
|
||||
@Prop({ type: Types.ObjectId, ref: 'Site' })
|
||||
site?: Types.ObjectId;
|
||||
|
||||
@Field(() => ID, { nullable: true })
|
||||
@Prop({ type: Types.ObjectId, ref: 'BuildAddress' })
|
||||
address?: Types.ObjectId;
|
||||
|
||||
@Field(() => [ID], { nullable: true })
|
||||
@Prop({ type: [{ type: Types.ObjectId, ref: 'BuildArea' }] })
|
||||
areas?: Types.ObjectId[];
|
||||
|
||||
@Field(() => [BuildIban], { nullable: true })
|
||||
@Prop({ type: [BuildIban] })
|
||||
ibans?: BuildIban[];
|
||||
|
||||
@Field(() => [BuildResponsible], { nullable: true })
|
||||
@Prop({ type: [BuildResponsible] })
|
||||
responsibles?: BuildResponsible[];
|
||||
}
|
||||
|
||||
export type BuildDocument = Build & Document;
|
||||
export const BuildSchema = SchemaFactory.createForClass(Build);
|
||||
16
backend/src/models/company.model.ts
Normal file
16
backend/src/models/company.model.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { Base } from '@/models/base.model';
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class Company extends Base {
|
||||
@Field(() => ID)
|
||||
@Prop({ required: true })
|
||||
name: string;
|
||||
|
||||
}
|
||||
|
||||
export type CompanyDocument = Company & Document;
|
||||
export const CompanySchema = SchemaFactory.createForClass(Company);
|
||||
41
backend/src/models/living-spaces.model.ts
Normal file
41
backend/src/models/living-spaces.model.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Document, Model, Types, Connection, connection as defaultConnection } from 'mongoose';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { Prop, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Base } from '@/models/base.model';
|
||||
import { BuildParts } from '@/models/build-parts.model';
|
||||
import { Person } from '@/models/person.model';
|
||||
import { Company } from '@/models/company.model';
|
||||
import { UserType } from '@/models/user-type.model';
|
||||
|
||||
@ObjectType()
|
||||
export class LivingSpaces extends Base {
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: BuildParts.name, required: true })
|
||||
part: Types.ObjectId;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: Company.name, required: true })
|
||||
company: Types.ObjectId;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: Person.name, required: true })
|
||||
person: Types.ObjectId;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: UserType.name, required: true })
|
||||
userType: Types.ObjectId;
|
||||
}
|
||||
|
||||
|
||||
export type LivingSpacesDocument = LivingSpaces & Document;
|
||||
export const LivingSpacesSchema = SchemaFactory.createForClass(LivingSpaces);
|
||||
|
||||
export function getDynamicLivingSpaceModel(collectionToken: string, conn?: Connection): Model<LivingSpacesDocument> {
|
||||
const connection = conn || defaultConnection;
|
||||
const collectionName = `LivingSpaces${collectionToken}`;
|
||||
if (connection.models[collectionName]) { return connection.models[collectionName] as Model<LivingSpacesDocument> }
|
||||
const schema = SchemaFactory.createForClass(LivingSpaces);
|
||||
schema.add({ uuid: { type: String, required: false, unique: true, default: () => randomUUID() } });
|
||||
return connection.model(collectionName, schema, collectionName) as unknown as Model<LivingSpacesDocument>;
|
||||
}
|
||||
51
backend/src/models/person.model.ts
Normal file
51
backend/src/models/person.model.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
import { Base } from '@/models/base.model';
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Person extends Base {
|
||||
@Prop({ required: true })
|
||||
firstName: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
surname: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
middleName: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
sexCode: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
personRef: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
personTag: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
fatherName: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
motherName: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
countryCode: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
nationalIdentityId: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
birthPlace: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
birthDate: Date;
|
||||
|
||||
@Prop({ required: true })
|
||||
taxNo: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
birthname: string;
|
||||
}
|
||||
|
||||
export type PersonDocument = Person & Document;
|
||||
export const PersonSchema = SchemaFactory.createForClass(Person);
|
||||
26
backend/src/models/user-type.model.ts
Normal file
26
backend/src/models/user-type.model.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class UserType {
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
type: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
token: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
typeToken: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
description: string;
|
||||
}
|
||||
|
||||
export type UserTypeDocument = UserType & Document;
|
||||
export const UserTypeSchema = SchemaFactory.createForClass(UserType);
|
||||
81
backend/src/models/user.model.ts
Normal file
81
backend/src/models/user.model.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { Base } from '@/models/base.model';
|
||||
import { Person } from '@/models/person.model';
|
||||
import { UserType } from '@/models/user-type.model';
|
||||
|
||||
@ObjectType()
|
||||
export class CollectionTokenItem {
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
prefix: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
token: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class CollectionToken {
|
||||
|
||||
@Field(() => [CollectionTokenItem])
|
||||
@Prop({ type: [CollectionTokenItem], default: [] })
|
||||
tokens: CollectionTokenItem[];
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, default: '' })
|
||||
default: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
@Schema({ timestamps: true })
|
||||
export class User extends Base {
|
||||
|
||||
@Field(() => ID)
|
||||
_id: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Prop({ default: () => new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) })
|
||||
expiresAt?: Date;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Prop()
|
||||
resetToken?: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
password: string;
|
||||
|
||||
@Field(() => [String], { nullable: 'itemsAndList' })
|
||||
@Prop({ type: [String], default: [], validate: [(val: string[]) => val.length <= 3, 'History can have max 3 items'] })
|
||||
history?: string[];
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true })
|
||||
tag: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, unique: true })
|
||||
email: string;
|
||||
|
||||
@Field()
|
||||
@Prop({ required: true, unique: true })
|
||||
phone: string;
|
||||
|
||||
@Field(() => CollectionToken)
|
||||
@Prop({ type: CollectionToken, default: () => ({ tokens: [], default: '' }) })
|
||||
collectionTokens: CollectionToken;
|
||||
|
||||
@Field(() => ID)
|
||||
@Prop({ type: Types.ObjectId, ref: Person.name, required: true })
|
||||
person: Types.ObjectId;
|
||||
|
||||
@Field(() => ID, { nullable: true })
|
||||
@Prop({ type: Types.ObjectId, ref: UserType.name })
|
||||
type?: Types.ObjectId;
|
||||
}
|
||||
|
||||
export type UserDocument = User & Document;
|
||||
export const UserSchema = SchemaFactory.createForClass(User);
|
||||
7
backend/src/people/dto/create-person.input.ts
Normal file
7
backend/src/people/dto/create-person.input.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { InputType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
|
||||
@InputType()
|
||||
export class CreatePersonInput {
|
||||
|
||||
}
|
||||
11
backend/src/people/people.module.ts
Normal file
11
backend/src/people/people.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PeopleResolver } from './people.resolver';
|
||||
import { PeopleService } from './people.service';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { Person, PersonSchema } from '@/models/person.model';
|
||||
|
||||
@Module({
|
||||
imports: [MongooseModule.forFeature([{ name: Person.name, schema: PersonSchema }])],
|
||||
providers: [PeopleResolver, PeopleService]
|
||||
})
|
||||
export class PeopleModule { }
|
||||
18
backend/src/people/people.resolver.spec.ts
Normal file
18
backend/src/people/people.resolver.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PeopleResolver } from './people.resolver';
|
||||
|
||||
describe('PeopleResolver', () => {
|
||||
let resolver: PeopleResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [PeopleResolver],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<PeopleResolver>(PeopleResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
29
backend/src/people/people.resolver.ts
Normal file
29
backend/src/people/people.resolver.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql';
|
||||
import { Types } from 'mongoose';
|
||||
import { Person } from '@/models/person.model';
|
||||
import { PeopleService } from '@/people/people.service';
|
||||
import { CreatePersonInput } from './dto/create-person.input';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import type { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
@Resolver()
|
||||
export class PeopleResolver {
|
||||
|
||||
constructor(private readonly peopleService: PeopleService) { }
|
||||
|
||||
@Query(() => [Person], { name: 'Persons' })
|
||||
async getPersons(@Info() info: GraphQLResolveInfo): Promise<Person[]> {
|
||||
const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findAll(projection);
|
||||
}
|
||||
|
||||
@Query(() => Person, { name: 'Person', nullable: true })
|
||||
async getPerson(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<Person | null> {
|
||||
const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findById(new Types.ObjectId(id), projection);
|
||||
}
|
||||
|
||||
@Mutation(() => Person, { name: 'createPerson' })
|
||||
async createPerson(@Args('input') input: CreatePersonInput): Promise<Person> {
|
||||
return this.peopleService.create(input);
|
||||
}
|
||||
|
||||
}
|
||||
18
backend/src/people/people.service.spec.ts
Normal file
18
backend/src/people/people.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PeopleService } from './people.service';
|
||||
|
||||
describe('PeopleService', () => {
|
||||
let service: PeopleService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [PeopleService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PeopleService>(PeopleService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
36
backend/src/people/people.service.ts
Normal file
36
backend/src/people/people.service.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Types, Model } from 'mongoose';
|
||||
import { Person, PersonDocument } from '@/models/person.model';
|
||||
import { CreatePersonInput } from './dto/create-person.input';
|
||||
|
||||
@Injectable()
|
||||
export class PeopleService {
|
||||
|
||||
constructor(
|
||||
@InjectModel(Person.name) private readonly personModel: Model<PersonDocument>
|
||||
) { }
|
||||
|
||||
async findAll(projection?: any): Promise<PersonDocument[]> {
|
||||
return this.personModel.find({}, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec();
|
||||
}
|
||||
|
||||
async findById(id: Types.ObjectId, projection?: any): Promise<PersonDocument | null> {
|
||||
return this.personModel.findById(id, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec();
|
||||
}
|
||||
|
||||
async create(input: CreatePersonInput): Promise<PersonDocument> {
|
||||
const person = new this.personModel(input);
|
||||
return person.save();
|
||||
}
|
||||
|
||||
buildProjection(fields: Record<string, any>): any {
|
||||
const projection: any = {};
|
||||
for (const key in fields) {
|
||||
if (key === 'person' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`person.${subField}`] = 1 } }
|
||||
else { projection[key] = 1 }
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
|
||||
}
|
||||
61
backend/src/schema.gql
Normal file
61
backend/src/schema.gql
Normal file
@@ -0,0 +1,61 @@
|
||||
# ------------------------------------------------------
|
||||
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
||||
# ------------------------------------------------------
|
||||
|
||||
type CollectionTokenItem {
|
||||
prefix: String!
|
||||
token: String!
|
||||
}
|
||||
|
||||
type CollectionToken {
|
||||
tokens: [CollectionTokenItem!]!
|
||||
default: String!
|
||||
}
|
||||
|
||||
type User {
|
||||
expiresAt: DateTime
|
||||
resetToken: String
|
||||
password: String!
|
||||
history: [String]
|
||||
tag: String!
|
||||
email: String!
|
||||
phone: String!
|
||||
collectionTokens: CollectionToken!
|
||||
person: ID!
|
||||
type: ID
|
||||
}
|
||||
|
||||
"""
|
||||
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
|
||||
"""
|
||||
scalar DateTime
|
||||
|
||||
type Query {
|
||||
users: [User!]!
|
||||
user(id: ID!): User
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(input: CreateUserInput!): User!
|
||||
}
|
||||
|
||||
input CreateUserInput {
|
||||
password: String!
|
||||
history: [String!]
|
||||
tag: String!
|
||||
email: String!
|
||||
phone: String!
|
||||
collectionTokens: CollectionTokenInput!
|
||||
person: ID!
|
||||
type: ID
|
||||
}
|
||||
|
||||
input CollectionTokenInput {
|
||||
tokens: [CollectionTokenItemInput!]!
|
||||
default: String!
|
||||
}
|
||||
|
||||
input CollectionTokenItemInput {
|
||||
prefix: String!
|
||||
token: String!
|
||||
}
|
||||
7
backend/src/user-types/dto/create-user-types.input.ts
Normal file
7
backend/src/user-types/dto/create-user-types.input.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { InputType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
|
||||
@InputType()
|
||||
export class CreateUserTypesInput {
|
||||
|
||||
}
|
||||
8
backend/src/user-types/user-types.module.ts
Normal file
8
backend/src/user-types/user-types.module.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UserTypesResolver } from './user-types.resolver';
|
||||
import { UserTypesService } from './user-types.service';
|
||||
|
||||
@Module({
|
||||
providers: [UserTypesResolver, UserTypesService]
|
||||
})
|
||||
export class UserTypesModule {}
|
||||
18
backend/src/user-types/user-types.resolver.spec.ts
Normal file
18
backend/src/user-types/user-types.resolver.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UserTypesResolver } from './user-types.resolver';
|
||||
|
||||
describe('UserTypesResolver', () => {
|
||||
let resolver: UserTypesResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UserTypesResolver],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<UserTypesResolver>(UserTypesResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
29
backend/src/user-types/user-types.resolver.ts
Normal file
29
backend/src/user-types/user-types.resolver.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql';
|
||||
import { Types } from 'mongoose';
|
||||
import { UserType } from '@/models/user-type.model';
|
||||
import { UserTypesService } from '@/user-types/user-types.service';
|
||||
import type { GraphQLResolveInfo } from 'graphql';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { CreateUserTypesInput } from './dto/create-user-types.input';
|
||||
|
||||
@Resolver()
|
||||
export class UserTypesResolver {
|
||||
|
||||
constructor(private readonly userTypesService: UserTypesService) { }
|
||||
|
||||
@Query(() => [UserType], { name: 'userTypes' })
|
||||
async getUsers(@Info() info: GraphQLResolveInfo): Promise<UserType[]> {
|
||||
const fields = graphqlFields(info); const projection = this.userTypesService.buildProjection(fields); return this.userTypesService.findAll(projection);
|
||||
}
|
||||
|
||||
@Query(() => UserType, { name: 'userType', nullable: true })
|
||||
async getUserType(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<UserType | null> {
|
||||
const fields = graphqlFields(info); const projection = this.userTypesService.buildProjection(fields); return this.userTypesService.findById(new Types.ObjectId(id), projection);
|
||||
}
|
||||
|
||||
@Mutation(() => UserType, { name: 'createUserType' })
|
||||
async createUserType(@Args('input') input: CreateUserTypesInput): Promise<UserType> {
|
||||
return this.userTypesService.create(input);
|
||||
}
|
||||
|
||||
}
|
||||
18
backend/src/user-types/user-types.service.spec.ts
Normal file
18
backend/src/user-types/user-types.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UserTypesService } from './user-types.service';
|
||||
|
||||
describe('UserTypesService', () => {
|
||||
let service: UserTypesService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UserTypesService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UserTypesService>(UserTypesService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
35
backend/src/user-types/user-types.service.ts
Normal file
35
backend/src/user-types/user-types.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateUserTypesInput } from './dto/create-user-types.input';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Types, Model } from 'mongoose';
|
||||
import { UserType, UserTypeDocument } from '@/models/user-type.model';
|
||||
|
||||
@Injectable()
|
||||
export class UserTypesService {
|
||||
|
||||
constructor(
|
||||
@InjectModel(UserType.name) private readonly userTypesModel: Model<UserTypeDocument>
|
||||
) { }
|
||||
|
||||
async findAll(projection?: any): Promise<UserTypeDocument[]> {
|
||||
return this.userTypesModel.find({}, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec();
|
||||
}
|
||||
|
||||
async findById(id: Types.ObjectId, projection?: any): Promise<UserTypeDocument | null> {
|
||||
return this.userTypesModel.findById(id, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec();
|
||||
}
|
||||
|
||||
async create(input: CreateUserTypesInput): Promise<UserTypeDocument> {
|
||||
const user = new this.userTypesModel(input);
|
||||
return user.save();
|
||||
}
|
||||
|
||||
buildProjection(fields: Record<string, any>): any {
|
||||
const projection: any = {};
|
||||
for (const key in fields) {
|
||||
if (key === 'user' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`user.${subField}`] = 1 } }
|
||||
else { projection[key] = 1 }
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
47
backend/src/users/dto/create-user.input.ts
Normal file
47
backend/src/users/dto/create-user.input.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { InputType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
@InputType()
|
||||
export class CollectionTokenItemInput {
|
||||
@Field()
|
||||
prefix: string;
|
||||
|
||||
@Field()
|
||||
token: string;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class CollectionTokenInput {
|
||||
@Field(() => [CollectionTokenItemInput])
|
||||
tokens: CollectionTokenItemInput[];
|
||||
|
||||
@Field()
|
||||
default: string;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class CreateUserInput {
|
||||
|
||||
@Field()
|
||||
password: string;
|
||||
|
||||
@Field(() => [String], { nullable: true })
|
||||
history?: string[];
|
||||
|
||||
@Field()
|
||||
tag: string;
|
||||
|
||||
@Field()
|
||||
email: string;
|
||||
|
||||
@Field()
|
||||
phone: string;
|
||||
|
||||
@Field(() => CollectionTokenInput)
|
||||
collectionTokens: CollectionTokenInput;
|
||||
|
||||
@Field(() => ID)
|
||||
person: string;
|
||||
|
||||
@Field(() => ID, { nullable: true })
|
||||
type?: string;
|
||||
}
|
||||
11
backend/src/users/users.module.ts
Normal file
11
backend/src/users/users.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { UsersResolver } from './users.resolver';
|
||||
import { UsersService } from './users.service';
|
||||
import { UserSchema, User } from '@/models/user.model';
|
||||
|
||||
@Module({
|
||||
imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
|
||||
providers: [UsersResolver, UsersService],
|
||||
})
|
||||
export class UsersModule { }
|
||||
18
backend/src/users/users.resolver.spec.ts
Normal file
18
backend/src/users/users.resolver.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersResolver } from './users.resolver';
|
||||
|
||||
describe('UsersResolver', () => {
|
||||
let resolver: UsersResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersResolver],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<UsersResolver>(UsersResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
29
backend/src/users/users.resolver.ts
Normal file
29
backend/src/users/users.resolver.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql';
|
||||
import { Types } from 'mongoose';
|
||||
import { User } from '@/models/user.model';
|
||||
import { UsersService } from '@/users/users.service';
|
||||
import type { GraphQLResolveInfo } from 'graphql';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { CreateUserInput } from './dto/create-user.input';
|
||||
|
||||
@Resolver(() => User)
|
||||
export class UsersResolver {
|
||||
|
||||
constructor(private readonly usersService: UsersService) { }
|
||||
|
||||
@Query(() => [User], { name: 'users' })
|
||||
async getUsers(@Info() info: GraphQLResolveInfo): Promise<User[]> {
|
||||
const fields = graphqlFields(info); const projection = this.usersService.buildProjection(fields); return this.usersService.findAll(projection);
|
||||
}
|
||||
|
||||
@Query(() => User, { name: 'user', nullable: true })
|
||||
async getUser(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<User | null> {
|
||||
const fields = graphqlFields(info); const projection = this.usersService.buildProjection(fields); return this.usersService.findById(new Types.ObjectId(id), projection);
|
||||
}
|
||||
|
||||
@Mutation(() => User, { name: 'createUser' })
|
||||
async createUser(@Args('input') input: CreateUserInput): Promise<User> {
|
||||
return this.usersService.create(input);
|
||||
}
|
||||
|
||||
}
|
||||
18
backend/src/users/users.service.spec.ts
Normal file
18
backend/src/users/users.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
35
backend/src/users/users.service.ts
Normal file
35
backend/src/users/users.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Types, Model } from 'mongoose';
|
||||
import { User, UserDocument } from '@/models/user.model';
|
||||
import { CreateUserInput } from './dto/create-user.input';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
|
||||
constructor(
|
||||
@InjectModel(User.name) private readonly userModel: Model<UserDocument>
|
||||
) { }
|
||||
|
||||
async findAll(projection?: any): Promise<UserDocument[]> {
|
||||
return this.userModel.find({}, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec();
|
||||
}
|
||||
|
||||
async findById(id: Types.ObjectId, projection?: any): Promise<UserDocument | null> {
|
||||
return this.userModel.findById(id, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec();
|
||||
}
|
||||
|
||||
async create(input: CreateUserInput): Promise<UserDocument> {
|
||||
const user = new this.userModel(input);
|
||||
return user.save();
|
||||
}
|
||||
|
||||
buildProjection(fields: Record<string, any>): any {
|
||||
const projection: any = {};
|
||||
for (const key in fields) {
|
||||
if (key === 'person' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`person.${subField}`] = 1 } }
|
||||
else { projection[key] = 1 }
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
25
backend/test/app.e2e-spec.ts
Normal file
25
backend/test/app.e2e-spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import request from 'supertest';
|
||||
import { App } from 'supertest/types';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication<App>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
9
backend/test/jest-e2e.json
Normal file
9
backend/test/jest-e2e.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
}
|
||||
4
backend/tsconfig.build.json
Normal file
4
backend/tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
30
backend/tsconfig.json
Normal file
30
backend/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"resolvePackageJsonExports": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user