Browse Source

Add all server checks to CI - fix lint issues

CI will now run linter, type-checks and tests for the server.

All the lint issues have been fixed.
Jaime Baez 2 years ago
parent
commit
1f4ba73da7

+ 3 - 3
.github/workflows/test.yml

@@ -18,8 +18,8 @@ jobs:
       - name: Run Immich Server 2E2 Test
       - name: Run Immich Server 2E2 Test
         run: docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test up --abort-on-container-exit --exit-code-from immich-server-test
         run: docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test up --abort-on-container-exit --exit-code-from immich-server-test
 
 
-  unit-tests:
-    name: Run unit test suites
+  server-unit-tests:
+    name: Run server unit test suites and checks
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
 
 
     steps:
     steps:
@@ -27,4 +27,4 @@ jobs:
         uses: actions/checkout@v3
         uses: actions/checkout@v3
 
 
       - name: Run tests
       - name: Run tests
-        run: cd server && npm install && npm run test
+        run: cd server && npm ci && npm run check:all

+ 2 - 3
server/apps/immich/src/api-v1/album/album-repository.ts

@@ -11,7 +11,6 @@ import { GetAlbumsDto } from './dto/get-albums.dto';
 import { RemoveAssetsDto } from './dto/remove-assets.dto';
 import { RemoveAssetsDto } from './dto/remove-assets.dto';
 import { UpdateAlbumDto } from './dto/update-album.dto';
 import { UpdateAlbumDto } from './dto/update-album.dto';
 import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
 import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
