From d20b05d76090491b8dc9986e8e7511638b967586 Mon Sep 17 00:00:00 2001 From: Pavel Gnedov Date: Tue, 19 Jul 2022 03:44:08 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20=D1=8E?= =?UTF-8?q?=D0=B7=D0=B5=D1=80=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../redmine-issues-cache-writer.service.ts | 2 +- .../index.d.ts => redmine-types.ts} | 17 +++- .../src/models/redmine-types/index.js | 0 .../src/models/redmine-types/package.json | 12 --- .../event-emitter/src/models/save-response.ts | 2 +- .../redmine-data-loader.ts | 2 +- .../user-cache-writer.service.ts | 2 +- .../src/users/users.controller.ts | 2 +- libs/event-emitter/src/users/users.service.ts | 52 ++++++---- libs/event-emitter/src/utils/memory-cache.ts | 2 +- .../src/utils/timestamp-is-timeouted.ts | 2 +- .../src/utils/timestamp-now-fill.ts | 2 +- libs/redmine-types/index.d.ts | 99 ------------------- libs/redmine-types/index.js | 0 libs/redmine-types/package.json | 12 --- package-lock.json | 11 +-- package.json | 5 +- 17 files changed, 54 insertions(+), 170 deletions(-) rename libs/event-emitter/src/models/{redmine-types/index.d.ts => redmine-types.ts} (87%) delete mode 100644 libs/event-emitter/src/models/redmine-types/index.js delete mode 100644 libs/event-emitter/src/models/redmine-types/package.json delete mode 100644 libs/redmine-types/index.d.ts delete mode 100644 libs/redmine-types/index.js delete mode 100644 libs/redmine-types/package.json diff --git a/libs/event-emitter/src/issue-cache-writer/redmine-issues-cache-writer.service.ts b/libs/event-emitter/src/issue-cache-writer/redmine-issues-cache-writer.service.ts index 7f205a9..0c38f2b 100644 --- a/libs/event-emitter/src/issue-cache-writer/redmine-issues-cache-writer.service.ts +++ b/libs/event-emitter/src/issue-cache-writer/redmine-issues-cache-writer.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { RedmineTypes } from 'libs/redmine-types'; +import { RedmineTypes } from '../models/redmine-types'; import nano from 'nano'; import { Subject } from 'rxjs'; import { Issues } from '../couchdb-datasources/issues'; diff --git a/libs/event-emitter/src/models/redmine-types/index.d.ts b/libs/event-emitter/src/models/redmine-types.ts similarity index 87% rename from libs/event-emitter/src/models/redmine-types/index.d.ts rename to libs/event-emitter/src/models/redmine-types.ts index a904d28..09c3587 100644 --- a/libs/event-emitter/src/models/redmine-types/index.d.ts +++ b/libs/event-emitter/src/models/redmine-types.ts @@ -1,6 +1,4 @@ -/// - -// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword +// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword, @typescript-eslint/no-namespace export module RedmineTypes { export type IdAndName = { id: number; @@ -51,7 +49,7 @@ export module RedmineTypes { journals?: Journal[]; }; - // eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword + // eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword, @typescript-eslint/no-namespace export module Unknown { export const num = -1; export const str = ''; @@ -83,6 +81,7 @@ export module RedmineTypes { export const user: User = { id: num, + login: unknownName, firstname: unknownName, lastname: unknownName, mail: str, @@ -96,4 +95,14 @@ export module RedmineTypes { lastname: string; mail: string; }; + + export function ExtractUser(obj: User): User { + return { + id: obj.id, + login: obj.login, + firstname: obj.firstname, + lastname: obj.lastname, + mail: obj.mail, + }; + } } diff --git a/libs/event-emitter/src/models/redmine-types/index.js b/libs/event-emitter/src/models/redmine-types/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/libs/event-emitter/src/models/redmine-types/package.json b/libs/event-emitter/src/models/redmine-types/package.json deleted file mode 100644 index 4df9704..0000000 --- a/libs/event-emitter/src/models/redmine-types/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "redmine-types", - "version": "0.1.0", - "description": "", - "main": "index.js", - "types": "index.d.ts", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Pavel Gnedov", - "license": "MIT" -} diff --git a/libs/event-emitter/src/models/save-response.ts b/libs/event-emitter/src/models/save-response.ts index 91f2d05..d43c4f6 100644 --- a/libs/event-emitter/src/models/save-response.ts +++ b/libs/event-emitter/src/models/save-response.ts @@ -1,4 +1,4 @@ -import { RedmineTypes } from 'libs/redmine-types'; +import { RedmineTypes } from './redmine-types'; // TODO: Переименовать в IssueCacheSaveResponse diff --git a/libs/event-emitter/src/redmine-data-loader/redmine-data-loader.ts b/libs/event-emitter/src/redmine-data-loader/redmine-data-loader.ts index dcbca96..f116783 100644 --- a/libs/event-emitter/src/redmine-data-loader/redmine-data-loader.ts +++ b/libs/event-emitter/src/redmine-data-loader/redmine-data-loader.ts @@ -51,7 +51,7 @@ export class RedmineDataLoader { this.logger.debug( `Loaded user, userNumber = ${userNumber}, login = ${user.login}, firstname = ${user.firstname}, lastname = ${user.lastname}`, ); - return user; + return RedmineTypes.ExtractUser(user); } private getIssueUrl(issueNumber: number): string { diff --git a/libs/event-emitter/src/user-cache-writer/user-cache-writer.service.ts b/libs/event-emitter/src/user-cache-writer/user-cache-writer.service.ts index 14157d7..3615d6f 100644 --- a/libs/event-emitter/src/user-cache-writer/user-cache-writer.service.ts +++ b/libs/event-emitter/src/user-cache-writer/user-cache-writer.service.ts @@ -24,7 +24,7 @@ export class RedmineUserCacheWriterService { prevUser = null; } const newUser: nano.DocumentGetResponse & RedmineTypes.User & Timestamped = - TimestampNowFill({ ...(user as any) }); + TimestampNowFill({ ...(user as any), _id: String(id) }); if (prevUser) { newUser._rev = prevUser._rev; } diff --git a/libs/event-emitter/src/users/users.controller.ts b/libs/event-emitter/src/users/users.controller.ts index 2367d43..686723e 100644 --- a/libs/event-emitter/src/users/users.controller.ts +++ b/libs/event-emitter/src/users/users.controller.ts @@ -11,7 +11,7 @@ export class UsersController { return await this.usersService.getUser(id); } - @Get(':id.json') + @Get(':id/json') async getUserLikeRedmine( @Param('id') id: number, ): Promise<{ user: RedmineTypes.User }> { diff --git a/libs/event-emitter/src/users/users.service.ts b/libs/event-emitter/src/users/users.service.ts index 40b543e..1209dce 100644 --- a/libs/event-emitter/src/users/users.service.ts +++ b/libs/event-emitter/src/users/users.service.ts @@ -1,17 +1,21 @@ -import { Injectable } from '@nestjs/common'; -import { RedmineTypes } from '../models/redmine-types'; +import { Injectable, Logger } from '@nestjs/common'; import { Timestamped } from '../models/timestamped'; import { Users } from '../couchdb-datasources/users'; import { RedmineDataLoader } from '../redmine-data-loader/redmine-data-loader'; -import { TimestampNowFill } from '../utils/timestamp-now-fill'; import { RedmineUserCacheWriterService } from '../user-cache-writer/user-cache-writer.service'; -import { TimestampIsTimeouted } from '../utils/timestamp-is-timeouted'; +import { RedmineTypes } from '../models/redmine-types'; +import { MemoryCache } from '../utils/memory-cache'; export const USER_MEMORY_CACHE_LIFETIME = 24 * 60 * 60 * 1000; +const USER_MEMORY_CACHE_AUTOCLEAN_INTERVAL = 1000 * 60 * 5; @Injectable() export class UsersService { - private memoryCache: Record = {}; + private logger = new Logger(UsersService.name); + private memoryCache = new MemoryCache( + USER_MEMORY_CACHE_LIFETIME, + USER_MEMORY_CACHE_AUTOCLEAN_INTERVAL, + ); constructor( private users: Users, @@ -26,8 +30,8 @@ export class UsersService { } const userFromCache = await this.getUserFromCache(userId); if (userFromCache) { - this.memoryCache[userId] = TimestampNowFill({ ...userFromCache }); - return this.memoryCache[userId]; + this.memoryCache.set(userId, userFromCache); + return userFromCache; } let userFromRedmine = await this.getUserFromRedmine(userId); if (userFromRedmine) { @@ -35,13 +39,18 @@ export class UsersService { userFromRedmine, ); } - const unknownUser = TimestampNowFill({ ...RedmineTypes.Unknown.user }); - this.memoryCache[userId] = (userFromRedmine || unknownUser) as any; - return this.memoryCache[userId]; + return this.memoryCache.set( + userId, + userFromRedmine || RedmineTypes.Unknown.user, + ); } async getUserFromRedmine(userId: number): Promise { - return await this.redmineDataLoader.loadUser(userId); + const user = await this.redmineDataLoader.loadUser(userId); + this.logger.debug( + `Get user from redmine with userId = ${userId}, login = ${user.login}`, + ); + return user; } async getUserFromCache( @@ -49,22 +58,23 @@ export class UsersService { ): Promise<(RedmineTypes.User & Timestamped) | null> { const usersDb = await this.users.getDatasource(); try { - return (await usersDb.get(String(userId))) as any; + const user = (await usersDb.get(String(userId))) as any; + this.logger.debug( + `Get user from couchdb with userId = ${userId}, login = ${user.login}`, + ); + return user; } catch (ex) { return null; } } getUserFromMemoryCache(userId: number): RedmineTypes.User | null { - if ( - this.memoryCache[userId] && - !TimestampIsTimeouted( - this.memoryCache[userId], - USER_MEMORY_CACHE_LIFETIME, - ) - ) { - return this.memoryCache[userId]; + const user = this.memoryCache.get(userId); + if (user) { + this.logger.debug( + `Get user from memory cache with userId = ${userId}, login = ${user.login}`, + ); } - return null; + return user; } } diff --git a/libs/event-emitter/src/utils/memory-cache.ts b/libs/event-emitter/src/utils/memory-cache.ts index 59c5fb6..b7433a4 100644 --- a/libs/event-emitter/src/utils/memory-cache.ts +++ b/libs/event-emitter/src/utils/memory-cache.ts @@ -11,7 +11,7 @@ export class MemoryCache { } } - get(key: K): T | null { + get(key: K): (T & Timestamped) | null { const k = key as any; if (this.memoryCache[k]) { if (TimestampIsTimeouted(this.memoryCache[k], this.timeout)) { diff --git a/libs/event-emitter/src/utils/timestamp-is-timeouted.ts b/libs/event-emitter/src/utils/timestamp-is-timeouted.ts index dd07e17..55f4e49 100644 --- a/libs/event-emitter/src/utils/timestamp-is-timeouted.ts +++ b/libs/event-emitter/src/utils/timestamp-is-timeouted.ts @@ -4,6 +4,6 @@ export function TimestampIsTimeouted( obj: Timestamped, timeout: number, ): boolean { - const now = new Date().getDate(); + const now = new Date().getTime(); return obj.timestamp__ < now - timeout; } diff --git a/libs/event-emitter/src/utils/timestamp-now-fill.ts b/libs/event-emitter/src/utils/timestamp-now-fill.ts index 2b14705..79f7aa3 100644 --- a/libs/event-emitter/src/utils/timestamp-now-fill.ts +++ b/libs/event-emitter/src/utils/timestamp-now-fill.ts @@ -1,6 +1,6 @@ import { Timestamped } from '../models/timestamped'; export function TimestampNowFill(obj: T): T & Timestamped { - const now = new Date().getDate(); + const now = new Date().getTime(); return { ...obj, timestamp__: now }; } diff --git a/libs/redmine-types/index.d.ts b/libs/redmine-types/index.d.ts deleted file mode 100644 index a904d28..0000000 --- a/libs/redmine-types/index.d.ts +++ /dev/null @@ -1,99 +0,0 @@ -/// - -// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword -export module RedmineTypes { - export type IdAndName = { - id: number; - name: string; - }; - - export type CustomField = { - id: number; - name: string; - value: string; - }; - - export type JournalDetail = { - property: string; - name: string; - old_value?: string; - new_value?: string; - }; - - export type Journal = { - id: number; - user: IdAndName; - notes?: string; - created_on: string; - details?: JournalDetail[]; - }; - - export type Issue = { - id: number; - project: IdAndName; - tracker: IdAndName; - status: IdAndName; - priority: IdAndName; - author: IdAndName; - category: IdAndName; - fixed_version: IdAndName; - subject: string; - description: string; - start_date: string; - done_ratio: number; - spent_hours: number; - total_spent_hours: number; - custom_fields: CustomField[]; - created_on: string; - updated_on?: string; - closed_on?: string; - relations?: Record[]; - journals?: Journal[]; - }; - - // eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword - export module Unknown { - export const num = -1; - export const str = ''; - export const idAndName: IdAndName = { - id: -1, - name: str, - }; - export const unknownName = 'Unknown'; - export const subject = 'Unknown'; - export const date = '1970-01-01T00:00:00Z'; - export const issue: Issue = { - id: num, - project: idAndName, - tracker: idAndName, - status: idAndName, - priority: idAndName, - author: idAndName, - category: idAndName, - fixed_version: idAndName, - subject: subject, - description: str, - start_date: date, - done_ratio: num, - spent_hours: num, - total_spent_hours: num, - custom_fields: [], - created_on: date, - }; - - export const user: User = { - id: num, - firstname: unknownName, - lastname: unknownName, - mail: str, - }; - } - - export type User = { - id: number; - login: string; - firstname: string; - lastname: string; - mail: string; - }; -} diff --git a/libs/redmine-types/index.js b/libs/redmine-types/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/libs/redmine-types/package.json b/libs/redmine-types/package.json deleted file mode 100644 index 4df9704..0000000 --- a/libs/redmine-types/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "redmine-types", - "version": "0.1.0", - "description": "", - "main": "index.js", - "types": "index.d.ts", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Pavel Gnedov", - "license": "MIT" -} diff --git a/package-lock.json b/package-lock.json index 96a6d7b..d5b4a29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "name": "eltex-redmine-helper-2", "version": "0.0.1", "license": "UNLICENSED", - "workspaces": [ - "libs/redmine-types" - ], "dependencies": { "@nestjs/common": "^8.0.0", "@nestjs/config": "^2.0.0", @@ -54,6 +51,7 @@ }, "libs/redmine-types": { "version": "0.1.0", + "extraneous": true, "license": "MIT" }, "node_modules/@ampproject/remapping": { @@ -6595,10 +6593,6 @@ "node": ">= 0.10" } }, - "node_modules/redmine-types": { - "resolved": "libs/redmine-types", - "link": true - }, "node_modules/reflect-metadata": { "version": "0.1.13", "license": "Apache-2.0" @@ -12384,9 +12378,6 @@ "resolve": "^1.1.6" } }, - "redmine-types": { - "version": "file:libs/redmine-types" - }, "reflect-metadata": { "version": "0.1.13" }, diff --git a/package.json b/package.json index 3a39561..a61e0d0 100644 --- a/package.json +++ b/package.json @@ -83,8 +83,5 @@ "moduleNameMapper": { "^@app/event-emitter(|/.*)$": "/libs/event-emitter/src/$1" } - }, - "workspaces": [ - "libs/redmine-types" - ] + } }