From 74bfe7931cee3c9469d7eb2cc525a3037df5c53c Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Fri, 25 Mar 2022 15:42:39 +0530 Subject: [PATCH] chore: fix types --- backend/common.ts | 14 ++-- backend/database/core.ts | 138 +++++++++++++++++------------------ backend/database/manager.ts | 27 ++++--- backend/database/runPatch.ts | 2 +- backend/database/types.ts | 27 +++++-- schemas/helpers.ts | 8 +- schemas/index.ts | 44 ++++++----- schemas/regional/index.ts | 3 +- tsconfig.json | 5 +- 9 files changed, 149 insertions(+), 119 deletions(-) diff --git a/backend/common.ts b/backend/common.ts index a5832ebe..79a885cb 100644 --- a/backend/common.ts +++ b/backend/common.ts @@ -1,21 +1,23 @@ -export const sqliteTypeMap = { +import { KnexColumnType } from './database/types'; + +export const sqliteTypeMap: Record = { AutoComplete: 'text', Currency: 'text', Int: 'integer', Float: 'float', Percent: 'float', - Check: 'integer', + Check: 'boolean', Code: 'text', - Date: 'text', - Datetime: 'text', - Time: 'text', + Date: 'date', + Datetime: 'datetime', + Time: 'time', Text: 'text', Data: 'text', Link: 'text', DynamicLink: 'text', Password: 'text', Select: 'text', - File: 'text', + File: 'binary', Attach: 'text', AttachImage: 'text', Color: 'text', diff --git a/backend/database/core.ts b/backend/database/core.ts index 063bb5e6..65fe7415 100644 --- a/backend/database/core.ts +++ b/backend/database/core.ts @@ -6,14 +6,14 @@ import { DuplicateEntryError, LinkValidationError, NotFoundError, - ValueError, + ValueError } from '../../frappe/utils/errors'; import { Field, FieldTypeEnum, RawValue, SchemaMap, - TargetField, + TargetField } from '../../schemas/types'; import { sqliteTypeMap } from '../common'; import { @@ -21,11 +21,11 @@ import { FieldValueMap, GetAllOptions, GetQueryBuilderOptions, - QueryFilter, + QueryFilter } from './types'; export default class DatabaseCore { - knex: Knex; + knex?: Knex; typeMap = sqliteTypeMap; dbPath: string; schemaMap: SchemaMap = {}; @@ -39,7 +39,7 @@ export default class DatabaseCore { filename: this.dbPath, }, pool: { - afterCreate(conn, done) { + afterCreate(conn: { run: (query: string) => void }, done: () => void) { conn.run('PRAGMA foreign_keys=ON'); done(); }, @@ -61,21 +61,22 @@ export default class DatabaseCore { } close() { - this.knex.destroy(); + this.knex!.destroy(); } async commit() { try { await this.raw('commit'); } catch (err) { - if (err.type !== CannotCommitError) { + const type = this.#getError(err as Error); + if (type !== CannotCommitError) { throw err; } } } async raw(query: string, params: Knex.RawBinding[] = []) { - return await this.knex.raw(query, params); + return await this.knex!.raw(query, params); } async migrate() { @@ -104,7 +105,7 @@ export default class DatabaseCore { let row = []; try { - const qb = this.knex(schemaName); + const qb = this.knex!(schemaName); if (name !== undefined) { qb.where({ name }); } @@ -197,7 +198,7 @@ export default class DatabaseCore { groupBy, orderBy = 'creation', order = 'desc', - }: GetAllOptions = {}): Promise { + }: GetAllOptions): Promise { const schema = this.schemaMap[schemaName]; if (!fields) { fields = ['name', ...(schema.keywordFields ?? [])]; @@ -207,7 +208,7 @@ export default class DatabaseCore { fields = [fields]; } - return (await this.#getQueryBuilder(schemaName, fields, filters, { + return (await this.#getQueryBuilder(schemaName, fields, filters ?? {}, { offset: start, limit, groupBy, @@ -226,7 +227,7 @@ export default class DatabaseCore { return fieldname; }); - let builder = this.knex('SingleValue'); + let builder = this.knex!('SingleValue'); builder = builder.where(fieldnames[0]); fieldnames.slice(1).forEach(({ fieldname, parent }) => { @@ -252,7 +253,7 @@ export default class DatabaseCore { } async rename(schemaName: string, oldName: string, newName: string) { - await this.knex(schemaName) + await this.knex!(schemaName) .update({ name: newName }) .where('name', oldName); await this.commit(); @@ -282,15 +283,15 @@ export default class DatabaseCore { } async #tableExists(schemaName: string) { - return await this.knex.schema.hasTable(schemaName); + return await this.knex!.schema.hasTable(schemaName); } async #singleExists(singleSchemaName: string) { - const res = await this.knex('SingleValue') + const res = await this.knex!('SingleValue') .count('parent as count') .where('parent', singleSchemaName) .first(); - return res.count > 0; + return (res?.count ?? 0) > 0; } async #removeColumns(schemaName: string, targetColumns: string[]) { @@ -298,7 +299,7 @@ export default class DatabaseCore { } async #deleteSingleValues(singleSchemaName: string) { - return await this.knex('SingleValue') + return await this.knex!('SingleValue') .where('parent', singleSchemaName) .delete(); } @@ -325,9 +326,9 @@ export default class DatabaseCore { // Alter table hacx for sqlite in case of schema change. const tempName = `__${schemaName}`; - await this.knex.schema.dropTableIfExists(tempName); + await this.knex!.schema.dropTableIfExists(tempName); - await this.knex.raw('PRAGMA foreign_keys=OFF'); + await this.knex!.raw('PRAGMA foreign_keys=OFF'); await this.#createTable(schemaName, tempName); if (tableRows.length > 200) { @@ -337,27 +338,29 @@ export default class DatabaseCore { if (rowSlice.length === 0) { break; } - await this.knex.batchInsert(tempName, rowSlice); + await this.knex!.batchInsert(tempName, rowSlice); } } else { - await this.knex.batchInsert(tempName, tableRows); + await this.knex!.batchInsert(tempName, tableRows); } - await this.knex.schema.dropTable(schemaName); - await this.knex.schema.renameTable(tempName, schemaName); - await this.knex.raw('PRAGMA foreign_keys=ON'); + await this.knex!.schema.dropTable(schemaName); + await this.knex!.schema.renameTable(tempName, schemaName); + await this.knex!.raw('PRAGMA foreign_keys=ON'); } async #getTableColumns(schemaName: string): Promise { - const info = await this.raw(`PRAGMA table_info(${schemaName})`); - return info.map((d) => d.name); + const info: FieldValueMap[] = await this.raw( + `PRAGMA table_info(${schemaName})` + ); + return info.map((d) => d.name as string); } async #getForeignKeys(schemaName: string): Promise { - const foreignKeyList = await this.raw( + const foreignKeyList: FieldValueMap[] = await this.raw( `PRAGMA foreign_key_list(${schemaName})` ); - return foreignKeyList.map((d) => d.from); + return foreignKeyList.map((d) => d.from as string); } #getQueryBuilder( @@ -366,7 +369,7 @@ export default class DatabaseCore { filters: QueryFilter, options: GetQueryBuilderOptions ): Knex.QueryBuilder { - const builder = this.knex.select(fields).from(schemaName); + const builder = this.knex!.select(fields).from(schemaName); this.#applyFiltersToBuilder(builder, filters); @@ -432,9 +435,9 @@ export default class DatabaseCore { filtersArray.map((filter) => { const [field, operator, comparisonValue] = filter; if (operator === '=') { - builder.where(field, comparisonValue); + builder.where(field as string, comparisonValue); } else { - builder.where(field, operator, comparisonValue); + builder.where(field as string, operator as string, comparisonValue); } }); } @@ -445,10 +448,8 @@ export default class DatabaseCore { const diff: ColumnDiff = { added: [], removed: [] }; for (const field of validFields) { - if ( - !tableColumns.includes(field.fieldname) && - this.typeMap[field.fieldtype] - ) { + const hasDbType = this.typeMap.hasOwnProperty(field.fieldtype); + if (!tableColumns.includes(field.fieldname) && hasDbType) { diff.added.push(field); } } @@ -463,7 +464,7 @@ export default class DatabaseCore { return diff; } - async #getNewForeignKeys(schemaName): Promise { + async #getNewForeignKeys(schemaName: string): Promise { const foreignKeys = await this.#getForeignKeys(schemaName); const newForeignKeys: Field[] = []; const schema = this.schemaMap[schemaName]; @@ -490,7 +491,9 @@ export default class DatabaseCore { return; } - const column = table[columnType](field.fieldname); + const column = table[columnType]( + field.fieldname + ) as Knex.SqlLiteColumnBuilder; // primary key if (field.fieldname === 'name') { @@ -525,23 +528,21 @@ export default class DatabaseCore { const diff: ColumnDiff = await this.#getColumnDiff(schemaName); const newForeignKeys: Field[] = await this.#getNewForeignKeys(schemaName); - return this.knex.schema - .table(schemaName, (table) => { - if (diff.added.length) { - for (const field of diff.added) { - this.#buildColumnForTable(table, field); - } + return this.knex!.schema.table(schemaName, (table) => { + if (diff.added.length) { + for (const field of diff.added) { + this.#buildColumnForTable(table, field); } + } - if (diff.removed.length) { - this.#removeColumns(schemaName, diff.removed); - } - }) - .then(() => { - if (newForeignKeys.length) { - return this.#addForeignKeys(schemaName, newForeignKeys); - } - }); + if (diff.removed.length) { + this.#removeColumns(schemaName, diff.removed); + } + }).then(() => { + if (newForeignKeys.length) { + return this.#addForeignKeys(schemaName, newForeignKeys); + } + }); } async #createTable(schemaName: string, tableName?: string) { @@ -551,7 +552,7 @@ export default class DatabaseCore { } #runCreateTableQuery(schemaName: string, fields: Field[]) { - return this.knex.schema.createTable(schemaName, (table) => { + return this.knex!.schema.createTable(schemaName, (table) => { for (const field of fields) { this.#buildColumnForTable(table, field); } @@ -560,7 +561,7 @@ export default class DatabaseCore { async #getNonExtantSingleValues(singleSchemaName: string) { const existingFields = ( - await this.knex('SingleValue') + await this.knex!('SingleValue') .where({ parent: singleSchemaName }) .select('fieldname') ).map(({ fieldname }) => fieldname); @@ -577,11 +578,11 @@ export default class DatabaseCore { } async #deleteOne(schemaName: string, name: string) { - return this.knex(schemaName).where('name', name).delete(); + return this.knex!(schemaName).where('name', name).delete(); } #deleteChildren(schemaName: string, parentName: string) { - return this.knex(schemaName).where('parent', parentName).delete(); + return this.knex!(schemaName).where('parent', parentName).delete(); } #runDeleteOtherChildren( @@ -590,7 +591,7 @@ export default class DatabaseCore { added: string[] ) { // delete other children - return this.knex(field.target) + return this.knex!(field.target) .where('parent', parentName) .andWhere('name', 'not in', added) .delete(); @@ -623,21 +624,21 @@ export default class DatabaseCore { try { // copy from old to new table - await this.knex(tempName).insert(this.knex.select().from(schemaName)); + await this.knex!(tempName).insert(this.knex!.select().from(schemaName)); } catch (err) { await this.raw('ROLLBACK'); await this.raw('PRAGMA foreign_keys=ON'); - const rows = await this.knex.select().from(schemaName); + const rows = await this.knex!.select().from(schemaName); await this.prestigeTheTable(schemaName, rows); return; } // drop old table - await this.knex.schema.dropTable(schemaName); + await this.knex!.schema.dropTable(schemaName); // rename new table - await this.knex.schema.renameTable(tempName, schemaName); + await this.knex!.schema.renameTable(tempName, schemaName); await this.raw('COMMIT'); await this.raw('PRAGMA foreign_keys=ON'); @@ -663,8 +664,7 @@ export default class DatabaseCore { name: string, fields: string | string[] = '*' ) { - const fieldValueMap: FieldValueMap = await this.knex - .select(fields) + const fieldValueMap: FieldValueMap = await this.knex!.select(fields) .from(schemaName) .where('name', name) .first(); @@ -694,7 +694,7 @@ export default class DatabaseCore { fieldValueMap.name = getRandomString(); } - const validMap = {}; + const validMap: FieldValueMap = {}; for (const { fieldname, fieldtype } of fields) { if (fieldtype === FieldTypeEnum.Table) { continue; @@ -703,7 +703,7 @@ export default class DatabaseCore { validMap[fieldname] = fieldValueMap[fieldname]; } - return this.knex(schemaName).insert(fieldValueMap); + return this.knex!(schemaName).insert(fieldValueMap); } async #updateSingleValues( @@ -728,7 +728,7 @@ export default class DatabaseCore { fieldname: string, value: RawValue ) { - return await this.knex('SingleValue') + return await this.knex!('SingleValue') .where({ parent: singleSchemaName, fieldname, @@ -758,7 +758,7 @@ export default class DatabaseCore { } return acc; - }, {}); + }, {} as FieldValueMap); await this.#updateSingleValues(schemaName, defaultValues); } @@ -767,7 +767,7 @@ export default class DatabaseCore { async #updateNonExtantSingleValues(schemaName: string) { const singleValues = await this.#getNonExtantSingleValues(schemaName); for (const sv of singleValues) { - await this.#updateSingleValue(schemaName, sv.fieldname, sv.value); + await this.#updateSingleValue(schemaName, sv.fieldname, sv.value!); } } @@ -775,7 +775,7 @@ export default class DatabaseCore { const updateMap = { ...fieldValueMap }; delete updateMap.name; - return await this.knex(schemaName) + return await this.knex!(schemaName) .where('name', fieldValueMap.name as string) .update(updateMap); } diff --git a/backend/database/manager.ts b/backend/database/manager.ts index ce5ed3af..51d142b8 100644 --- a/backend/database/manager.ts +++ b/backend/database/manager.ts @@ -6,7 +6,7 @@ import { runPatches } from './runPatch'; import { Patch } from './types'; export class DatabaseManager { - db: DatabaseCore; + db?: DatabaseCore; constructor() {} async createNewDatabase(dbPath: string, countryCode: string) { @@ -26,6 +26,10 @@ export class DatabaseManager { } async migrate() { + if (this.db === undefined) { + return; + } + const patchesToExecute = await this.#getPatchesToExecute(); const preMigrationPatches = patchesToExecute.filter( (p) => p.patch.beforeMigrate @@ -40,21 +44,26 @@ export class DatabaseManager { } async #getPatchesToExecute(): Promise { - const query: { name: string }[] = await this.db - .knex('PatchRun') - .select('name'); + if (this.db === undefined) { + return []; + } + + const query: { name: string }[] = await this.db.knex!('PatchRun').select( + 'name' + ); const executedPatches = query.map((q) => q.name); return patches.filter((p) => !executedPatches.includes(p.name)); } async #getCountryCode(): Promise { - if (!this.db) { + if (this.db === undefined) { return undefined; } - const query = await this.db - .knex('SingleValue') - .where({ fieldname: 'countryCode', parent: 'SystemSettings' }); + const query = await this.db.knex!('SingleValue').where({ + fieldname: 'countryCode', + parent: 'SystemSettings', + }); if (query.length > 0) { return query[0].countryCode as string; @@ -67,7 +76,7 @@ export class DatabaseManager { try { fs.unlink(dbPath); } catch (err) { - if (err.code === 'ENOENT') { + if ((err as NodeJS.ErrnoException).code === 'ENOENT') { return; } diff --git a/backend/database/runPatch.ts b/backend/database/runPatch.ts index 39cf5ece..8c521700 100644 --- a/backend/database/runPatch.ts +++ b/backend/database/runPatch.ts @@ -27,5 +27,5 @@ async function runPatch(patch: Patch, dm: DatabaseManager): Promise { async function makeEntry(patchName: string, dm: DatabaseManager) { const defaultFieldValueMap = getDefaultMetaFieldValueMap() as FieldValueMap; defaultFieldValueMap.name = patchName; - await dm.db.insert('PatchRun', defaultFieldValueMap); + await dm.db!.insert('PatchRun', defaultFieldValueMap); } diff --git a/backend/database/types.ts b/backend/database/types.ts index 45547423..11f50ee0 100644 --- a/backend/database/types.ts +++ b/backend/database/types.ts @@ -1,19 +1,20 @@ import { Field, RawValue } from '../../schemas/types'; +import { DatabaseManager } from './manager'; export type QueryFilter = Record; export interface GetQueryBuilderOptions { - offset: number; - limit: number; - groupBy: string; - orderBy: string; - order: 'desc' | 'asc'; + offset?: number; + limit?: number; + groupBy?: string; + orderBy?: string; + order?: 'desc' | 'asc'; } export interface GetAllOptions { - schemaName?: string; + schemaName: string; fields?: string[]; - filters?: Record; + filters?: QueryFilter; start?: number; limit?: number; groupBy?: string; @@ -31,7 +32,17 @@ export interface Patch { name: string; version: string; patch: { - execute: (DatabaseManager) => Promise; + execute: (dm: DatabaseManager) => Promise; beforeMigrate?: boolean; }; } + +export type KnexColumnType = + | 'text' + | 'integer' + | 'float' + | 'boolean' + | 'date' + | 'datetime' + | 'time' + | 'binary'; diff --git a/schemas/helpers.ts b/schemas/helpers.ts index 65e7a570..93fce200 100644 --- a/schemas/helpers.ts +++ b/schemas/helpers.ts @@ -1,15 +1,15 @@ -export function getMapFromList( +export function getMapFromList( list: T[], - name: string = 'name' + name: K ): Record { const acc: Record = {}; for (const t of list) { - const key = t[name] as string | undefined; + const key = t[name]; if (key === undefined) { continue; } - acc[key] = t; + acc[String(key)] = t; } return acc; } diff --git a/schemas/index.ts b/schemas/index.ts index a71611cc..59f2a443 100644 --- a/schemas/index.ts +++ b/schemas/index.ts @@ -13,8 +13,8 @@ export function getSchemas(countryCode: string = '-'): SchemaMap { return schemaMap; } -function addMetaFields(schemaMap: SchemaMap): SchemaMap { - const metaSchemaMap = getMapFromList(metaSchemas); +export function addMetaFields(schemaMap: SchemaMap): SchemaMap { + const metaSchemaMap = getMapFromList(metaSchemas, 'name'); const base = metaSchemaMap.base; const tree = getCombined(metaSchemaMap.tree, base); @@ -29,15 +29,15 @@ function addMetaFields(schemaMap: SchemaMap): SchemaMap { } if (schema.isTree && schema.isSubmittable) { - schema.fields = [...schema.fields, ...submittableTree.fields]; + schema.fields = [...schema.fields, ...submittableTree.fields!]; } else if (schema.isTree) { - schema.fields = [...schema.fields, ...tree.fields]; + schema.fields = [...schema.fields, ...tree.fields!]; } else if (schema.isSubmittable) { - schema.fields = [...schema.fields, ...submittable.fields]; + schema.fields = [...schema.fields, ...submittable.fields!]; } else if (schema.isChild) { - schema.fields = [...schema.fields, ...child.fields]; + schema.fields = [...schema.fields, ...child.fields!]; } else { - schema.fields = [...schema.fields, ...base.fields]; + schema.fields = [...schema.fields, ...base.fields!]; } } @@ -45,18 +45,23 @@ function addMetaFields(schemaMap: SchemaMap): SchemaMap { } function getCoreSchemas(): SchemaMap { - const rawSchemaMap = getMapFromList(coreSchemas); + const rawSchemaMap = getMapFromList(coreSchemas, 'name'); const coreSchemaMap = getAbstractCombinedSchemas(rawSchemaMap); return cleanSchemas(coreSchemaMap); } function getAppSchemas(countryCode: string): SchemaMap { - const combinedSchemas = getRegionalCombinedSchemas(countryCode); + const appSchemaMap = getMapFromList(appSchemas, 'name'); + const regionalSchemaMap = getRegionalSchemaMap(countryCode); + const combinedSchemas = getRegionalCombinedSchemas( + appSchemaMap, + regionalSchemaMap + ); const schemaMap = getAbstractCombinedSchemas(combinedSchemas); return cleanSchemas(schemaMap); } -function cleanSchemas(schemaMap: SchemaMap): SchemaMap { +export function cleanSchemas(schemaMap: SchemaMap): SchemaMap { for (const name in schemaMap) { const schema = schemaMap[name]; if (schema.isAbstract && !schema.extends) { @@ -97,13 +102,13 @@ function getCombined( return combined; } -function getAbstractCombinedSchemas(schemas: SchemaStubMap): SchemaMap { +export function getAbstractCombinedSchemas(schemas: SchemaStubMap): SchemaMap { const abstractSchemaNames: string[] = Object.keys(schemas).filter( (n) => schemas[n].isAbstract ); const extendingSchemaNames: string[] = Object.keys(schemas).filter((n) => - abstractSchemaNames.includes(schemas[n].extends) + abstractSchemaNames.includes(schemas[n].extends ?? '') ); const completeSchemas: Schema[] = Object.keys(schemas) @@ -113,11 +118,11 @@ function getAbstractCombinedSchemas(schemas: SchemaStubMap): SchemaMap { ) .map((n) => schemas[n] as Schema); - const schemaMap = getMapFromList(completeSchemas) as SchemaMap; + const schemaMap = getMapFromList(completeSchemas, 'name') as SchemaMap; for (const name of extendingSchemaNames) { const extendingSchema = schemas[name] as Schema; - const abstractSchema = schemas[extendingSchema.extends] as SchemaStub; + const abstractSchema = schemas[extendingSchema.extends!] as SchemaStub; schemaMap[name] = getCombined(extendingSchema, abstractSchema) as Schema; } @@ -129,9 +134,10 @@ function getAbstractCombinedSchemas(schemas: SchemaStubMap): SchemaMap { return schemaMap; } -function getRegionalCombinedSchemas(countryCode: string): SchemaStubMap { - const regionalSchemaMap = getRegionalSchema(countryCode); - const appSchemaMap = getMapFromList(appSchemas); +export function getRegionalCombinedSchemas( + appSchemaMap: SchemaStubMap, + regionalSchemaMap: SchemaStubMap +): SchemaStubMap { const combined = { ...appSchemaMap }; for (const name in regionalSchemaMap) { @@ -148,7 +154,7 @@ function getRegionalCombinedSchemas(countryCode: string): SchemaStubMap { return combined; } -function getRegionalSchema(countryCode: string): SchemaStubMap { +function getRegionalSchemaMap(countryCode: string): SchemaStubMap { const countrySchemas = regionalSchemas[countryCode] as | SchemaStub[] | undefined; @@ -156,5 +162,5 @@ function getRegionalSchema(countryCode: string): SchemaStubMap { return {}; } - return getMapFromList(countrySchemas); + return getMapFromList(countrySchemas, 'name'); } diff --git a/schemas/regional/index.ts b/schemas/regional/index.ts index 3616cca1..de55493e 100644 --- a/schemas/regional/index.ts +++ b/schemas/regional/index.ts @@ -1,6 +1,7 @@ +import { SchemaStub } from 'schemas/types'; import IndianSchemas from './in'; /** * Regional Schemas are exported by country code. */ -export default { in: IndianSchemas }; +export default { in: IndianSchemas } as Record; diff --git a/tsconfig.json b/tsconfig.json index 560cf708..e06233d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, "sourceMap": true, "baseUrl": ".", "types": ["webpack-env"], @@ -23,8 +24,8 @@ "src/**/*.tsx", "src/**/*.vue", - "tests/**/*.ts", - "tests/**/*.tsx", + "schemas/**/*.ts", + "backend/**/*.ts", "frappe/**/*.ts", "models/**/*.ts",