Прогнал линтером для автоматических доделок, но оставлять его не стал - слишком много ошибок

This commit is contained in:
Nikita Dezzpil Orlov 2021-09-29 18:01:08 +03:00
parent ce3a192cbd
commit c6db8bc2fe
14 changed files with 118 additions and 8324 deletions

View File

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -14,7 +14,7 @@ export default {
bail: 1, bail: 1,
verbose: true, verbose: true,
testSequencer: '<rootDir>/src/tests/conf/sequencer.js', testSequencer: '<rootDir>/src/tests/conf/sequencer.js',
testTimeout: 60000, testTimeout: 60_000,
coverageProvider: 'v8', coverageProvider: 'v8',
coverageReporters: ['json', 'lcov', 'text', 'clover', 'teamcity'], coverageReporters: ['json', 'lcov', 'text', 'clover', 'teamcity'],

8204
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,7 @@
], ],
"scripts": { "scripts": {
"test": "node --no-warnings node_modules/.bin/jest --runInBand --forceExit", "test": "node --no-warnings node_modules/.bin/jest --runInBand --forceExit",
"prepare": "npm run lint && npm test && npm run build", "prepare": "npm test && npm run build",
"lint": "xo --space=4",
"build": "tsc -b -v" "build": "tsc -b -v"
}, },
"repository": { "repository": {
@ -25,11 +24,9 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@jest/test-sequencer": "^27.2.3", "@jest/test-sequencer": "^27.2.3",
"@types/bson": "^4.2.0",
"@types/chai": "^4.2.22", "@types/chai": "^4.2.22",
"@types/jest": "^27.0.2", "@types/jest": "^27.0.2",
"@types/node": "^14.17.19", "@types/node": "^14.17.19",
"@types/rfdc": "^1.2.0",
"chai": "^4.3.4", "chai": "^4.3.4",
"jasmine": "^3.9.0", "jasmine": "^3.9.0",
"jasmine-fail-fast": "^2.0.1", "jasmine-fail-fast": "^2.0.1",
@ -40,8 +37,7 @@
"rfdc": "^1.3.0", "rfdc": "^1.3.0",
"ts-jest": "^27.0.5", "ts-jest": "^27.0.5",
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"typescript": "^4.4.3", "typescript": "^4.4.3"
"xo": "^0.44.0"
}, },
"dependencies": { "dependencies": {
"mongodb": "^4.1.2", "mongodb": "^4.1.2",

View File

@ -1,33 +1,32 @@
import rfdc from 'rfdc' import rfdc from 'rfdc'
interface ValuesObject { type ValuesObject = Record<string, any>
[key: string]: any
}
export abstract class Data { export abstract class Data {
[key: string]: any
static assign(vo: Data, values: ValuesObject): Data { static assign(vo: Data, values: ValuesObject): Data {
for (let key of Object.getOwnPropertyNames(values)) { for (const key of Object.getOwnPropertyNames(values)) {
vo[key] = values[key] vo[key] = values[key]
} }
return vo return vo
} }
[key: string]: any
set(key: string, value: any) { set(key: string, value: any) {
this[key] = value this[key] = value
} }
fromObject(obj: ValuesObject): void { fromObject(object: ValuesObject): void {
for (let key of Object.getOwnPropertyNames(obj)) { for (const key of Object.getOwnPropertyNames(object)) {
if (key === '_id') continue if (key === '_id') continue
this[key] = obj[key] this[key] = object[key]
} }
} }
toObject(exclude: string[] = []): object { toObject(exclude: string[] = []): Record<string, any> {
const obj = {} as ValuesObject const object = {} as ValuesObject
for (let key of Object.getOwnPropertyNames(this)) { for (const key of Object.getOwnPropertyNames(this)) {
if (key.startsWith('__')) { if (key.startsWith('__')) {
continue continue
} }
@ -36,14 +35,15 @@ export abstract class Data {
const type = typeof this[key] const type = typeof this[key]
if (type === 'object') { if (type === 'object') {
obj[key] = rfdc()(this[key]) object[key] = rfdc()(this[key])
} else if (['function', 'undefined'].indexOf(type) >= 0) { } else if (['function', 'undefined'].includes(type)) {
// ... // ...
} else { } else {
obj[key] = this[key] object[key] = this[key]
} }
} }
return obj
return object
} }
abstract uniqKey(): string abstract uniqKey(): string

View File

@ -2,9 +2,7 @@ import { Data } from './data'
import { Repo } from './repo' import { Repo } from './repo'
import { Storage } from './storage' import { Storage } from './storage'
export type EntityConstructor<T extends Data> = { export type EntityConstructor<T extends Data> = new (data?: T) => Entity<T>
new (data?: T): Entity<T>
}
export class ErrEntityHasNoUniqKeyValue extends Error {} export class ErrEntityHasNoUniqKeyValue extends Error {}
@ -13,7 +11,7 @@ export abstract class Entity<T extends Data> {
abstract _getVO(): T abstract _getVO(): T
protected _data: T protected _data: T
private __id: string = '' // storage inner id private __id = '' // Storage inner id
get data(): T { get data(): T {
return this._data return this._data

View File

@ -8,8 +8,8 @@ export abstract class Repo<T extends Entity<any>> {
protected _storage: Storage protected _storage: Storage
protected _entity: T protected _entity: T
protected _limit: number = 0 protected _limit = 0
protected _offset: number = 0 protected _offset = 0
/** /**
* Возвращает объект соотв. сущности, например new App() * Возвращает объект соотв. сущности, например new App()
@ -21,8 +21,7 @@ export abstract class Repo<T extends Entity<any>> {
*/ */
abstract Name(): string abstract Name(): string
// @ts-ignore _transformer: (object: any) => any
_transformer: (obj: any) => any
constructor(storage: Storage) { constructor(storage: Storage) {
this._storage = storage this._storage = storage
@ -31,10 +30,10 @@ export abstract class Repo<T extends Entity<any>> {
} }
resetTransformer() { resetTransformer() {
this._transformer = function (obj: any): any { this._transformer = function (object: any): any {
const entity = this.Entity() const entity = this.Entity()
entity.data.fromObject(obj) entity.data.fromObject(object)
entity._id = obj._id entity._id = object._id
return entity return entity
} }
} }
@ -58,7 +57,8 @@ export abstract class Repo<T extends Entity<any>> {
`found few (${entryList.length}) entries in ${this.Name()} for id ${id}` `found few (${entryList.length}) entries in ${this.Name()} for id ${id}`
) )
} }
if (entryList.length == 0) {
if (entryList.length === 0) {
throw new ErrEntityNotFound(`not found entry in [${this.Name()}] for id [${id}]`) throw new ErrEntityNotFound(`not found entry in [${this.Name()}] for id [${id}]`)
} }
@ -69,27 +69,31 @@ export abstract class Repo<T extends Entity<any>> {
* *
* @protected * @protected
*/ */
async _findByParams(params: {}, limit?: number, order?: {}): Promise<T[]> { async _findByParams(
const cursor = await this._storage.find(this.Name(), params) parameters: Record<string, unknown>,
limit?: number,
order?: Record<string, unknown>
): Promise<T[]> {
const cursor = await this._storage.find(this.Name(), parameters)
if (limit && limit > 0) await cursor.limit(limit) if (limit && limit > 0) await cursor.limit(limit)
if (order) await cursor.sort(order) if (order) await cursor.sort(order)
const data = await cursor.toArray() const data = await cursor.toArray()
const list = [] const list = []
for (let item of data) { for (const item of data) {
list.push(this._transformer(item)) list.push(this._transformer(item))
} }
return list return list
} }
async findMany(params: object, order?: object): Promise<T[]> { async findMany(parameters: Record<string, unknown>, order?: Record<string, unknown>): Promise<T[]> {
const cursor = await this._storage.find(this.Name(), params) const cursor = await this._storage.find(this.Name(), parameters)
if (this._offset > 0) await cursor.skip(this._offset) if (this._offset > 0) await cursor.skip(this._offset)
if (this._limit > 0) await cursor.limit(this._limit) if (this._limit > 0) await cursor.limit(this._limit)
if (order) await cursor.sort(order) if (order) await cursor.sort(order)
const data = await cursor.toArray() const data = await cursor.toArray()
const list = [] const list = []
for (let item of data) { for (const item of data) {
list.push(this._transformer(item)) list.push(this._transformer(item))
} }
@ -102,7 +106,7 @@ export abstract class Repo<T extends Entity<any>> {
return this return this
} }
async count(query?: object): Promise<number> { async count(query?: Record<string, unknown>): Promise<number> {
return this._storage.count(this.Name(), query) return this._storage.count(this.Name(), query)
} }

View File

@ -1,6 +1,6 @@
export interface StorageCursor { export interface StorageCursor {
limit(num: number): StorageCursor limit(number_: number): StorageCursor
sort(params: {}): StorageCursor sort(parameters: Record<string, unknown>): StorageCursor
skip(offset: number): StorageCursor skip(offset: number): StorageCursor
toArray(): Promise<any[]> toArray(): Promise<any[]>
} }
@ -15,18 +15,18 @@ export abstract class Storage {
this._dsn = dsn this._dsn = dsn
} }
abstract find(name: string, query: Object): Promise<StorageCursor> abstract find(name: string, query: Record<string, unknown>): Promise<StorageCursor>
abstract save(name: string, uniqKey: string, data: Object): Promise<string> abstract save(name: string, uniqKey: string, data: Record<string, unknown>): Promise<string>
abstract createSession(): StorageSession abstract createSession(): StorageSession
abstract count(name: string, query?: object): Promise<number> abstract count(name: string, query?: Record<string, unknown>): Promise<number>
abstract remove(collectionName: string, uniqKeyName: string, uniqKey: string): Promise<boolean> abstract remove(collectionName: string, uniqKeyName: string, uniqKey: string): Promise<boolean>
} }
export abstract class StorageSession { export abstract class StorageSession {
abstract start(options?: {}): Promise<void> abstract start(options?: Record<string, unknown>): Promise<void>
abstract commit(fn: () => any, options?: {}): Promise<void> abstract commit(fn: () => any, options?: Record<string, unknown>): Promise<void>
} }

View File

@ -1,9 +1,9 @@
import { Storage, StorageCursor, StorageSession } from '../storage'
import { ClientSession, MongoClient, TransactionOptions } from 'mongodb' import { ClientSession, MongoClient, TransactionOptions } from 'mongodb'
import { Storage, StorageCursor, StorageSession } from '../storage'
import { ErrEntityHasNoUniqKeyValue } from '../entity' import { ErrEntityHasNoUniqKeyValue } from '../entity'
export class MongoStorage extends Storage { export class MongoStorage extends Storage {
private _client: MongoClient private readonly _client: MongoClient
_session: ClientSession _session: ClientSession
constructor(dsn: string) { constructor(dsn: string) {
@ -20,19 +20,19 @@ export class MongoStorage extends Storage {
await this._client.connect() await this._client.connect()
} }
async find(collectionName: string, query: object): Promise<StorageCursor> { async find(collectionName: string, query: Record<string, unknown>): Promise<StorageCursor> {
await this._connect() await this._connect()
const coll = await this._client.db().collection(collectionName) const coll = await this._client.db().collection(collectionName)
return coll.find(query) return coll.find(query)
} }
async count(collectionName: string, query: object): Promise<number> { async count(collectionName: string, query: Record<string, unknown>): Promise<number> {
await this._connect() await this._connect()
const coll = await this._client.db().collection(collectionName) const coll = await this._client.db().collection(collectionName)
return coll.countDocuments(query) return coll.countDocuments(query)
} }
async save(collectionName: string, uniqKey: string, data: object): Promise<string> { async save(collectionName: string, uniqKey: string, data: Record<string, unknown>): Promise<string> {
await this._connect() await this._connect()
const id = data[uniqKey] const id = data[uniqKey]
const coll = await this._client.db().collection(collectionName) const coll = await this._client.db().collection(collectionName)
@ -42,14 +42,14 @@ export class MongoStorage extends Storage {
if (result.lastErrorObject) { if (result.lastErrorObject) {
if (result.lastErrorObject.updatedExisting) { if (result.lastErrorObject.updatedExisting) {
return result.value._id return result.value._id
} else {
return result.lastErrorObject.upserted
} }
} else {
throw new TypeError(`can not save data to ${collectionName} with result ${result}`) return result.lastErrorObject.upserted
} }
throw new TypeError(`can not save data to ${collectionName} with result ${result}`)
} else { } else {
// нельзя сохранить сущность без значения уникального ключа // Нельзя сохранить сущность без значения уникального ключа
// const result = await coll.insertOne(data) // const result = await coll.insertOne(data)
// return result.insertedId._id.toHexString() // return result.insertedId._id.toHexString()
throw new ErrEntityHasNoUniqKeyValue() throw new ErrEntityHasNoUniqKeyValue()
@ -96,8 +96,8 @@ export class MongoStorageSession extends StorageSession {
async commit(fn, options?: TransactionOptions) { async commit(fn, options?: TransactionOptions) {
try { try {
await this._session.withTransaction(fn, options) await this._session.withTransaction(fn, options)
} catch (e) { } catch (error) {
throw e throw error
} }
await this._session.endSession() await this._session.endSession()

View File

@ -1,13 +1,13 @@
const Sequencer = require('@jest/test-sequencer').default; const Sequencer = require('@jest/test-sequencer').default
class CustomSequencer extends Sequencer { class CustomSequencer extends Sequencer {
sort(tests) { sort(tests) {
// Test structure information // Test structure information
// https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21 // https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21
const copyTests = Array.from(tests); const copyTests = Array.from(tests)
// @ts-ignore // @ts-ignore
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1)); return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1))
} }
} }
module.exports = CustomSequencer; module.exports = CustomSequencer

View File

@ -1,10 +1,10 @@
const failFast = require("jasmine-fail-fast"); const failFast = require('jasmine-fail-fast')
// Добавляем нормальный fail fast // Добавляем нормальный fail fast
// https://github.com/facebook/jest/issues/2867 // https://github.com/facebook/jest/issues/2867
//if (process.argv.includes("--bail")) { // if (process.argv.includes("--bail")) {
// @ts-ignore // @ts-expect-error
const jasmineEnv = jasmine.getEnv(); const jasmineEnv = jasmine.getEnv()
jasmineEnv.addReporter(failFast.init()); jasmineEnv.addReporter(failFast.init())
//} // }

View File

@ -6,30 +6,30 @@ export class ItemData extends Data {
return 'a' return 'a'
} }
a: number = 0 a = 0
b: number b: number
c: { d: number; e?: number } = { d: 1 } c: { d: number; e?: number } = { d: 1 }
__f: number = 0 __f = 0
sum() { sum() {
return this.a + this.c.d + this.c.e return this.a + this.c.d + this.c.e
} }
} }
describe('Data', function () { describe('Data', () => {
describe('#toObject', function () { describe('#toObject', () => {
it('should return object', function () { it('should return object', () => {
const data = new ItemData() const data = new ItemData()
data.a = 1 data.a = 1
data.c = { d: 2, e: undefined } data.c = { d: 2, e: undefined }
const obj = data.toObject() const object = data.toObject()
assert.strictEqual('a' in obj, true) assert.strictEqual('a' in object, true)
assert.strictEqual('b' in obj, false) assert.strictEqual('b' in object, false)
assert.strictEqual('c' in obj, true) assert.strictEqual('c' in object, true)
assert.strictEqual('d' in obj['c'], true) assert.strictEqual('d' in object['c'], true)
assert.strictEqual('e' in obj['c'], true) assert.strictEqual('e' in object['c'], true)
assert.strictEqual('__f' in obj, false) assert.strictEqual('__f' in object, false)
assert.strictEqual('sum' in obj, false) assert.strictEqual('sum' in object, false)
}) })
}) })
describe('#fromObject', () => { describe('#fromObject', () => {
@ -37,10 +37,10 @@ describe('Data', function () {
const dataOrig = new ItemData() const dataOrig = new ItemData()
dataOrig.a = 1 dataOrig.a = 1
dataOrig.c = { d: 2, e: undefined } dataOrig.c = { d: 2, e: undefined }
const obj = dataOrig.toObject() const object = dataOrig.toObject()
const dataRecovered = new ItemData() const dataRecovered = new ItemData()
dataRecovered.fromObject(obj) dataRecovered.fromObject(object)
assert.notStrictEqual(dataOrig, dataRecovered) assert.notStrictEqual(dataOrig, dataRecovered)
}) })

View File

@ -1,10 +1,10 @@
import { ItemData } from './data.test' import assert from 'assert'
import { Entity } from '../app/entity' import { Entity } from '../app/entity'
import { Storage } from '../app/storage' import { Storage } from '../app/storage'
import { Repo } from '../app/repo' import { Repo } from '../app/repo'
import { MapStorage } from './storage.test'
import { Data } from '../app/data' import { Data } from '../app/data'
import assert from 'assert' import { MapStorage } from './storage.test'
import { ItemData } from './data.test'
class Item extends Entity<ItemData> { class Item extends Entity<ItemData> {
_getVO(): ItemData { _getVO(): ItemData {
@ -16,6 +16,7 @@ class Item extends Entity<ItemData> {
getA(): number { getA(): number {
return this._data.a return this._data.a
} }
getC(): { d: number; e?: number } { getC(): { d: number; e?: number } {
return this._data.c return this._data.c
} }
@ -29,13 +30,14 @@ class ItemRepository extends Repo<Item> {
Name() { Name() {
return 'items' return 'items'
} }
Entity() { Entity() {
return new Item() return new Item()
} }
} }
describe('ItemRepository', function () { describe('ItemRepository', () => {
describe('map storage save', function () { describe('map storage save', () => {
const storage = new MapStorage('') const storage = new MapStorage('')
const repo = new ItemRepository(storage) const repo = new ItemRepository(storage)

View File

@ -9,16 +9,16 @@ export class MapCursor implements StorageCursor {
this._map = map this._map = map
} }
limit(num: number): StorageCursor { limit(number_: number): StorageCursor {
this._limit = num this._limit = number_
return this return this
} }
sort(params: {}): StorageCursor { sort(parameters: Record<string, unknown>): StorageCursor {
return this return this
} }
toArray(): Promise<any[]> { async toArray(): Promise<any[]> {
return Promise.resolve(Array.from(this._map.values())) return Promise.resolve(Array.from(this._map.values()))
} }
@ -37,50 +37,52 @@ export class MapStorage extends Storage {
this._mapPool = {} this._mapPool = {}
} }
async find(name: string, query: Object): Promise<StorageCursor> { async find(name: string, query: Record<string, unknown>): Promise<StorageCursor> {
if (!(name in this._mapPool)) { if (!(name in this._mapPool)) {
return new MapCursor(new Map()) return new MapCursor(new Map())
} }
const map: Map<any, any> = new Map() const map: Map<any, any> = new Map()
for (let [_, elem] of this._mapPool[name]) { for (const [_, element] of this._mapPool[name]) {
nextParam: for (let [key, val] of Object.entries(query)) { nextParam: for (const [key, value] of Object.entries(query)) {
if (key in elem) { if (key in element) {
for (let symbol of ['=', '>', '<', '!']) { for (const symbol of ['=', '>', '<', '!']) {
if ((val + '').startsWith(symbol)) { if (String(value).startsWith(symbol) && eval(String(element[key] + value))) {
if (eval(elem[key] + val + '')) { map.set(_, element)
map.set(_, elem) break nextParam
break nextParam
}
} }
} }
if (elem[key] == val) {
map.set(_, elem) if (element[key] == value) {
map.set(_, element)
} }
} }
} }
} }
return new MapCursor(map) return new MapCursor(map)
} }
count(name: string, query: object): Promise<number> { async count(name: string, query: Record<string, unknown>): Promise<number> {
return Promise.resolve(0) return Promise.resolve(0)
} }
remove(collectionName: string, uniqKeyName: string, uniqKey: string): Promise<boolean> { async remove(collectionName: string, uniqKeyName: string, uniqKey: string): Promise<boolean> {
return Promise.resolve(true) return Promise.resolve(true)
} }
async save(name: string, idKey: string, data: Object): Promise<string> { async save(name: string, idKey: string, data: Record<string, unknown>): Promise<string> {
if (!(name in this._mapPool)) { if (!(name in this._mapPool)) {
this._mapPool[name] = new Map() this._mapPool[name] = new Map()
this._mapPoolId[name] = {} this._mapPoolId[name] = {}
} }
const id = data[idKey] + '' const id = String(data[idKey])
this._mapPool[name].set(id, data) this._mapPool[name].set(id, data)
if (!(id in this._mapPoolId[name])) { if (!(id in this._mapPoolId[name])) {
this._mapPoolId[name][id] = Date.now() this._mapPoolId[name][id] = Date.now()
} }
return Promise.resolve(this._mapPoolId[name][id]) return Promise.resolve(this._mapPoolId[name][id])
} }
@ -90,17 +92,17 @@ export class MapStorage extends Storage {
} }
class MapStorageSession extends StorageSession { class MapStorageSession extends StorageSession {
async commit(fn, options?: {}): Promise<any> { async commit(fn, options?: Record<string, unknown>): Promise<any> {
return Promise.resolve(undefined) return Promise.resolve(undefined)
} }
async start(options?: {}): Promise<any> { async start(options?: Record<string, unknown>): Promise<any> {
return Promise.resolve(undefined) return Promise.resolve(undefined)
} }
} }
describe('Storage', function () { describe('Storage', () => {
describe('#save', function () { describe('#save', () => {
it('should return inner id', async () => { it('should return inner id', async () => {
const storage = new MapStorage('') const storage = new MapStorage('')