change repo
This commit is contained in:
25
.eslintrc.js
Normal file
25
.eslintrc.js
Normal file
@@ -0,0 +1,25 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"nuxt.isNuxtApp": false
|
||||
}
|
||||
73
README.md
Normal file
73
README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" 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://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></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"/></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"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ pnpm install
|
||||
```
|
||||
|
||||
## Running the app
|
||||
|
||||
```bash
|
||||
# development
|
||||
$ pnpm run start
|
||||
|
||||
# watch mode
|
||||
$ pnpm run start:dev
|
||||
|
||||
# production mode
|
||||
$ pnpm run start:prod
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
$ pnpm run test
|
||||
|
||||
# e2e tests
|
||||
$ pnpm run test:e2e
|
||||
|
||||
# test coverage
|
||||
$ pnpm run test:cov
|
||||
```
|
||||
|
||||
## 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://kamilmysliwiec.com)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
||||
8
nest-cli.json
Normal file
8
nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
||||
80
package.json
Normal file
80
package.json
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "page-speed-service",
|
||||
"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": {
|
||||
"@nestjs/axios": "^3.0.1",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.1.1",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/swagger": "^7.2.0",
|
||||
"@nestjs/typeorm": "^10.0.1",
|
||||
"axios": "^1.6.5",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"mysql2": "^3.7.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.1",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"typeorm": "^0.3.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"prettier": "^3.0.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^6.3.3",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-loader": "^9.4.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
5544
pnpm-lock.yaml
generated
Normal file
5544
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
src/app.module.ts
Normal file
53
src/app.module.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ControllersController } from './controller/controllers/controllers.controller';
|
||||
import { ControllersService } from './controller/controllers/controllers.service';
|
||||
import { CustomerController } from './service/customer/customer.controller';
|
||||
import { CustomerModule } from './service/customer/customer.module';
|
||||
import { CustomerService } from './service/customer/customer.service';
|
||||
import { Customer } from './service/customer/entities/customer.entity';
|
||||
import { PageSpeedData } from './service/pagespeed/entities/pagespeeddata.entity';
|
||||
import { PagespeedModule } from './service/pagespeed/pagespeed.module';
|
||||
import { PagespeedService } from './service/pagespeed/pagespeed.service';
|
||||
import { Website } from './service/website/entities/website.entity';
|
||||
import { WebsiteController } from './service/website/website.controller';
|
||||
import { WebsiteService } from './service/website/website.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
HttpModule,
|
||||
PagespeedModule,
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
ignoreEnvFile: false,
|
||||
envFilePath: '.env',
|
||||
}),
|
||||
],
|
||||
inject: [ConfigService],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
type: 'mysql',
|
||||
host: configService.get<string>('HOST'),
|
||||
port: parseInt(configService.get<string>('PORT')),
|
||||
username: configService.get<string>('DB_USER'),
|
||||
password: configService.get<string>('PASSWORD'),
|
||||
database: configService.get<string>('DATABASE'),
|
||||
entities: [PageSpeedData, Customer, Website],
|
||||
synchronize: true,
|
||||
logging: false,
|
||||
}),
|
||||
}),
|
||||
CustomerModule,
|
||||
],
|
||||
controllers: [CustomerController, WebsiteController, ControllersController],
|
||||
providers: [
|
||||
CustomerService,
|
||||
WebsiteService,
|
||||
PagespeedService,
|
||||
ControllersService,
|
||||
ConfigService,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
20
src/controller/controllers/controllers.controller.spec.ts
Normal file
20
src/controller/controllers/controllers.controller.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ControllersController } from './controllers.controller';
|
||||
import { ControllersService } from './controllers.service';
|
||||
|
||||
describe('ControllersController', () => {
|
||||
let controller: ControllersController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [ControllersController],
|
||||
providers: [ControllersService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<ControllersController>(ControllersController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
92
src/controller/controllers/controllers.controller.ts
Normal file
92
src/controller/controllers/controllers.controller.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import {
|
||||
Body,
|
||||
ClassSerializerInterceptor,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Res,
|
||||
UseInterceptors,
|
||||
UsePipes,
|
||||
ValidationPipe,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Response } from 'express';
|
||||
import { CustomerService } from 'src/service/customer/customer.service';
|
||||
import { PageSpeedData } from 'src/service/pagespeed/entities/pagespeeddata.entity';
|
||||
import { PagespeedService } from 'src/service/pagespeed/pagespeed.service';
|
||||
import { WebsiteService } from 'src/service/website/website.service';
|
||||
|
||||
@ApiTags('pagespeed')
|
||||
@Controller('controllers')
|
||||
export class ControllersController {
|
||||
constructor(
|
||||
private readonly customerService: CustomerService,
|
||||
private readonly websiteService: WebsiteService,
|
||||
private readonly pageSpeedService: PagespeedService,
|
||||
) {}
|
||||
@Post('newpage')
|
||||
async getPageSpeedResult(
|
||||
@Body()
|
||||
body: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
url: string;
|
||||
displayName: string;
|
||||
email: string;
|
||||
dsgvo: boolean;
|
||||
},
|
||||
@Res() res: Response,
|
||||
): Promise<any> {
|
||||
try {
|
||||
const url = body.url;
|
||||
const firstName = body.firstName;
|
||||
const lastName = body.lastName;
|
||||
const email = body.email;
|
||||
const displayName = body.displayName;
|
||||
const dsgvo = body.dsgvo;
|
||||
const customer = await this.customerService.createOrUpdateCustomer({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
dsgvo,
|
||||
});
|
||||
const website = await this.websiteService.createOrUpdateWebsite({
|
||||
url,
|
||||
displayName,
|
||||
customer,
|
||||
});
|
||||
|
||||
const result = await this.pageSpeedService.getPageSpeedResult(
|
||||
url,
|
||||
website.id,
|
||||
);
|
||||
|
||||
const pageSpeedId = result.id; // Extract the pageSpeedId from the backend response to utilize it within the frontend.
|
||||
// This allows us to reference the specific page speed data on the client side,
|
||||
// enabling dynamic content updates and user-specific interactions based on the
|
||||
// performance metrics of the requested web page.
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'PageSpeed data retrieved and saved successfully.',
|
||||
pageSpeedId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error in getPageSpeedResult:', error);
|
||||
return res.status(500).json({
|
||||
message: 'An error occurred while retrieving PageSpeed data.',
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
@Get('currentresult/:id')
|
||||
@UseInterceptors(ClassSerializerInterceptor)
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
async findTheHole(@Param('id') id: string): Promise<PageSpeedData[]> {
|
||||
return this.pageSpeedService.getAllPageSpeeds(id);
|
||||
}
|
||||
@Get('gettall/:id')
|
||||
async findAll(@Param('id') id: string): Promise<PageSpeedData[]> {
|
||||
return this.pageSpeedService.getAllPageSpeeds(id);
|
||||
}
|
||||
}
|
||||
29
src/controller/controllers/controllers.module.ts
Normal file
29
src/controller/controllers/controllers.module.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CustomerService } from 'src/service/customer/customer.service';
|
||||
import { Customer } from 'src/service/customer/entities/customer.entity';
|
||||
import { PageSpeedData } from 'src/service/pagespeed/entities/pagespeeddata.entity';
|
||||
import { PagespeedService } from 'src/service/pagespeed/pagespeed.service';
|
||||
import { Website } from 'src/service/website/entities/website.entity';
|
||||
import { WebsiteService } from 'src/service/website/website.service';
|
||||
import { ControllersController } from './controllers.controller';
|
||||
import { ControllersService } from './controllers.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule,
|
||||
HttpModule,
|
||||
TypeOrmModule.forFeature([Customer, Website, PageSpeedData]),
|
||||
],
|
||||
controllers: [ControllersController],
|
||||
providers: [
|
||||
ControllersService,
|
||||
CustomerService,
|
||||
WebsiteService,
|
||||
PagespeedService,
|
||||
],
|
||||
exports: [CustomerService, WebsiteService, PagespeedService, TypeOrmModule],
|
||||
})
|
||||
export class ControllersModule {}
|
||||
18
src/controller/controllers/controllers.service.spec.ts
Normal file
18
src/controller/controllers/controllers.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ControllersService } from './controllers.service';
|
||||
|
||||
describe('ControllersService', () => {
|
||||
let service: ControllersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ControllersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ControllersService>(ControllersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
4
src/controller/controllers/controllers.service.ts
Normal file
4
src/controller/controllers/controllers.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class ControllersService {}
|
||||
1
src/controller/controllers/entities/controller.entity.ts
Normal file
1
src/controller/controllers/entities/controller.entity.ts
Normal file
@@ -0,0 +1 @@
|
||||
export class Controller {}
|
||||
32
src/interceptor/currentresult.interceptor.ts
Normal file
32
src/interceptor/currentresult.interceptor.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class CurrentResultInterceptor implements NestInterceptor {
|
||||
intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler<any>,
|
||||
): Observable<any> | Promise<Observable<any>> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const { displayName, url } = request.body;
|
||||
|
||||
if (!displayName || !url) {
|
||||
console.log('Name', displayName, 'URL', url, 'HOLE:', request.body);
|
||||
throw new HttpException('Please enter propertys', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
return next.handle().pipe(
|
||||
map((data) => {
|
||||
return data;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
34
src/main.ts
Normal file
34
src/main.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import { CreateWebsiteDto } from './service/website/dto/create-website.dto';
|
||||
import { CreateCustomerDto } from './service/customer/dto/create-customer.dto';
|
||||
import { CreatePageSpeedDto } from './service/pagespeed/dto/create-pagespeed.dto';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('PageSpeed API')
|
||||
.setDescription('PageSpeed API description')
|
||||
.setVersion('1.0')
|
||||
.addTag('pagespeed')
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, config, {
|
||||
extraModels: [CreateWebsiteDto, CreateCustomerDto, CreatePageSpeedDto],
|
||||
});
|
||||
SwaggerModule.setup('api', app, document);
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
transform: true, //activates the transformation from plain JSO.
|
||||
whitelist: true, // removes all charataristics from DTO.
|
||||
forbidNonWhitelisted: true, //deleting errors that is not wihtelisted.
|
||||
}),
|
||||
);
|
||||
//#region acception corse
|
||||
app.enableCors();
|
||||
//#endregion
|
||||
await app.listen(3001);
|
||||
}
|
||||
bootstrap();
|
||||
20
src/service/customer/customer.controller.spec.ts
Normal file
20
src/service/customer/customer.controller.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CustomerController } from './customer.controller';
|
||||
import { CustomerService } from './customer.service';
|
||||
|
||||
describe('CustomerController', () => {
|
||||
let controller: CustomerController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [CustomerController],
|
||||
providers: [CustomerService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<CustomerController>(CustomerController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
54
src/service/customer/customer.controller.ts
Normal file
54
src/service/customer/customer.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { PagespeedService } from '../pagespeed/pagespeed.service';
|
||||
import { WebsiteService } from '../website/website.service';
|
||||
import { CustomerService } from './customer.service';
|
||||
|
||||
@Controller('customer')
|
||||
export class CustomerController {
|
||||
// constructor(
|
||||
// private readonly customerService: CustomerService,
|
||||
// private readonly websiteService: WebsiteService,
|
||||
// private readonly pageSpeedService: PagespeedService,
|
||||
// ) {}
|
||||
// @Post()
|
||||
// async createCustomer(
|
||||
// @Body() createCustomerDto: CreateCustomerDto,
|
||||
// ): Promise<Customer> {
|
||||
// return this.customerService.createOrUpdateCustomer(createCustomerDto);
|
||||
// }
|
||||
// @Post()
|
||||
// async createCustomer(
|
||||
// @Body() createCustomerDto: CreateCustomerDto,
|
||||
// ): Promise<Customer> {
|
||||
// return this.customerService.createOrUpdateCustomer(createCustomerDto);
|
||||
// }
|
||||
//
|
||||
// @Get('all')
|
||||
// findAll(): Promise<Customer[]> {
|
||||
// return this.customerService.getAllCustomers();
|
||||
// }
|
||||
// @Get('id/:id')
|
||||
// async findOneCustomerById(@Param('id') id: string): Promise<Customer> {
|
||||
// return this.customerService.getCustomerById(id);
|
||||
// }
|
||||
// @Get('name/:name')
|
||||
// async findOneCustomerByName(@Param('name') name: string): Promise<Customer> {
|
||||
// return this.customerService.getCustomerByName(name);
|
||||
// }
|
||||
// @Get('website/:id')
|
||||
// async findOneWebsiteById(@Param('id') id: string): Promise<Website> {
|
||||
// return this.websiteService.getWebsiteById(id);
|
||||
// }
|
||||
// @Get('website/:id/pagespeed')
|
||||
// async getPageSpeedByWebsiteId(
|
||||
// @Param('id') id: string,
|
||||
// ): Promise<PageSpeedData[]> {
|
||||
// return this.pageSpeedService.getPageSpeedsByWebsiteId(id);
|
||||
// }
|
||||
// @Get('display/:displayName')
|
||||
// async findOneWebsiteByDisplayName(
|
||||
// @Param('displayName') displayName: string,
|
||||
// ): Promise<Website> {
|
||||
// return this.websiteService.getWebsiteByDisplayName(displayName);
|
||||
// }
|
||||
}
|
||||
23
src/service/customer/customer.module.ts
Normal file
23
src/service/customer/customer.module.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { PagespeedService } from '../pagespeed/pagespeed.service';
|
||||
import { WebsiteService } from '../website/website.service';
|
||||
import { PageSpeedData } from '../pagespeed/entities/pagespeeddata.entity';
|
||||
import { Website } from '../website/entities/website.entity';
|
||||
import { CustomerController } from './customer.controller';
|
||||
import { CustomerService } from './customer.service';
|
||||
import { Customer } from './entities/customer.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule,
|
||||
HttpModule,
|
||||
TypeOrmModule.forFeature([Customer, Website, PageSpeedData]),
|
||||
],
|
||||
controllers: [CustomerController],
|
||||
providers: [CustomerService, WebsiteService, PagespeedService],
|
||||
exports: [CustomerService, WebsiteService, PagespeedService, TypeOrmModule],
|
||||
})
|
||||
export class CustomerModule {}
|
||||
18
src/service/customer/customer.service.spec.ts
Normal file
18
src/service/customer/customer.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CustomerService } from './customer.service';
|
||||
|
||||
describe('CustomerService', () => {
|
||||
let service: CustomerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [CustomerService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CustomerService>(CustomerService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
39
src/service/customer/customer.service.ts
Normal file
39
src/service/customer/customer.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateCustomerDto } from './dto/create-customer.dto';
|
||||
import { Customer } from './entities/customer.entity';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerService {
|
||||
constructor(
|
||||
@InjectRepository(Customer)
|
||||
private customerRepository: Repository<Customer>,
|
||||
) {}
|
||||
|
||||
async createOrUpdateCustomer(
|
||||
data: Partial<CreateCustomerDto>,
|
||||
): Promise<Customer> {
|
||||
let customer = await this.customerRepository.findOne({
|
||||
where: { email: data.email },
|
||||
});
|
||||
if (!customer) {
|
||||
customer = new Customer();
|
||||
customer.firstName = data.firstName;
|
||||
customer.lastName = data.lastName;
|
||||
customer.email = data.email;
|
||||
customer.dsgvo = data.dsgvo;
|
||||
await this.customerRepository.save(customer);
|
||||
}
|
||||
return customer;
|
||||
}
|
||||
async getAllCustomers(): Promise<Customer[]> {
|
||||
return this.customerRepository.find();
|
||||
}
|
||||
async getCustomerById(id: string): Promise<Customer> {
|
||||
return this.customerRepository.findOne({ where: { id: id } });
|
||||
}
|
||||
async getCustomerByName(name: string): Promise<Customer> {
|
||||
return this.customerRepository.findOne({ where: { firstName: name } });
|
||||
}
|
||||
}
|
||||
32
src/service/customer/dto/create-customer.dto.ts
Normal file
32
src/service/customer/dto/create-customer.dto.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsEmail, IsString } from 'class-validator';
|
||||
import { CreateWebsiteDto } from '../../website/dto/create-website.dto';
|
||||
|
||||
export class CreateCustomerDto {
|
||||
@ApiProperty({ description: 'Customer Id' })
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty({ description: 'First name' })
|
||||
@IsString()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty({ description: 'Last name ' })
|
||||
@IsString()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty({ description: 'E-Mail adress' })
|
||||
@IsString()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: () => CreateWebsiteDto,
|
||||
description: 'Website Association',
|
||||
})
|
||||
website: CreateWebsiteDto[];
|
||||
|
||||
@ApiProperty({ description: 'DSGVO confirmation' })
|
||||
@IsBoolean()
|
||||
dsgvo: boolean;
|
||||
}
|
||||
24
src/service/customer/dto/load-customer.dto.ts
Normal file
24
src/service/customer/dto/load-customer.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { LoadWebsiteDto } from '../../website/dto/load-website.dto';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class LoadCustomerDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ type: () => LoadWebsiteDto })
|
||||
website: LoadCustomerDto;
|
||||
}
|
||||
25
src/service/customer/dto/update-customer.dto.ts
Normal file
25
src/service/customer/dto/update-customer.dto.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsEmail } from 'class-validator';
|
||||
import { UpdateWebsiteDto } from '../../website/dto/update-website.dto';
|
||||
|
||||
export class UpdateCustomerDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ type: () => UpdateWebsiteDto })
|
||||
website: UpdateWebsiteDto[];
|
||||
}
|
||||
36
src/service/customer/entities/customer.entity.ts
Normal file
36
src/service/customer/entities/customer.entity.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEmail } from 'class-validator';
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Website } from '../../website/entities/website.entity';
|
||||
|
||||
@Entity()
|
||||
export class Customer {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ nullable: false })
|
||||
@ApiProperty()
|
||||
firstName: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
@ApiProperty()
|
||||
lastName: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty()
|
||||
@OneToMany(() => Website, (website) => website.customers)
|
||||
websites: Website[];
|
||||
|
||||
@ApiProperty()
|
||||
@Column({ nullable: false })
|
||||
dsgvo: boolean;
|
||||
}
|
||||
209
src/service/pagespeed/dto/create-pagespeed.dto.ts
Normal file
209
src/service/pagespeed/dto/create-pagespeed.dto.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { CreateWebsiteDto } from '../../website/dto/create-website.dto';
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreatePageSpeedDto {
|
||||
@ApiProperty({ description: 'Pagespeed Id' })
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: () => CreateWebsiteDto,
|
||||
description: 'Website Foring Key',
|
||||
})
|
||||
website: CreateWebsiteDto;
|
||||
|
||||
@Exclude()
|
||||
@ApiProperty({ description: 'Hole page-speed result ' })
|
||||
lighthouseObject: any;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstContentfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstContentfulPaintScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstContentfulPaintNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstMeaningfulPaintScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstMeaningfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstMeaningfulPaintNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstMeaningfulPaintNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
mainThreadWorkBreakdownDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
mainThreadWorkBreakdownNumricValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
mainThreadWorkBreakdownNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
mainThreadWorkBreakdownItemsGroupLabel: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
mainThreadWorkBreakdownItemsDuration: number[];
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
speedIndexScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
speedIndexDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
speedIndexNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
speedIndexNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
largestContentfulPaintScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
largestContentfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
largestContentfulPaintNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
largestContentfulPaintNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
unusedCssRulesItems: string[];
|
||||
|
||||
@ApiProperty()
|
||||
@IsOptional()
|
||||
thirdPartySummaryDisplayValue: string | null;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
thirdPartySummaryItemsUrl: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
thirdPartySummaryItemsTransfer: number[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
thirdPartySummaryItemsMainThred: number[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
thirdPartySummaryItemsBlockingTime: number[];
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsOptional()
|
||||
totalByteWeightScore: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalByteWeightDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
totalByteWeightNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalByteWeightNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
totalByteWeightItemsUrl: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
totalByteWeightItemsTotalBytes: number[];
|
||||
|
||||
@ApiProperty()
|
||||
domSizeScore: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
domSizeDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
domSizeNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
domSizeNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
unusedJavaScript: string[];
|
||||
}
|
||||
152
src/service/pagespeed/dto/load-pagespeed.dto.ts
Normal file
152
src/service/pagespeed/dto/load-pagespeed.dto.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { LoadWebsiteDto } from '../../website/dto/load-website.dto';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class LoadPageSpeedDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
@ApiProperty({ type: () => LoadWebsiteDto })
|
||||
website: LoadWebsiteDto;
|
||||
|
||||
@Exclude()
|
||||
@ApiProperty({ type: () => [String] })
|
||||
lighthouseObjet: string[];
|
||||
|
||||
@ApiProperty()
|
||||
firstContentfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
firstContentfulPaintScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
firstContentfulPaintNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
mainThreadWorkBreakdownDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
mainThreadWorkBreakdownNumricValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
mainThreadWorkBreakdownNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
mainThreadWorkBreakdownItemsGroupLabel: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
mainThreadWorkBreakdownItemsDuration: number[];
|
||||
|
||||
@ApiProperty()
|
||||
speedIndexScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
speedIndexDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
speedIndexNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
speedIndexNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
largestContentfulPaintScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
largestContentfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
largestContentfulPaintNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
largestContentfulPaintNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalBlockingTimeScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalBlockingTimeDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalBlockingTimeNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalBlockingTimeNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
unusedCssRulesItems: string[];
|
||||
|
||||
@ApiProperty()
|
||||
thirdPartySummaryDisplayValue: string | null;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
thirdPartySummaryItemsUrl: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
thirdPartySummaryItemsTransfer: number[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
thirdPartySummaryItemsMainThred: number[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
thirdPartySummaryItemsBlockingTime: number[];
|
||||
|
||||
@ApiProperty()
|
||||
timeToInteractiveScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
timeToInteractiveDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
timeToInteractiveNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
timeToInteractiveNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalByteWeightScore: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
totalByteWeightDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalByteWeightNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalByteWeightNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
totalByteWeightItemsUrl: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
totalByteWeightItemsTotalBytes: number[];
|
||||
|
||||
@ApiProperty()
|
||||
domSizeScore: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
domSizeDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
domSizeNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
domSizeNumericUnit: string;
|
||||
}
|
||||
190
src/service/pagespeed/dto/update-pagespeed.dto.ts
Normal file
190
src/service/pagespeed/dto/update-pagespeed.dto.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { UpdateWebsiteDto } from '../../website/dto/update-website.dto';
|
||||
import { IsNumber, IsString } from 'class-validator';
|
||||
|
||||
export class UpdatePageSpeedDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty({ type: () => UpdateWebsiteDto })
|
||||
website: UpdateWebsiteDto;
|
||||
|
||||
@Exclude()
|
||||
@ApiProperty()
|
||||
lighthouseObject: any;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstContentfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstContentfulPaintScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstContentfulPaintNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstMeaningfulPaintScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstMeaningfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
firstMeaningfulPaintNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstMeaningfulPaintNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
mainThreadWorkBreakdownDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
mainThreadWorkBreakdownNumricValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
mainThreadWorkBreakdownNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
mainThreadWorkBreakdownItemsGroupLabel: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
mainThreadWorkBreakdownItemsDuration: number[];
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
speedIndexScore: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
speedIndexDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
speedIndexNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
speedIndexNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
largestContentfulPaintScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
largestContentfulPaintDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
largestContentfulPaintNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
largestContentfulPaintNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalBlockingTimeNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
unusedCssRulesItems: string[];
|
||||
|
||||
@ApiProperty()
|
||||
thirdPartySummaryDisplayValue: string | null;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
thirdPartySummaryItemsUrl: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
thirdPartySummaryItemsTransfer: number[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
thirdPartySummaryItemsMainThred: number[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
thirdPartySummaryItemsBlockingTime: number[];
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveScore: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
timeToInteractiveNumericUnit: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalByteWeightScore: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalByteWeightDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
totalByteWeightNumericValue: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
totalByteWeightNumericUnit: string;
|
||||
|
||||
@ApiProperty({ type: () => [String] })
|
||||
@IsString()
|
||||
totalByteWeightItemsUrl: string[];
|
||||
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
@IsNumber()
|
||||
totalByteWeightItemsTotalBytes: number[];
|
||||
|
||||
@ApiProperty()
|
||||
domSizeScore: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
domSizeDisplayValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
domSizeNumericValue: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
domSizeNumericUnit: string;
|
||||
}
|
||||
218
src/service/pagespeed/entities/pagespeeddata.entity.ts
Normal file
218
src/service/pagespeed/entities/pagespeeddata.entity.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { Website } from '../../website/entities/website.entity';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
PrimaryGeneratedColumn,
|
||||
ManyToOne,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class PageSpeedData {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
@ManyToOne(() => Website, (website) => website.pageSpeedDatas)
|
||||
@JoinColumn({ name: 'websiteId' })
|
||||
@ApiProperty()
|
||||
website: Website;
|
||||
|
||||
@Exclude()
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [String] })
|
||||
lighthouseObject: string[];
|
||||
|
||||
//#region firstContentfulPaint
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstContentfulPaintDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstContentfulPaintScore: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstContentfulPaintNumericValue: number;
|
||||
//#endregion
|
||||
|
||||
//#region firstMeaningfulPaint
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintScore: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintNumericValue: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
firstMeaningfulPaintNumericUnit: string;
|
||||
//#endregion
|
||||
|
||||
//#region mainThreadWorkBreakdown
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
mainThreadWorkBreakdownDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
mainThreadWorkBreakdownNumricValue: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
mainThreadWorkBreakdownNumericUnit: string;
|
||||
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [String] })
|
||||
mainThreadWorkBreakdownItemsGroupLabel: string[];
|
||||
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
mainThreadWorkBreakdownItemsDuration: number[];
|
||||
//#endregion
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
speedIndexScore: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
speedIndexDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
speedIndexNumericValue: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
speedIndexNumericUnit: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
largestContentfulPaintScore: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
largestContentfulPaintDisplayValue: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
largestContentfulPaintNumericValue: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
largestContentfulPaintNumericUnit: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalBlockingTimeScore: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalBlockingTimeDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalBlockingTimeNumericValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalBlockingTimeNumericUnit: string;
|
||||
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [String], nullable: true })
|
||||
unusedCssRulesItems: string[];
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
thirdPartySummaryDisplayValue: string | null;
|
||||
|
||||
@Column('json', { nullable: true })
|
||||
@ApiProperty({ type: () => [String] })
|
||||
thirdPartySummaryItemsUrl: string[];
|
||||
|
||||
@Column('json', { nullable: true })
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
thirdPartySummaryItemsTransfer: number[];
|
||||
|
||||
@Column('json', { nullable: true })
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
thirdPartySummaryItemsMainThred: number[];
|
||||
|
||||
@Column('json', { nullable: true })
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
thirdPartySummaryItemsBlockingTime: number[];
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
timeToInteractiveScore: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
timeToInteractiveDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
timeToInteractiveNumericValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
timeToInteractiveNumericUnit: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
totalByteWeightScore: number | null;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalByteWeightDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalByteWeightNumericValue: number;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
totalByteWeightNumericUnit: string;
|
||||
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [String] })
|
||||
totalByteWeightItemsUrl: string[];
|
||||
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [Number] })
|
||||
totalByteWeightItemsTotalBytes: number[];
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
domSizeScore: number | null;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
domSizeDisplayValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
domSizeNumericValue: string;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
domSizeNumericUnit: string;
|
||||
|
||||
@Column('json')
|
||||
@ApiProperty({ type: () => [String], nullable: true })
|
||||
unusedJavaScript: string[];
|
||||
}
|
||||
20
src/service/pagespeed/pagespeed.controller.spec.ts
Normal file
20
src/service/pagespeed/pagespeed.controller.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PagespeedController } from './pagespeed.controller';
|
||||
import { PagespeedService } from './pagespeed.service';
|
||||
|
||||
describe('PagespeedController', () => {
|
||||
let controller: PagespeedController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [PagespeedController],
|
||||
providers: [PagespeedService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<PagespeedController>(PagespeedController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
4
src/service/pagespeed/pagespeed.controller.ts
Normal file
4
src/service/pagespeed/pagespeed.controller.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
|
||||
@Controller('pagespeed')
|
||||
export class PagespeedController {}
|
||||
21
src/service/pagespeed/pagespeed.module.ts
Normal file
21
src/service/pagespeed/pagespeed.module.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { PageSpeedData } from './entities/pagespeeddata.entity';
|
||||
import { PagespeedController } from './pagespeed.controller';
|
||||
import { PagespeedService } from './pagespeed.service';
|
||||
import { WebsiteModule } from '../website/website.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
HttpModule,
|
||||
ConfigModule,
|
||||
WebsiteModule,
|
||||
TypeOrmModule.forFeature([PageSpeedData]),
|
||||
],
|
||||
controllers: [PagespeedController],
|
||||
providers: [PagespeedService],
|
||||
exports: [PagespeedService],
|
||||
})
|
||||
export class PagespeedModule {}
|
||||
18
src/service/pagespeed/pagespeed.service.spec.ts
Normal file
18
src/service/pagespeed/pagespeed.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PagespeedService } from './pagespeed.service';
|
||||
|
||||
describe('PagespeedService', () => {
|
||||
let service: PagespeedService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [PagespeedService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PagespeedService>(PagespeedService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
78
src/service/pagespeed/pagespeed.service.ts
Normal file
78
src/service/pagespeed/pagespeed.service.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { HttpException, Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { WebsiteService } from '../website/website.service';
|
||||
import { PageSpeedData } from './entities/pagespeeddata.entity';
|
||||
import {
|
||||
convertDTOToEntity,
|
||||
createPageSpeedDTOFromApiResponse,
|
||||
} from './pagespeed.utils';
|
||||
|
||||
@Injectable()
|
||||
export class PagespeedService {
|
||||
constructor(
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly websiteService: WebsiteService,
|
||||
@InjectRepository(PageSpeedData)
|
||||
private readonly pageSpeedRepository: Repository<PageSpeedData>,
|
||||
) {}
|
||||
|
||||
API_KEY = this.configService.get<string>('API_KEY');
|
||||
//#region Request with URL and API Key to Google Lighthouse to get PageSpeedResults
|
||||
// It's essential to note that the Google API only accepts URLs with a protocol (such as "http://" or "https://").
|
||||
// If a domain is registered only with a subdomain, you can only test it with the subdomain included.
|
||||
// Without a subdomain, it will result in a 500 Error.
|
||||
async pageSpeedRequest(url: string): Promise<any> {
|
||||
if (!url.startsWith('https://') && !url.startsWith('http://')) {
|
||||
url = 'https://' + url;
|
||||
}
|
||||
const expression = new RegExp(
|
||||
/(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?\/[a-zA-Z0-9]{2,}|((https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?)|(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})?/g,
|
||||
);
|
||||
if (!url || url === '' || url.match(expression) === null) {
|
||||
throw new HttpException(
|
||||
"Bad Request: The URL must begin with 'http://' or 'https://' and have a valid domain format.",
|
||||
400,
|
||||
);
|
||||
}
|
||||
const encodedUrl = encodeURI(url);
|
||||
const apiUrl = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodedUrl}&key=${this.API_KEY}`;
|
||||
const response$ = this.httpService
|
||||
.get(apiUrl)
|
||||
.pipe(map((response) => response.data));
|
||||
const data = await firstValueFrom(response$);
|
||||
return data;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
async getPageSpeedResult(
|
||||
url: string,
|
||||
websiteId: string,
|
||||
): Promise<PageSpeedData> {
|
||||
const data = await this.pageSpeedRequest(url);
|
||||
const website = await this.websiteService.getWebsiteById(websiteId);
|
||||
if (!website) {
|
||||
throw new Error(`Website with WebsiteID ${websiteId} not found`);
|
||||
}
|
||||
// Create DTO with API results, with request "data"
|
||||
const pageSpeedDTO = createPageSpeedDTOFromApiResponse(data);
|
||||
// Convert DTO to Entity it is a function in utils that gets 2 objets to convertDTOToEntity in entitys.
|
||||
const entity = convertDTOToEntity(pageSpeedDTO, website);
|
||||
// Save Entity into Database the entity that crated in the convertfunction will saved in the entity
|
||||
await this.pageSpeedRepository.save(entity);
|
||||
return entity;
|
||||
}
|
||||
async getPageSpeedsByWebsiteId(websiteId: string): Promise<PageSpeedData[]> {
|
||||
return await this.pageSpeedRepository.find({
|
||||
where: { website: { id: websiteId } },
|
||||
});
|
||||
}
|
||||
async getAllPageSpeeds(webId: string): Promise<PageSpeedData[]> {
|
||||
return await this.pageSpeedRepository.find({ where: { id: webId } });
|
||||
}
|
||||
}
|
||||
319
src/service/pagespeed/pagespeed.utils.ts
Normal file
319
src/service/pagespeed/pagespeed.utils.ts
Normal file
@@ -0,0 +1,319 @@
|
||||
import { Website } from '../website/entities/website.entity';
|
||||
import { CreatePageSpeedDto } from './dto/create-pagespeed.dto';
|
||||
import { PageSpeedData } from './entities/pagespeeddata.entity';
|
||||
|
||||
export function createPageSpeedDTOFromApiResponse(
|
||||
data: any,
|
||||
): CreatePageSpeedDto {
|
||||
const pageSpeedDTO = new CreatePageSpeedDto();
|
||||
|
||||
// Datas from pagespeed request
|
||||
const mainLighthouseObjet = data.lighthouseResult;
|
||||
const firstContentfulPaintData =
|
||||
data.lighthouseResult.audits['first-contentful-paint'];
|
||||
const firstMeaningfulPaintData =
|
||||
data.lighthouseResult.audits['first-meaningful-paint'];
|
||||
const mainThreadWorkBreakdownData =
|
||||
data.lighthouseResult.audits['mainthread-work-breakdown'];
|
||||
const unusedCssRulesData = data.lighthouseResult.audits['unused-css-rules'];
|
||||
const speedIndexData = data.lighthouseResult.audits['speed-index'];
|
||||
const thirdPartySummaryData =
|
||||
data.lighthouseResult.audits['third-party-summary'];
|
||||
const totalByteWeightData = data.lighthouseResult.audits['total-byte-weight'];
|
||||
const totalBlockingTimeData =
|
||||
data.lighthouseResult.audits['total-blocking-time'];
|
||||
const timeToInteractiveData = data.lighthouseResult.audits['interactive'];
|
||||
const domSizeData = data.lighthouseResult.audits['dom-size'];
|
||||
const largestContentfulPaintData =
|
||||
data.lighthouseResult.audits['largest-contentful-paint'];
|
||||
const unusedJavaScript = data.lighthouseResult.audits['unused-javascript'];
|
||||
|
||||
// Save howl object
|
||||
pageSpeedDTO.lighthouseObject = mainLighthouseObjet;
|
||||
|
||||
// First Contentful Paint Data
|
||||
pageSpeedDTO.firstContentfulPaintScore = firstContentfulPaintData.score;
|
||||
pageSpeedDTO.firstContentfulPaintNumericValue =
|
||||
firstContentfulPaintData.numericUnit;
|
||||
pageSpeedDTO.firstContentfulPaintNumericValue =
|
||||
firstContentfulPaintData.numericValue;
|
||||
pageSpeedDTO.firstContentfulPaintDisplayValue =
|
||||
firstContentfulPaintData.displayValue;
|
||||
|
||||
// First Meaningful Paint Data
|
||||
pageSpeedDTO.firstMeaningfulPaintScore = firstMeaningfulPaintData.score;
|
||||
pageSpeedDTO.firstMeaningfulPaintNumericValue =
|
||||
firstMeaningfulPaintData.numericValue;
|
||||
pageSpeedDTO.firstMeaningfulPaintNumericUnit =
|
||||
firstMeaningfulPaintData.numericUnit;
|
||||
pageSpeedDTO.firstMeaningfulPaintDisplayValue =
|
||||
firstMeaningfulPaintData.displayValue;
|
||||
|
||||
// Main Thread Work Breakdown Data
|
||||
pageSpeedDTO.mainThreadWorkBreakdownDisplayValue =
|
||||
mainThreadWorkBreakdownData.displayValue;
|
||||
pageSpeedDTO.mainThreadWorkBreakdownNumricValue =
|
||||
mainThreadWorkBreakdownData.numericValue;
|
||||
pageSpeedDTO.mainThreadWorkBreakdownNumericUnit =
|
||||
mainThreadWorkBreakdownData.numericUnit;
|
||||
pageSpeedDTO.mainThreadWorkBreakdownItemsDuration = [];
|
||||
const items = mainThreadWorkBreakdownData.details.items;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
pageSpeedDTO.mainThreadWorkBreakdownItemsDuration.push(item.duration);
|
||||
}
|
||||
pageSpeedDTO.mainThreadWorkBreakdownItemsGroupLabel = [];
|
||||
const group = mainThreadWorkBreakdownData.details.items;
|
||||
for (let i = 0; i < group.length; i++) {
|
||||
const item = group[i];
|
||||
pageSpeedDTO.mainThreadWorkBreakdownItemsGroupLabel.push(item.groupLabel);
|
||||
}
|
||||
// Largest Contentful Paint Data
|
||||
pageSpeedDTO.largestContentfulPaintScore = largestContentfulPaintData.score;
|
||||
pageSpeedDTO.largestContentfulPaintDisplayValue =
|
||||
largestContentfulPaintData.displayValue;
|
||||
pageSpeedDTO.largestContentfulPaintNumericValue =
|
||||
largestContentfulPaintData.numericValue;
|
||||
pageSpeedDTO.largestContentfulPaintNumericUnit =
|
||||
largestContentfulPaintData.numericUnit;
|
||||
|
||||
// Unused CSS Rules Data
|
||||
if (unusedCssRulesData && unusedCssRulesData.details) {
|
||||
pageSpeedDTO.unusedCssRulesItems = [];
|
||||
const cssdata = unusedCssRulesData.details.items;
|
||||
for (let i = 0; i < cssdata.length; i++) {
|
||||
const item = cssdata[i];
|
||||
pageSpeedDTO.unusedCssRulesItems.push(item);
|
||||
}
|
||||
}
|
||||
// Unused JavaScript
|
||||
if (unusedJavaScript && unusedJavaScript.details) {
|
||||
pageSpeedDTO.unusedJavaScript = [];
|
||||
const javadata = unusedJavaScript.details.items;
|
||||
for (let i = 0; i < javadata.length; i++) {
|
||||
const item = javadata[i];
|
||||
pageSpeedDTO.unusedJavaScript.push(item);
|
||||
}
|
||||
}
|
||||
// Speed Index Data
|
||||
pageSpeedDTO.speedIndexScore = speedIndexData.score;
|
||||
pageSpeedDTO.speedIndexDisplayValue = speedIndexData.displayValue;
|
||||
pageSpeedDTO.speedIndexNumericValue = speedIndexData.numericValue;
|
||||
pageSpeedDTO.speedIndexNumericUnit = speedIndexData.numericUnit;
|
||||
|
||||
// Third Party Summary Data
|
||||
pageSpeedDTO.thirdPartySummaryDisplayValue =
|
||||
thirdPartySummaryData.displayValue;
|
||||
// Third Party Summary Url
|
||||
if (
|
||||
thirdPartySummaryData &&
|
||||
thirdPartySummaryData.detail &&
|
||||
thirdPartySummaryData.detail.items
|
||||
) {
|
||||
pageSpeedDTO.thirdPartySummaryItemsUrl = [];
|
||||
for (let i = 0; i < thirdPartySummaryData.details.items.length; i++) {
|
||||
const item = thirdPartySummaryData.details.items[i];
|
||||
for (let j = 0; j < item.subItems.items.length; j++) {
|
||||
const subItem = item.subItems.items[j];
|
||||
pageSpeedDTO.thirdPartySummaryItemsUrl.push(subItem.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Third Party Summary Transfer Size
|
||||
if (
|
||||
thirdPartySummaryData &&
|
||||
thirdPartySummaryData.details &&
|
||||
thirdPartySummaryData.details.items
|
||||
) {
|
||||
pageSpeedDTO.thirdPartySummaryItemsTransfer = [];
|
||||
for (let i = 0; i < thirdPartySummaryData.details.items.length; i++) {
|
||||
const item = thirdPartySummaryData.details.items[i];
|
||||
for (let j = 0; j < item.subItems.items.length; j++) {
|
||||
const subItem = item.subItems.items[j];
|
||||
pageSpeedDTO.thirdPartySummaryItemsTransfer.push(subItem.transferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Third Party Summary Main Thread Time
|
||||
if (
|
||||
thirdPartySummaryData &&
|
||||
thirdPartySummaryData.details &&
|
||||
thirdPartySummaryData.details.items
|
||||
) {
|
||||
pageSpeedDTO.thirdPartySummaryItemsMainThred = [];
|
||||
for (let i = 0; i < thirdPartySummaryData.details.items.length; i++) {
|
||||
const item = thirdPartySummaryData.details.items[i];
|
||||
for (let j = 0; j < item.subItems.items.length; j++) {
|
||||
const subItem = item.subItems.items[j];
|
||||
pageSpeedDTO.thirdPartySummaryItemsMainThred.push(
|
||||
subItem.mainThreadTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Third Party Summary Blocking Time
|
||||
if (
|
||||
thirdPartySummaryData &&
|
||||
thirdPartySummaryData.details &&
|
||||
thirdPartySummaryData.details.items
|
||||
) {
|
||||
pageSpeedDTO.thirdPartySummaryItemsBlockingTime = [];
|
||||
for (let i = 0; i < thirdPartySummaryData.details.items.length; i++) {
|
||||
const item = thirdPartySummaryData.details.items[i];
|
||||
for (let j = 0; j < item.subItems.items.length; j++) {
|
||||
const subItem = item.subItems.items[j];
|
||||
pageSpeedDTO.thirdPartySummaryItemsBlockingTime.push(
|
||||
subItem.blockingTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Total Byte Weight Data
|
||||
pageSpeedDTO.totalByteWeightScore = totalByteWeightData.score;
|
||||
pageSpeedDTO.totalByteWeightDisplayValue = totalByteWeightData.displayValue;
|
||||
pageSpeedDTO.totalByteWeightNumericValue = totalByteWeightData.numericValue;
|
||||
pageSpeedDTO.totalByteWeightNumericUnit = totalByteWeightData.numericUnit;
|
||||
pageSpeedDTO.totalByteWeightItemsUrl = [];
|
||||
const byteUrl = totalByteWeightData.details.items;
|
||||
for (let i = 0; i < byteUrl.length; i++) {
|
||||
const item = byteUrl[i];
|
||||
pageSpeedDTO.totalByteWeightItemsUrl.push(item.url);
|
||||
}
|
||||
pageSpeedDTO.totalByteWeightItemsTotalBytes = [];
|
||||
const totalByte = totalByteWeightData.details.items;
|
||||
for (let i = 0; i < totalByte.length; i++) {
|
||||
const item = totalByte[i];
|
||||
pageSpeedDTO.totalByteWeightItemsUrl.push(item.url);
|
||||
}
|
||||
|
||||
// Total Blocking Time Data
|
||||
pageSpeedDTO.totalBlockingTimeScore = totalBlockingTimeData.score;
|
||||
pageSpeedDTO.totalBlockingTimeDisplayValue =
|
||||
totalBlockingTimeData.displayValue;
|
||||
pageSpeedDTO.totalBlockingTimeNumericValue =
|
||||
totalBlockingTimeData.numericValue;
|
||||
pageSpeedDTO.totalBlockingTimeNumericUnit = totalBlockingTimeData.numericUnit;
|
||||
|
||||
// Time To Interactive Data
|
||||
pageSpeedDTO.timeToInteractiveScore = timeToInteractiveData.score;
|
||||
pageSpeedDTO.timeToInteractiveDisplayValue =
|
||||
timeToInteractiveData.displayValue;
|
||||
pageSpeedDTO.timeToInteractiveNumericValue =
|
||||
timeToInteractiveData.numericValue;
|
||||
pageSpeedDTO.timeToInteractiveNumericUnit = timeToInteractiveData.numericUnit;
|
||||
|
||||
// DOM Size Data
|
||||
pageSpeedDTO.domSizeScore = domSizeData.score;
|
||||
pageSpeedDTO.domSizeDisplayValue = domSizeData.displayValue;
|
||||
pageSpeedDTO.domSizeNumericValue = domSizeData.numericValue;
|
||||
pageSpeedDTO.domSizeNumericUnit = domSizeData.numericUnit;
|
||||
|
||||
return pageSpeedDTO;
|
||||
}
|
||||
|
||||
export function convertDTOToEntity(
|
||||
dto: CreatePageSpeedDto,
|
||||
website: Website,
|
||||
): PageSpeedData {
|
||||
// Create new PageSpeedData entity
|
||||
const entity = new PageSpeedData();
|
||||
|
||||
// Datas from pagespeed request
|
||||
|
||||
if (!website) {
|
||||
throw new Error(`Website with WebsiteID ${website} not found`);
|
||||
}
|
||||
entity.website = website;
|
||||
// Save howl object
|
||||
entity.lighthouseObject = dto.lighthouseObject;
|
||||
|
||||
// First Contentful Paint Data
|
||||
entity.firstContentfulPaintScore = dto.firstContentfulPaintScore;
|
||||
entity.firstContentfulPaintNumericValue =
|
||||
dto.firstContentfulPaintNumericValue;
|
||||
entity.firstContentfulPaintDisplayValue =
|
||||
dto.firstContentfulPaintDisplayValue;
|
||||
|
||||
// First Meaningful Paint Data
|
||||
entity.firstMeaningfulPaintScore = dto.firstMeaningfulPaintScore;
|
||||
entity.firstMeaningfulPaintNumericValue =
|
||||
dto.firstMeaningfulPaintNumericValue;
|
||||
entity.firstMeaningfulPaintNumericUnit = dto.firstMeaningfulPaintNumericUnit;
|
||||
entity.firstMeaningfulPaintDisplayValue =
|
||||
dto.firstMeaningfulPaintDisplayValue;
|
||||
|
||||
// Main Thread Work Breakdown Data
|
||||
entity.mainThreadWorkBreakdownDisplayValue =
|
||||
dto.mainThreadWorkBreakdownDisplayValue;
|
||||
entity.mainThreadWorkBreakdownNumricValue =
|
||||
dto.mainThreadWorkBreakdownNumricValue;
|
||||
entity.mainThreadWorkBreakdownNumericUnit =
|
||||
dto.mainThreadWorkBreakdownNumericUnit;
|
||||
entity.mainThreadWorkBreakdownItemsDuration =
|
||||
dto.mainThreadWorkBreakdownItemsDuration;
|
||||
|
||||
entity.mainThreadWorkBreakdownItemsGroupLabel =
|
||||
dto.mainThreadWorkBreakdownItemsGroupLabel;
|
||||
// Largest Contentful Paint Data
|
||||
entity.largestContentfulPaintScore = dto.largestContentfulPaintScore;
|
||||
entity.largestContentfulPaintDisplayValue =
|
||||
dto.largestContentfulPaintDisplayValue;
|
||||
entity.largestContentfulPaintNumericValue =
|
||||
dto.largestContentfulPaintNumericValue;
|
||||
entity.largestContentfulPaintNumericUnit =
|
||||
dto.largestContentfulPaintNumericUnit;
|
||||
|
||||
// Unused CSS Rules Data
|
||||
entity.unusedCssRulesItems = dto.unusedCssRulesItems;
|
||||
|
||||
// Unused Java Script Data
|
||||
entity.unusedJavaScript = dto.unusedJavaScript;
|
||||
|
||||
// Speed Index Data
|
||||
entity.speedIndexScore = dto.speedIndexScore;
|
||||
entity.speedIndexDisplayValue = dto.speedIndexDisplayValue;
|
||||
entity.speedIndexNumericValue = dto.speedIndexNumericValue;
|
||||
entity.speedIndexNumericUnit = dto.speedIndexNumericUnit;
|
||||
|
||||
// Third Party Summary Data
|
||||
entity.thirdPartySummaryDisplayValue = dto.thirdPartySummaryDisplayValue;
|
||||
// Third Party Summary Url
|
||||
entity.thirdPartySummaryItemsUrl = dto.thirdPartySummaryItemsUrl;
|
||||
|
||||
// Third Party Summary Transfer Size
|
||||
entity.thirdPartySummaryItemsTransfer = dto.thirdPartySummaryItemsTransfer;
|
||||
// Third Party Summary Main Thread Time
|
||||
entity.thirdPartySummaryItemsMainThred = dto.thirdPartySummaryItemsMainThred;
|
||||
// Third Party Summary Blocking Time
|
||||
entity.thirdPartySummaryItemsBlockingTime =
|
||||
dto.thirdPartySummaryItemsBlockingTime;
|
||||
|
||||
// Total Byte Weight Data
|
||||
entity.totalByteWeightScore = dto.totalByteWeightScore;
|
||||
entity.totalByteWeightDisplayValue = dto.totalByteWeightDisplayValue;
|
||||
entity.totalByteWeightNumericValue = dto.totalByteWeightNumericValue;
|
||||
entity.totalByteWeightNumericUnit = dto.totalByteWeightNumericUnit;
|
||||
entity.totalByteWeightItemsUrl = dto.totalByteWeightItemsUrl;
|
||||
entity.totalByteWeightItemsTotalBytes = dto.totalByteWeightItemsTotalBytes;
|
||||
|
||||
// Total Blocking Time Data
|
||||
entity.totalBlockingTimeScore = dto.totalBlockingTimeScore;
|
||||
entity.totalBlockingTimeDisplayValue = dto.totalBlockingTimeDisplayValue;
|
||||
entity.totalBlockingTimeNumericValue = dto.totalBlockingTimeNumericValue;
|
||||
entity.totalBlockingTimeNumericUnit = dto.totalBlockingTimeNumericUnit;
|
||||
|
||||
// Time To Interactive Data
|
||||
entity.timeToInteractiveScore = dto.timeToInteractiveScore;
|
||||
entity.timeToInteractiveDisplayValue = dto.timeToInteractiveDisplayValue;
|
||||
entity.timeToInteractiveNumericValue = dto.timeToInteractiveNumericValue;
|
||||
entity.timeToInteractiveNumericUnit = dto.timeToInteractiveNumericUnit;
|
||||
|
||||
// DOM Size Data
|
||||
entity.domSizeScore = dto.domSizeScore;
|
||||
entity.domSizeDisplayValue = dto.domSizeDisplayValue;
|
||||
entity.domSizeNumericValue = dto.domSizeNumericValue;
|
||||
entity.domSizeNumericUnit = dto.domSizeNumericUnit;
|
||||
|
||||
return entity;
|
||||
}
|
||||
29
src/service/website/dto/create-website.dto.ts
Normal file
29
src/service/website/dto/create-website.dto.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { CreateCustomerDto } from '../../customer/dto/create-customer.dto';
|
||||
import { CreatePageSpeedDto } from '../../pagespeed/dto/create-pagespeed.dto';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class CreateWebsiteDto {
|
||||
@ApiProperty({ description: 'Website Id' })
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty({ description: 'Company Name' })
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
@ApiProperty({ type: () => CreateCustomerDto, description: 'Forign Key' })
|
||||
@IsNotEmpty()
|
||||
customer: { id: string };
|
||||
|
||||
@ApiProperty({ description: 'Website Neme' })
|
||||
@IsString()
|
||||
url: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: () => CreatePageSpeedDto,
|
||||
description: 'PageSpeed Association',
|
||||
})
|
||||
@IsNotEmpty()
|
||||
pageSpeedDatas: CreatePageSpeedDto[];
|
||||
}
|
||||
24
src/service/website/dto/load-website.dto.ts
Normal file
24
src/service/website/dto/load-website.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
import { LoadCustomerDto } from '../../customer/dto/load-customer.dto';
|
||||
import { LoadPageSpeedDto } from '../../pagespeed/dto/load-pagespeed.dto';
|
||||
|
||||
export class LoadWebsiteDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
websiteId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
@ApiProperty({ type: () => LoadCustomerDto })
|
||||
customer: LoadCustomerDto;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
url: string;
|
||||
|
||||
@ApiProperty({ type: () => LoadPageSpeedDto })
|
||||
pageSpeedDatas: LoadPageSpeedDto[];
|
||||
}
|
||||
24
src/service/website/dto/update-website.dto.ts
Normal file
24
src/service/website/dto/update-website.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
import { UpdateCustomerDto } from '../../customer/dto/update-customer.dto';
|
||||
import { UpdatePageSpeedDto } from '../../pagespeed/dto/update-pagespeed.dto';
|
||||
|
||||
export class UpdateWebsiteDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
@ApiProperty({ type: () => UpdateCustomerDto })
|
||||
customer: UpdateCustomerDto;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
url: string;
|
||||
|
||||
@ApiProperty({ type: () => UpdatePageSpeedDto })
|
||||
pageSpeedDatas: UpdatePageSpeedDto[];
|
||||
}
|
||||
39
src/service/website/entities/website.entity.ts
Normal file
39
src/service/website/entities/website.entity.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
Entity,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Column } from 'typeorm';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PageSpeedData } from '../../pagespeed/entities/pagespeeddata.entity';
|
||||
import { Customer } from '../../customer/entities/customer.entity';
|
||||
|
||||
@Entity()
|
||||
export class Website {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
@Column()
|
||||
@ApiProperty()
|
||||
displayName: string;
|
||||
|
||||
@ManyToOne(() => Customer, (customer) => customer.websites)
|
||||
@JoinColumn({ name: 'customerId' })
|
||||
@ApiProperty()
|
||||
customers: Customer;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@ApiProperty()
|
||||
url: string;
|
||||
|
||||
@OneToMany(() => PageSpeedData, (pageSpeedData) => pageSpeedData.website)
|
||||
@ApiProperty()
|
||||
pageSpeedDatas: PageSpeedData[];
|
||||
}
|
||||
20
src/service/website/website.controller.spec.ts
Normal file
20
src/service/website/website.controller.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { WebsiteController } from './website.controller';
|
||||
import { WebsiteService } from './website.service';
|
||||
|
||||
describe('WebsiteController', () => {
|
||||
let controller: WebsiteController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [WebsiteController],
|
||||
providers: [WebsiteService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<WebsiteController>(WebsiteController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
4
src/service/website/website.controller.ts
Normal file
4
src/service/website/website.controller.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
|
||||
@Controller('website')
|
||||
export class WebsiteController {}
|
||||
25
src/service/website/website.module.ts
Normal file
25
src/service/website/website.module.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module, forwardRef } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Customer } from '../customer/entities/customer.entity';
|
||||
import { PageSpeedData } from '../pagespeed/entities/pagespeeddata.entity';
|
||||
import { Website } from './entities/website.entity';
|
||||
import { WebsiteController } from './website.controller';
|
||||
import { WebsiteService } from './website.service';
|
||||
import { CustomerModule } from '../customer/customer.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { PagespeedModule } from '../pagespeed/pagespeed.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([Customer, Website, PageSpeedData]),
|
||||
forwardRef(() => PagespeedModule),
|
||||
CustomerModule,
|
||||
HttpModule,
|
||||
ConfigModule,
|
||||
],
|
||||
controllers: [WebsiteController],
|
||||
providers: [WebsiteService],
|
||||
exports: [WebsiteService, TypeOrmModule],
|
||||
})
|
||||
export class WebsiteModule {}
|
||||
18
src/service/website/website.service.spec.ts
Normal file
18
src/service/website/website.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { WebsiteService } from './website.service';
|
||||
|
||||
describe('WebsiteService', () => {
|
||||
let service: WebsiteService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [WebsiteService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<WebsiteService>(WebsiteService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
69
src/service/website/website.service.ts
Normal file
69
src/service/website/website.service.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Customer } from '../customer/entities/customer.entity';
|
||||
import { CreateWebsiteDto } from './dto/create-website.dto';
|
||||
import { Website } from './entities/website.entity';
|
||||
|
||||
@Injectable()
|
||||
export class WebsiteService {
|
||||
constructor(
|
||||
@InjectRepository(Customer)
|
||||
private customerRepository: Repository<Customer>,
|
||||
@InjectRepository(Website)
|
||||
private websiteRepository: Repository<Website>,
|
||||
) {}
|
||||
|
||||
async createOrUpdateWebsite(
|
||||
data: Partial<CreateWebsiteDto>,
|
||||
websiteId?: Website['id'],
|
||||
): Promise<Website> {
|
||||
if (!data.customer.id) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
const customer = await this.customerRepository.findOne({
|
||||
where: { id: data.customer.id },
|
||||
});
|
||||
|
||||
if (!customer) {
|
||||
throw new BadRequestException(
|
||||
`Customer with id ${data.customer.id} not found`,
|
||||
);
|
||||
}
|
||||
await this.websiteRepository.upsert(
|
||||
{
|
||||
id: websiteId,
|
||||
url: data.url,
|
||||
displayName: data.displayName,
|
||||
customers: customer,
|
||||
pageSpeedDatas: data.pageSpeedDatas,
|
||||
},
|
||||
['id'],
|
||||
);
|
||||
|
||||
return this.websiteRepository.findOne({ where: { id: websiteId } });
|
||||
}
|
||||
async getAllWebsites(): Promise<Website[]> {
|
||||
return this.websiteRepository.find();
|
||||
}
|
||||
async getWebsiteById(id: Website['id']): Promise<Website> {
|
||||
return this.websiteRepository.findOne({ where: { id: id } });
|
||||
}
|
||||
async getAllWebsitesByCustomerId(id: string): Promise<Website[]> {
|
||||
return this.websiteRepository
|
||||
.createQueryBuilder('website')
|
||||
.leftJoinAndSelect('website.customer', 'customer')
|
||||
.where('customer.id = :id', { id })
|
||||
.getMany();
|
||||
}
|
||||
async getWebsiteByDisplayName(displayName: string): Promise<Website> {
|
||||
return this.websiteRepository.findOne({
|
||||
where: { displayName: displayName },
|
||||
});
|
||||
}
|
||||
}
|
||||
24
test/app.e2e-spec.ts
Normal file
24
test/app.e2e-spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
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
test/jest-e2e.json
Normal file
9
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
tsconfig.build.json
Normal file
4
tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user