-import { AlbumResponseDto } from './response-dto/album-response.dto';
 
 
 export interface IAlbumRepository {
 export interface IAlbumRepository {
   create(ownerId: string, createAlbumDto: CreateAlbumDto): Promise<AlbumEntity>;
   create(ownerId: string, createAlbumDto: CreateAlbumDto): Promise<AlbumEntity>;
@@ -165,7 +164,7 @@ export class AlbumRepository implements IAlbumRepository {
   }
   }
 
 
   async getListByAssetId(userId: string, assetId: string): Promise<AlbumEntity[]> {
   async getListByAssetId(userId: string, assetId: string): Promise<AlbumEntity[]> {
-    let query = this.albumRepository.createQueryBuilder('album');
+    const query = this.albumRepository.createQueryBuilder('album');
 
 
     const albums = await query
     const albums = await query
       .where('album.ownerId = :ownerId', { ownerId: userId })
       .where('album.ownerId = :ownerId', { ownerId: userId })
@@ -190,7 +189,7 @@ export class AlbumRepository implements IAlbumRepository {
   }
   }
 
 
   async get(albumId: string): Promise<AlbumEntity | undefined> {
   async get(albumId: string): Promise<AlbumEntity | undefined> {
-    let query = this.albumRepository.createQueryBuilder('album');
+    const query = this.albumRepository.createQueryBuilder('album');
 
 
     const album = await query
     const album = await query
       .where('album.id = :albumId', { albumId })
       .where('album.id = :albumId', { albumId })

+ 0 - 1
server/apps/immich/src/api-v1/album/album.controller.ts

@@ -11,7 +11,6 @@ import {
   ParseUUIDPipe,
   ParseUUIDPipe,
   Put,
   Put,
   Query,
   Query,
-  Header,
 } from '@nestjs/common';
 } from '@nestjs/common';
 import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe';
 import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe';
 import { AlbumService } from './album.service';
 import { AlbumService } from './album.service';

+ 0 - 1
server/apps/immich/src/api-v1/album/album.service.spec.ts

@@ -4,7 +4,6 @@ import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common';
 import { BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common';
 import { AlbumEntity } from '@app/database/entities/album.entity';
 import { AlbumEntity } from '@app/database/entities/album.entity';
 import { AlbumResponseDto } from './response-dto/album-response.dto';
 import { AlbumResponseDto } from './response-dto/album-response.dto';
-import { AssetAlbumEntity } from '@app/database/entities/asset-album.entity';
 
 
 describe('Album service', () => {
 describe('Album service', () => {
   let sut: AlbumService;
   let sut: AlbumService;

+ 1 - 1
server/apps/immich/src/api-v1/album/dto/update-album.dto.ts

@@ -1,4 +1,4 @@
-import { IsNotEmpty, IsOptional } from 'class-validator';
+import { IsOptional } from 'class-validator';
 
 
 export class UpdateAlbumDto {
 export class UpdateAlbumDto {
   @IsOptional()
   @IsOptional()

+ 1 - 2
server/apps/immich/src/api-v1/asset/asset.controller.ts

@@ -15,7 +15,6 @@ import {
   HttpCode,
   HttpCode,
   BadRequestException,
   BadRequestException,
   UploadedFile,
   UploadedFile,
-  Header,
 } from '@nestjs/common';
 } from '@nestjs/common';
 import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 import { AssetService } from './asset.service';
 import { AssetService } from './asset.service';
@@ -34,7 +33,7 @@ import { IAssetUploadedJob } from '@app/job/index';
 import { assetUploadedQueueName } from '@app/job/constants/queue-name.constant';
 import { assetUploadedQueueName } from '@app/job/constants/queue-name.constant';
 import { assetUploadedProcessorName } from '@app/job/constants/job-name.constant';
 import { assetUploadedProcessorName } from '@app/job/constants/job-name.constant';
 import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
-import { ApiBearerAuth, ApiBody, ApiConsumes, ApiResponse, ApiTags } from '@nestjs/swagger';
+import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
 import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
 import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
 import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
 import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
 import { AssetResponseDto } from './response-dto/asset-response.dto';
 import { AssetResponseDto } from './response-dto/asset-response.dto';

+ 32 - 33
server/apps/immich/src/api-v1/asset/asset.service.spec.ts

@@ -1,9 +1,8 @@
-import { AssetRepository, IAssetRepository } from './asset-repository';
+import { IAssetRepository } from './asset-repository';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { AssetService } from './asset.service';
 import { AssetService } from './asset.service';
 import { Repository } from 'typeorm';
 import { Repository } from 'typeorm';
-import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
-import { CreateAssetDto } from './dto/create-asset.dto';
+import { AssetEntity } from '@app/database/entities/asset.entity';
 
 
 describe('AssetService', () => {
 describe('AssetService', () => {
   let sui: AssetService;
   let sui: AssetService;
@@ -15,39 +14,39 @@ describe('AssetService', () => {
     email: 'auth@test.com',
     email: 'auth@test.com',
   });
   });
 
 
-  const _getCreateAssetDto = (): CreateAssetDto => {
-    const createAssetDto = new CreateAssetDto();
-    createAssetDto.deviceAssetId = 'deviceAssetId';
-    createAssetDto.deviceId = 'deviceId';
-    createAssetDto.assetType = AssetType.OTHER;
-    createAssetDto.createdAt = '2022-06-19T23:41:36.910Z';
-    createAssetDto.modifiedAt = '2022-06-19T23:41:36.910Z';
-    createAssetDto.isFavorite = false;
-    createAssetDto.duration = '0:00:00.000000';
+  // const _getCreateAssetDto = (): CreateAssetDto => {
+  //   const createAssetDto = new CreateAssetDto();
+  //   createAssetDto.deviceAssetId = 'deviceAssetId';
+  //   createAssetDto.deviceId = 'deviceId';
+  //   createAssetDto.assetType = AssetType.OTHER;
+  //   createAssetDto.createdAt = '2022-06-19T23:41:36.910Z';
+  //   createAssetDto.modifiedAt = '2022-06-19T23:41:36.910Z';
+  //   createAssetDto.isFavorite = false;
+  //   createAssetDto.duration = '0:00:00.000000';
 
 
-    return createAssetDto;
-  };
-  const _getAsset = () => {
-    const assetEntity = new AssetEntity();
+  //   return createAssetDto;
+  // };
+  // const _getAsset = () => {
+  //   const assetEntity = new AssetEntity();
 
 
-    assetEntity.id = 'e8edabfd-7d8a-45d0-9d61-7c7ca60f2c67';
-    assetEntity.userId = '3ea54709-e168-42b7-90b0-a0dfe8a7ecbd';
-    assetEntity.deviceAssetId = '4967046344801';
-    assetEntity.deviceId = '116766fd-2ef2-52dc-a3ef-149988997291';
-    assetEntity.type = AssetType.VIDEO;
-    assetEntity.originalPath =
-      'upload/3ea54709-e168-42b7-90b0-a0dfe8a7ecbd/original/116766fd-2ef2-52dc-a3ef-149988997291/51c97f95-244f-462d-bdf0-e1dc19913516.jpg';
-    assetEntity.resizePath = '';
-    assetEntity.createdAt = '2022-06-19T23:41:36.910Z';
-    assetEntity.modifiedAt = '2022-06-19T23:41:36.910Z';
-    assetEntity.isFavorite = false;
-    assetEntity.mimeType = 'image/jpeg';
-    assetEntity.webpPath = '';
-    assetEntity.encodedVideoPath = '';
-    assetEntity.duration = '0:00:00.000000';
+  //   assetEntity.id = 'e8edabfd-7d8a-45d0-9d61-7c7ca60f2c67';
+  //   assetEntity.userId = '3ea54709-e168-42b7-90b0-a0dfe8a7ecbd';
+  //   assetEntity.deviceAssetId = '4967046344801';
+  //   assetEntity.deviceId = '116766fd-2ef2-52dc-a3ef-149988997291';
+  //   assetEntity.type = AssetType.VIDEO;
+  //   assetEntity.originalPath =
+  //     'upload/3ea54709-e168-42b7-90b0-a0dfe8a7ecbd/original/116766fd-2ef2-52dc-a3ef-149988997291/51c97f95-244f-462d-bdf0-e1dc19913516.jpg';
+  //   assetEntity.resizePath = '';
+  //   assetEntity.createdAt = '2022-06-19T23:41:36.910Z';
+  //   assetEntity.modifiedAt = '2022-06-19T23:41:36.910Z';
+  //   assetEntity.isFavorite = false;
+  //   assetEntity.mimeType = 'image/jpeg';
+  //   assetEntity.webpPath = '';
+  //   assetEntity.encodedVideoPath = '';
+  //   assetEntity.duration = '0:00:00.000000';
 
 
-    return assetEntity;
-  };
+  //   return assetEntity;
+  // };
 
 
   beforeAll(() => {
   beforeAll(() => {
     assetRepositoryMock = {
     assetRepositoryMock = {

+ 1 - 2
server/apps/immich/src/api-v1/asset/dto/get-asset-thumbnail.dto.ts

@@ -1,6 +1,5 @@
 import { ApiProperty } from '@nestjs/swagger';
 import { ApiProperty } from '@nestjs/swagger';
-import { Transform } from 'class-transformer';
-import { IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
+import { IsOptional } from 'class-validator';
 
 
 export enum GetAssetThumbnailFormatEnum {
 export enum GetAssetThumbnailFormatEnum {
   JPEG = 'JPEG',
   JPEG = 'JPEG',

+ 2 - 2
server/apps/immich/src/api-v1/asset/dto/serve-file.dto.ts

@@ -1,6 +1,6 @@
 import { ApiProperty } from '@nestjs/swagger';
 import { ApiProperty } from '@nestjs/swagger';
-import { Transform, Type } from 'class-transformer';
-import { IsBoolean, IsBooleanString, IsNotEmpty, IsOptional } from 'class-validator';
+import { Transform } from 'class-transformer';
+import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator';
 
 
 export class ServeFileDto {
 export class ServeFileDto {
   @IsNotEmpty()
   @IsNotEmpty()

+ 1 - 8
server/apps/immich/src/api-v1/auth/auth.controller.ts

@@ -1,12 +1,5 @@
 import { Body, Controller, Post, Res, UseGuards, ValidationPipe } from '@nestjs/common';
 import { Body, Controller, Post, Res, UseGuards, ValidationPipe } from '@nestjs/common';
-import {
-  ApiBadRequestResponse,
-  ApiBearerAuth,
-  ApiBody,
-  ApiCreatedResponse,
-  ApiResponse,
-  ApiTags,
-} from '@nestjs/swagger';
+import { ApiBadRequestResponse, ApiBearerAuth, ApiTags } from '@nestjs/swagger';
 import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
 import { AuthService } from './auth.service';
 import { AuthService } from './auth.service';

+ 1 - 1
server/apps/immich/src/api-v1/auth/response-dto/login-response.dto.ts

@@ -1,5 +1,5 @@
 import { UserEntity } from '@app/database/entities/user.entity';
 import { UserEntity } from '@app/database/entities/user.entity';
-import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger';
+import { ApiResponseProperty } from '@nestjs/swagger';
 
 
 export class LoginResponseDto {
 export class LoginResponseDto {
   @ApiResponseProperty()
   @ApiResponseProperty()

+ 0 - 2
server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts

@@ -1,8 +1,6 @@
 import { DeviceType } from '@app/database/entities/device-info.entity';
 import { DeviceType } from '@app/database/entities/device-info.entity';
-import { PartialType } from '@nestjs/mapped-types';
 import { ApiProperty } from '@nestjs/swagger';
 import { ApiProperty } from '@nestjs/swagger';
 import { IsNotEmpty, IsOptional } from 'class-validator';
 import { IsNotEmpty, IsOptional } from 'class-validator';
-import { CreateDeviceInfoDto } from './create-device-info.dto';
 
 
 export class UpdateDeviceInfoDto {
 export class UpdateDeviceInfoDto {
   @IsNotEmpty()
   @IsNotEmpty()

+ 1 - 4
server/apps/immich/src/api-v1/server-info/server-info.controller.ts

@@ -1,13 +1,10 @@
-import { Controller, Get, UseGuards } from '@nestjs/common';
-import { ConfigService } from '@nestjs/config';
-import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
+import { Controller, Get } from '@nestjs/common';
 import { ServerInfoService } from './server-info.service';
 import { ServerInfoService } from './server-info.service';
 import { serverVersion } from '../../constants/server_version.constant';
 import { serverVersion } from '../../constants/server_version.constant';
 import { ApiTags } from '@nestjs/swagger';
 import { ApiTags } from '@nestjs/swagger';
 import { ServerPingResponse } from './response-dto/server-ping-response.dto';
 import { ServerPingResponse } from './response-dto/server-ping-response.dto';
 import { ServerVersionReponseDto } from './response-dto/server-version-response.dto';
 import { ServerVersionReponseDto } from './response-dto/server-version-response.dto';
 import { ServerInfoResponseDto } from './response-dto/server-info-response.dto';
 import { ServerInfoResponseDto } from './response-dto/server-info-response.dto';
-import { DataSource } from 'typeorm';
 
 
 @ApiTags('Server Info')
 @ApiTags('Server Info')
 @Controller('server-info')
 @Controller('server-info')

+ 0 - 1
server/apps/immich/src/api-v1/user/user.controller.ts

@@ -12,7 +12,6 @@ import {
   UploadedFile,
   UploadedFile,
   Response,
   Response,
   Request,
   Request,
-  StreamableFile,
   ParseBoolPipe,
   ParseBoolPipe,
 } from '@nestjs/common';
 } from '@nestjs/common';
 import { UserService } from './user.service';
 import { UserService } from './user.service';

+ 0 - 1
server/apps/immich/src/app.module.ts

@@ -15,7 +15,6 @@ import { AppController } from './app.controller';
 import { ScheduleModule } from '@nestjs/schedule';
 import { ScheduleModule } from '@nestjs/schedule';
 import { ScheduleTasksModule } from './modules/schedule-tasks/schedule-tasks.module';
 import { ScheduleTasksModule } from './modules/schedule-tasks/schedule-tasks.module';
 import { DatabaseModule } from '@app/database';
 import { DatabaseModule } from '@app/database';
-import { AppLoggerMiddleware } from './middlewares/app-logger.middleware';
 
 
 @Module({
 @Module({
   imports: [
   imports: [

+ 0 - 1
server/apps/immich/src/main.ts

@@ -1,4 +1,3 @@
-import { dataSource } from '@app/database/config/database.config';
 import { Logger } from '@nestjs/common';
 import { Logger } from '@nestjs/common';
 import { NestFactory } from '@nestjs/core';
 import { NestFactory } from '@nestjs/core';
 import { NestExpressApplication } from '@nestjs/platform-express';
 import { NestExpressApplication } from '@nestjs/platform-express';

+ 0 - 1
server/apps/immich/src/modules/background-task/background-task.service.ts

@@ -2,7 +2,6 @@ import { InjectQueue } from '@nestjs/bull/dist/decorators';
 import { Injectable } from '@nestjs/common';
 import { Injectable } from '@nestjs/common';
 import { Queue } from 'bull';
 import { Queue } from 'bull';
 import { randomUUID } from 'node:crypto';
 import { randomUUID } from 'node:crypto';
-import { AssetEntity } from '@app/database/entities/asset.entity';
 import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
 import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
 
 
 @Injectable()
 @Injectable()

+ 1 - 1
server/apps/microservices/src/processors/generate-checksum.processor.ts

@@ -18,7 +18,7 @@ export class GenerateChecksumProcessor {
   @Process()
   @Process()
   async generateChecksum() {
   async generateChecksum() {
     let hasNext = true;
     let hasNext = true;
-    let pageSize = 200;
+    const pageSize = 200;
 
 
     while (hasNext) {
     while (hasNext) {
       const assets = await this.assetRepository.find({
       const assets = await this.assetRepository.find({

+ 1 - 2
server/apps/microservices/src/processors/metadata-extraction.processor.ts

@@ -21,7 +21,6 @@ import axios from 'axios';
 import { Job } from 'bull';
 import { Job } from 'bull';
 import exifr from 'exifr';
 import exifr from 'exifr';
 import ffmpeg from 'fluent-ffmpeg';
 import ffmpeg from 'fluent-ffmpeg';
-import { readFile } from 'fs/promises';
 import path from 'path';
 import path from 'path';
 import sharp from 'sharp';
 import sharp from 'sharp';
 import { Repository } from 'typeorm/repository/Repository';
 import { Repository } from 'typeorm/repository/Repository';
@@ -330,7 +329,7 @@ export class MetadataExtractionProcessor {
           }
           }
 
 
           if (stream.r_frame_rate) {
           if (stream.r_frame_rate) {
-            let fpsParts = stream.r_frame_rate.split('/');
+            const fpsParts = stream.r_frame_rate.split('/');
 
 
             if (fpsParts.length === 2) {
             if (fpsParts.length === 2) {
               newExif.fps = Math.round(parseInt(fpsParts[0]) / parseInt(fpsParts[1]));
               newExif.fps = Math.round(parseInt(fpsParts[0]) / parseInt(fpsParts[1]));