From 7679731a9e1c3ca7d97b6b750a92bca36dc449d2 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:10:10 +0200 Subject: [PATCH 01/32] other(ts): copy parser types from bookbrainz-utils Since we also need them here, they will be deleted from there later. --- src/types/parser.ts | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/types/utils.ts | 3 ++ 2 files changed, 74 insertions(+) create mode 100644 src/types/parser.ts diff --git a/src/types/parser.ts b/src/types/parser.ts new file mode 100644 index 00000000..38922043 --- /dev/null +++ b/src/types/parser.ts @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 David Kellner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * Types for parsed entities, used for imports. + * TODO: Investigate whether these are also useful elsewhere, e.g. for form validation. + */ + +import {AliasT} from './aliases'; +import {EntityTypeString} from './entity'; +import {IdentifierT} from './identifiers'; +import {Insertable} from './utils'; + + +export type ParsedAlias = Insertable; + +export type ParsedIdentifier = Insertable; + +type ParsedBaseEntity = { + entityType: EntityTypeString; + alias: ParsedAlias[]; + annotation?: string; + disambiguation?: string; + identifiers: ParsedIdentifier[]; + metadata: { + identifiers?: ParsedIdentifier[]; + links: Array<{ + title: string; + url: string; + }>; + // TODO: find correct type in OL samples + originId?: object[]; + relationships: Array<{ + type: string; + value: string; + }>; + [custom: string]: any; + }; + source: string; + lastEdited?: string; + originId?: string; +}; + +export type ParsedAuthor = ParsedBaseEntity & { + beginDate?: string; + endDate?: string; + type?: 'Person'; +}; + +export type ParsedWork = ParsedBaseEntity; + +export type ParsedEntity = + | ParsedAuthor + | ParsedWork; diff --git a/src/types/utils.ts b/src/types/utils.ts index 37d49ff6..40f6a00f 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -18,3 +18,6 @@ /** Utility type to mark properties which are lazy-loaded by the ORM and might not always be present. */ export type LazyLoaded = Partial; + +/** Data models without ID which can be inserted into the ORM. */ +export type Insertable = Omit; From 9381eeef0e70d80232776eb027d1e4df5e85e7b1 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:12:11 +0200 Subject: [PATCH 02/32] chore(deps): update @metabrainz/bookshelf to v1.4.0 This version includes type definitions. --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9bdb2b43..b1df4e21 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ }, "homepage": "https://github.com/bookbrainz/bookbrainz-data-js", "dependencies": { - "@metabrainz/bookshelf": "^1.3.1", + "@metabrainz/bookshelf": "^1.4.0", "bookshelf-virtuals-plugin": "^1.0.0", "deep-diff": "^1.0.2", "immutable": "^3.8.2", diff --git a/yarn.lock b/yarn.lock index 8ae82d9f..bc5037ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1030,10 +1030,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@metabrainz/bookshelf@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@metabrainz/bookshelf/-/bookshelf-1.3.1.tgz#e8a1e607b1dc074ecfc43781aced87e9ea42e496" - integrity sha512-zlcMCXPrhddIfAyiD+XawtwRRn8cs3/f3ELYz3vTrRLm5dZKtWSpXPRn3uBgunejgV2Zc3/ljRkXQEW1EbpYXg== +"@metabrainz/bookshelf@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@metabrainz/bookshelf/-/bookshelf-1.4.0.tgz#83b44ccebbf8421fdffa79ecbc103b4e43da3881" + integrity sha512-m1znuBtL4fX6tQctFFVvl6WYTg3gDUuwqM4B7d4VaKepI4/L1Qoar65VD2zto+oknPsFBmKrBLOJAD614nMF7Q== dependencies: bluebird "^3.7.2" create-error "~0.3.1" From 98129913e72a1d187f978cb4ee61d0be3d6f6e17 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:45:36 +0200 Subject: [PATCH 03/32] refactor(types): introduce separate types for lazy-loaded properties Also use consistent names for inserted and fetched models: * ModelT * ModelWithIdT * LazyLoadedModelT --- src/types/aliases.ts | 22 ++++++++++++++-------- src/types/entity.ts | 14 ++++++++------ src/types/identifiers.ts | 22 ++++++++++++++-------- src/types/language.ts | 6 +++++- src/types/parser.ts | 10 ++++------ src/types/utils.ts | 6 ++++-- 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/types/aliases.ts b/src/types/aliases.ts index 873d5673..a21fc55b 100644 --- a/src/types/aliases.ts +++ b/src/types/aliases.ts @@ -16,23 +16,29 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import {LanguageT} from './language'; -import {LazyLoaded} from './utils'; +import {LazyLoaded, WithId} from './utils'; +import {LanguageWithIdT} from './language'; export type AliasT = { - id: number, name: string, sortName: string, languageId: number | null, primary: boolean, -} & LazyLoaded<{ - language: LanguageT, +}; + +export type AliasWithIdT = WithId; + +export type LazyLoadedAliasT = AliasWithIdT & LazyLoaded<{ + language: LanguageWithIdT, }>; export type AliasSetT = { - id: number, defaultAliasId: number | null, -} & LazyLoaded<{ - aliases: Array, +}; + +export type AliasSetWithIdT = WithId; + +export type LazyLoadedAliasSetT = AliasSetWithIdT & LazyLoaded<{ + aliases: Array, }>; diff --git a/src/types/entity.ts b/src/types/entity.ts index 3eebe6ef..d874c9e4 100644 --- a/src/types/entity.ts +++ b/src/types/entity.ts @@ -16,9 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import {AliasSetT, AliasT} from './aliases'; -import {IdentifierSetT} from './identifiers'; +import {LazyLoadedAliasSetT, LazyLoadedAliasT} from './aliases'; import {LazyLoaded} from './utils'; +import {LazyLoadedIdentifierSetT} from './identifiers'; export type EntityTypeString = @@ -32,8 +32,10 @@ export type EntityTypeString = // TODO: incomplete export type EntityT = { type: EntityTypeString, -} & LazyLoaded<{ - aliasSet: AliasSetT, - defaultAlias: AliasT, - identifierSet: IdentifierSetT, +}; + +export type LazyLoadedEntityT = EntityT & LazyLoaded<{ + aliasSet: LazyLoadedAliasSetT, + defaultAlias: LazyLoadedAliasT, + identifierSet: LazyLoadedIdentifierSetT, }>; diff --git a/src/types/identifiers.ts b/src/types/identifiers.ts index ab5a6d49..d99fb0a5 100644 --- a/src/types/identifiers.ts +++ b/src/types/identifiers.ts @@ -16,12 +16,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import {LazyLoaded, WithId} from './utils'; import {EntityTypeString} from './entity'; -import {LazyLoaded} from './utils'; export type IdentifierTypeT = { - id: number, label: string, description: string, detectionRegex: string | null, @@ -33,16 +32,23 @@ export type IdentifierTypeT = { deprecated: boolean, }; +export type IdentifierTypeWithIdT = WithId; + export type IdentifierT = { - id: number, typeId: number, value: string, -} & LazyLoaded<{ - type: IdentifierTypeT, +}; + +export type IdentifierWithIdT = WithId; + +export type LazyLoadedIdentifierT = IdentifierWithIdT & LazyLoaded<{ + type: IdentifierTypeWithIdT, }>; -export type IdentifierSetT = { +export type IdentifierSetWithIdT = { id: number, -} & LazyLoaded<{ - identifiers: Array, +}; + +export type LazyLoadedIdentifierSetT = IdentifierSetWithIdT & LazyLoaded<{ + identifiers: Array, }>; diff --git a/src/types/language.ts b/src/types/language.ts index 4319a547..c8e89dc0 100644 --- a/src/types/language.ts +++ b/src/types/language.ts @@ -16,8 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import {WithId} from './utils'; + + export type LanguageT = { - id: number, isoCode2t: string | null, isoCode2b: string | null, isoCode1: string | null, @@ -25,3 +27,5 @@ export type LanguageT = { frequency: number, isoCode3: string | null, }; + +export type LanguageWithIdT = WithId; diff --git a/src/types/parser.ts b/src/types/parser.ts index 38922043..260b9b11 100644 --- a/src/types/parser.ts +++ b/src/types/parser.ts @@ -24,23 +24,21 @@ import {AliasT} from './aliases'; import {EntityTypeString} from './entity'; import {IdentifierT} from './identifiers'; -import {Insertable} from './utils'; -export type ParsedAlias = Insertable; +}; -export type ParsedIdentifier = Insertable; type ParsedBaseEntity = { entityType: EntityTypeString; alias: ParsedAlias[]; annotation?: string; disambiguation?: string; - identifiers: ParsedIdentifier[]; + identifiers: IdentifierT[]; metadata: { - identifiers?: ParsedIdentifier[]; + identifiers?: IdentifierT[]; links: Array<{ title: string; url: string; diff --git a/src/types/utils.ts b/src/types/utils.ts index 40f6a00f..17e52524 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -19,5 +19,7 @@ /** Utility type to mark properties which are lazy-loaded by the ORM and might not always be present. */ export type LazyLoaded = Partial; -/** Data models without ID which can be inserted into the ORM. */ -export type Insertable = Omit; +/** Utility which adds a numeric `id` property to a given ORM model (for select statements). */ +export type WithId> = T & { + id: number, +}; From 242916dd18586b04182b0f795685f4406f75e87d Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:31:09 +0200 Subject: [PATCH 04/32] refactor(types): replace or move old types for aliases/identifiers --- src/func/alias.ts | 17 +++++++---------- src/func/create-entity.ts | 11 +++++------ src/func/identifier.ts | 9 +++++---- src/func/types.ts | 30 +++++++----------------------- src/types/aliases.ts | 9 +++++++++ 5 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/func/alias.ts b/src/func/alias.ts index ac657126..c83429e2 100644 --- a/src/func/alias.ts +++ b/src/func/alias.ts @@ -18,11 +18,7 @@ */ import * as _ from 'lodash'; -import type { - FormAliasT as Alias, - FormAliasWithDefaultT as AliasWithDefault, - Transaction -} from './types'; +import type {AliasWithDefaultT, FormAliasT} from '../types/aliases'; import { createNewSetWithItems, getAddedItems, @@ -30,15 +26,16 @@ import { getUnchangedItems } from './set'; import type {EntityTypeString} from '../types/entity'; +import type {Transaction} from './types'; import {snakeToCamel} from '../util'; export async function updateAliasSet( orm: any, transacting: Transaction, oldSet: any, oldDefaultAliasId: number | null | undefined, - newSetItemsWithDefault: Array + newSetItemsWithDefault: Array ) { - function comparisonFunc(obj: Alias, other: Alias) { + function comparisonFunc(obj: FormAliasT, other: FormAliasT) { return ( obj.name === other.name && obj.sortName === other.sortName && @@ -49,10 +46,10 @@ export async function updateAliasSet( const {AliasSet} = orm; - const newSetItems: Array = + const newSetItems: Array = newSetItemsWithDefault.map((item) => _.omit(item, 'default')); - const oldSetItems: Array = + const oldSetItems: Array = oldSet ? oldSet.related('aliases').toJSON() : []; if (_.isEmpty(oldSetItems) && _.isEmpty(newSetItems)) { @@ -161,7 +158,7 @@ export async function getBBIDsWithMatchingAlias( const aliasIds = _.map( await getAliasIds(transacting, name, caseSensitive), 'id' - ) as Array; + ) as Array; const aliasSetIds = _.map( await transacting.distinct('set_id') diff --git a/src/func/create-entity.ts b/src/func/create-entity.ts index 8afcb8f8..581d3d67 100644 --- a/src/func/create-entity.ts +++ b/src/func/create-entity.ts @@ -20,13 +20,12 @@ */ import * as _ from 'lodash'; -import type { - FormAliasWithDefaultT as AliasWithDefault, FormIdentifierT as Identifier, - Transaction -} from './types'; import { getAdditionalEntityProps, getEntityModelByType, getEntitySetMetadataByType } from './entity'; +import type {AliasWithDefaultT} from '../types/aliases'; +import type {IdentifierT} from '../types/identifiers'; +import type {Transaction} from './types'; import {createNote} from './note'; import {incrementEditorEditCountById} from './editor'; import {updateAliasSet} from './alias'; @@ -37,10 +36,10 @@ import {updateIdentifierSet} from './identifier'; interface EntityDataType { - aliases: Array, + aliases: Array, annotation: string, disambiguation: string, - identifiers: Array, + identifiers: Array, note: string, type: string } diff --git a/src/func/identifier.ts b/src/func/identifier.ts index b58348b6..5d3480dd 100644 --- a/src/func/identifier.ts +++ b/src/func/identifier.ts @@ -17,23 +17,24 @@ */ import * as _ from 'lodash'; -import type {FormIdentifierT as Identifier, Transaction} from './types'; import { createNewSetWithItems, getAddedItems, getRemovedItems, getUnchangedItems } from './set'; +import type {IdentifierT} from '../types/identifiers'; +import type {Transaction} from './types'; export function updateIdentifierSet( orm: any, transacting: Transaction, oldSet: any, - newSetItems: Array + newSetItems: Array ): Promise { - function comparisonFunc(obj: Identifier, other: Identifier) { + function comparisonFunc(obj: IdentifierT, other: IdentifierT) { return obj.value === other.value && obj.typeId === other.typeId; } const {IdentifierSet} = orm; - const oldSetItems: Array = + const oldSetItems: Array = oldSet ? oldSet.related('identifiers').toJSON() : []; const addedItems = diff --git a/src/func/types.ts b/src/func/types.ts index 1581a6ba..c762e3fa 100644 --- a/src/func/types.ts +++ b/src/func/types.ts @@ -17,19 +17,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import type {FormAliasT} from '../types/aliases'; +import type {IdentifierT} from '../types/identifiers'; import type {Knex} from 'knex'; export type Transaction = Knex.Transaction; -export interface FormAliasT { - id: number, - name: string, - sortName: string, - languageId: number, - primary: boolean -} - export interface FormRelationshipAttributesT { id: number, attributeType: number @@ -38,20 +32,6 @@ export interface FormRelationshipAttributesT { } } -export interface FormAliasWithDefaultT { - id: number, - name: string, - sortName: string, - languageId: number, - primary: boolean, - default: boolean -} - -export interface FormIdentifierT { - value: string, - typeId: number -} - export interface FormRelationshipT { attributeSetId: number, id: number, @@ -73,8 +53,12 @@ export interface FormReleaseEventT { areaId?: any } +// TODO: Actually all of these types would need an (optional) 'id' or 'bbid' attribute for their intended use +// (can be missing for new items), but not all of them are defined as such above! +// TODO: Although FormRelationshipAttributesT is missing from this type, createNewRelationshipAttributeSetWithItems +// (which requires SetItemT inputs) causes no type errors? export type SetItemT = - FormAliasT | FormIdentifierT | FormLanguageT | FormRelationshipT | + FormAliasT | IdentifierT | FormLanguageT | FormRelationshipT | FormPublisherT | FormReleaseEventT; export interface AuthorCreditNameT { diff --git a/src/types/aliases.ts b/src/types/aliases.ts index a21fc55b..4353ab71 100644 --- a/src/types/aliases.ts +++ b/src/types/aliases.ts @@ -33,6 +33,15 @@ export type LazyLoadedAliasT = AliasWithIdT & LazyLoaded<{ language: LanguageWithIdT, }>; +// TODO: find a better name +export type FormAliasT = AliasT & { + id: number; +}; + +export type AliasWithDefaultT = FormAliasT & { + default: boolean, +}; + export type AliasSetT = { defaultAliasId: number | null, }; From 2d4298024b87c0f1f5888667827727156a5cea5e Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:35:53 +0200 Subject: [PATCH 05/32] fix(types): make id and default flag of aliases optional An alias only needs an id during updates if it is not a new one. As long as one item of an alias set is marked as default, the others do not need this attribute. --- src/types/aliases.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/aliases.ts b/src/types/aliases.ts index 4353ab71..ed9aed0d 100644 --- a/src/types/aliases.ts +++ b/src/types/aliases.ts @@ -35,11 +35,11 @@ export type LazyLoadedAliasT = AliasWithIdT & LazyLoaded<{ // TODO: find a better name export type FormAliasT = AliasT & { - id: number; + id?: number; }; export type AliasWithDefaultT = FormAliasT & { - default: boolean, + default?: boolean, }; export type AliasSetT = { From cc35dcb8a5b6751608689849e6e7f61c5aa0b575 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 18:24:06 +0200 Subject: [PATCH 06/32] refactor(types): replace redundant old type definition of parsed alias --- src/types/parser.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/types/parser.ts b/src/types/parser.ts index 260b9b11..662a3119 100644 --- a/src/types/parser.ts +++ b/src/types/parser.ts @@ -21,19 +21,15 @@ * TODO: Investigate whether these are also useful elsewhere, e.g. for form validation. */ -import {AliasT} from './aliases'; +import {AliasWithDefaultT} from './aliases'; import {EntityTypeString} from './entity'; import {IdentifierT} from './identifiers'; -export type ParsedAlias = AliasT & { - default?: boolean; -}; - - type ParsedBaseEntity = { entityType: EntityTypeString; - alias: ParsedAlias[]; + // TODO: rename array property to `aliases`, also for consistency with e.g. `EntityDataType` + alias: AliasWithDefaultT[]; annotation?: string; disambiguation?: string; identifiers: IdentifierT[]; From 6b9235ac0c26973712ec5374b8c5ec2c197d8a7e Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:47:47 +0200 Subject: [PATCH 07/32] other(ts): export a type for the ORM Currently all its properties except for `bookshelf` have an inferred type of `any`, but that's a start at least. The situation will improve as soon as the Bookshelf models have types. --- src/index.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1ac9282c..a9a8330d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,8 @@ import * as func from './func'; import * as util from './util'; // eslint-disable-line import/no-namespace +import {type Knex, knex} from 'knex'; + import Bookshelf from '@metabrainz/bookshelf'; import achievementType from './models/achievementType'; import achievementUnlock from './models/achievementUnlock'; @@ -59,7 +61,6 @@ import gender from './models/gender'; import identifier from './models/identifier'; import identifierSet from './models/identifierSet'; import identifierType from './models/identifierType'; -import knex from 'knex'; import language from './models/language'; import languageSet from './models/languageSet'; import note from './models/note'; @@ -100,10 +101,10 @@ import workType from './models/workType'; /** * Initialize the database connection and models. - * @param {Object} config - A knex.js configuration object. + * @param {Knex.Config} config - A knex.js configuration object. * @returns {Object} All data models. */ -export default function init(config) { +export default function init(config: Knex.Config) { const bookshelf = Bookshelf(knex(config)); bookshelf.plugin('bookshelf-virtuals-plugin'); @@ -198,4 +199,6 @@ export default function init(config) { }; } +export type ORM = ReturnType; + export {func}; From 3134f419504bf7817ec1d1b5cb12f529bd403fe2 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:00:32 +0200 Subject: [PATCH 08/32] refactor(types): define type EntityTypeString based on a constant array --- src/types/entity.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/types/entity.ts b/src/types/entity.ts index d874c9e4..30dc7691 100644 --- a/src/types/entity.ts +++ b/src/types/entity.ts @@ -21,13 +21,16 @@ import {LazyLoaded} from './utils'; import {LazyLoadedIdentifierSetT} from './identifiers'; -export type EntityTypeString = - | 'Author' - | 'Edition' - | 'EditionGroup' - | 'Publisher' - | 'Series' - | 'Work'; +export const ENTITY_TYPES = [ + 'Author', + 'Edition', + 'EditionGroup', + 'Publisher', + 'Series', + 'Work' +] as const; + +export type EntityTypeString = typeof ENTITY_TYPES[number]; // TODO: incomplete export type EntityT = { From fdcf3335d6bc4bebad6244fec10d4084e30dbabc Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:27:10 +0200 Subject: [PATCH 09/32] refactor(func): make use of ENTITY_TYPES and EntityTypeString --- src/func/create-entity.ts | 5 +- src/func/entity.ts | 115 +++++++++--------- .../{create-import.js => create-import.ts} | 12 +- src/func/imports/recent-imports.ts | 26 ++-- 4 files changed, 75 insertions(+), 83 deletions(-) rename src/func/imports/{create-import.js => create-import.ts} (93%) diff --git a/src/func/create-entity.ts b/src/func/create-entity.ts index 581d3d67..7e7609f4 100644 --- a/src/func/create-entity.ts +++ b/src/func/create-entity.ts @@ -24,6 +24,7 @@ import { getAdditionalEntityProps, getEntityModelByType, getEntitySetMetadataByType } from './entity'; import type {AliasWithDefaultT} from '../types/aliases'; +import type {EntityTypeString} from '../types/entity'; import type {IdentifierT} from '../types/identifiers'; import type {Transaction} from './types'; import {createNote} from './note'; @@ -41,7 +42,7 @@ interface EntityDataType { disambiguation: string, identifiers: Array, note: string, - type: string + type: EntityTypeString } interface ExtraEntityDataType extends EntityDataType { @@ -53,7 +54,7 @@ interface CreateEntityPropsType { transacting: Transaction, editorId: string, entityData: ExtraEntityDataType, - entityType: string + entityType: EntityTypeString } export async function createEntity({ diff --git a/src/func/entity.ts b/src/func/entity.ts index cd193fba..2fd64c1f 100644 --- a/src/func/entity.ts +++ b/src/func/entity.ts @@ -19,77 +19,72 @@ */ import * as _ from 'lodash'; +import type {AliasWithIdT} from '../types/aliases'; import type {EntityTypeString} from '../types/entity'; +import type {ORM} from '..'; +import type {Transaction} from './types'; import {parseDate} from '../util'; -export const AUTHOR: EntityTypeString = 'Author'; -export const EDITION: EntityTypeString = 'Edition'; -export const EDITION_GROUP: EntityTypeString = 'EditionGroup'; -export const PUBLISHER: EntityTypeString = 'Publisher'; -export const SERIES: EntityTypeString = 'Series'; -export const WORK: EntityTypeString = 'Work'; - -export const entityTypes = { - AUTHOR, EDITION, EDITION_GROUP, PUBLISHER, SERIES, WORK -}; - /** * @param {Object} entityData - Object holding all data related to an entity * @param {string} entityType - The type of the entity * @returns {Object} - Returns all the additional entity specific data */ export function getAdditionalEntityProps( - entityData: Record, entityType: string + // TODO: `entityData` should have the type ParsedEntity later which is currently causing lots of type issues. + entityData: Record, entityType: EntityTypeString ) { - if (entityType === entityTypes.AUTHOR) { - const {typeId, genderId, beginAreaId, beginDate, endDate, - ended, endAreaId} = entityData; - - const [beginYear, beginMonth, beginDay] = parseDate(beginDate); - const [endYear, endMonth, endDay] = parseDate(endDate); - return { - beginAreaId, beginDate, beginDay, beginMonth, beginYear, - endAreaId, endDate, endDay, endMonth, endYear, - ended, genderId, typeId - }; - } - - if (entityType === entityTypes.EDITION) { - return _.pick(entityData, [ - 'editionGroupBbid', 'width', 'height', 'depth', 'weight', - 'pages', 'formatId', 'statusId' - ]); + switch (entityType) { + case 'Author': { + const {typeId, genderId, beginAreaId, beginDate, endDate, + ended, endAreaId} = entityData; + + const [beginYear, beginMonth, beginDay] = parseDate(beginDate); + const [endYear, endMonth, endDay] = parseDate(endDate); + return { + beginAreaId, beginDate, beginDay, beginMonth, beginYear, + endAreaId, endDate, endDay, endMonth, endYear, + ended, genderId, typeId + }; + } + + case 'Edition': + return _.pick(entityData, [ + 'editionGroupBbid', 'width', 'height', 'depth', 'weight', + 'pages', 'formatId', 'statusId' + ]); + + case 'Publisher': { + const {typeId, areaId, beginDate, endDate, ended} = entityData; + + const [beginYear, beginMonth, beginDay] = parseDate(beginDate); + const [endYear, endMonth, endDay] = parseDate(endDate); + + return { + areaId, beginDate, beginDay, beginMonth, beginYear, + endDate, endDay, endMonth, endYear, ended, typeId + }; + } + + case 'EditionGroup': + case 'Work': + return _.pick(entityData, ['typeId']); + + case 'Series': + return _.pick(entityData, ['entityType', 'orderingTypeId']); + + default: + return null; } - - if (entityType === entityTypes.PUBLISHER) { - const {typeId, areaId, beginDate, endDate, ended} = entityData; - - const [beginYear, beginMonth, beginDay] = parseDate(beginDate); - const [endYear, endMonth, endDay] = parseDate(endDate); - - return {areaId, beginDate, beginDay, beginMonth, beginYear, - endDate, endDay, endMonth, endYear, ended, typeId}; - } - - if (entityType === entityTypes.EDITION_GROUP || - entityType === entityTypes.WORK) { - return _.pick(entityData, ['typeId']); - } - - if (entityType === entityTypes.SERIES) { - return _.pick(entityData, ['entityType', 'orderingTypeId']); - } - - return null; } /** * @param {string} entityType - Entity type string * @returns {Object} - Returns entitySetMetadata (derivedSets) */ -export function getEntitySetMetadataByType(entityType: string): Array> { - if (entityType === EDITION) { +export function getEntitySetMetadataByType(entityType: EntityTypeString): Array> { + if (entityType === 'Edition') { return [ { entityIdField: 'languageSetId', @@ -115,7 +110,7 @@ export function getEntitySetMetadataByType(entityType: string): Array): any { +export function getEntityModels(orm: ORM) { const {Author, Edition, EditionGroup, Publisher, Series, Work} = orm; return { Author, @@ -157,7 +152,7 @@ export function getEntityModels(orm: Record): any { * @returns {object} - Bookshelf model object with the type specified in the * single param */ -export function getEntityModelByType(orm: Record, type: string): any { +export function getEntityModelByType(orm: ORM, type: EntityTypeString) { const entityModels = getEntityModels(orm); if (!entityModels[type]) { @@ -175,7 +170,7 @@ export function getEntityModelByType(orm: Record, type: string) * @param {any} transacting - Optional ORM transaction object * @returns {string} The final bbid to redirect to */ -export async function recursivelyGetRedirectBBID(orm: any, bbid: string, transacting?) { +export async function recursivelyGetRedirectBBID(orm: ORM, bbid: string, transacting?: Transaction) { const redirectSQLQuery = `SELECT target_bbid FROM bookbrainz.entity_redirect WHERE source_bbid = '${bbid}'`; const redirectQueryResults = await (transacting || orm.bookshelf.knex).raw(redirectSQLQuery); if (redirectQueryResults.rows && redirectQueryResults.rows.length) { @@ -194,7 +189,7 @@ export async function recursivelyGetRedirectBBID(orm: any, bbid: string, transac * @returns {Promise} A Promise that resolves to the entity in JSON format */ export async function getEntity( - orm: Record, entityType: string, bbid: string, relations: Array = [] + orm: ORM, entityType: EntityTypeString, bbid: string, relations: string[] = [] ): Promise> { // if bbid appears in entity_redirect table, use that bbid instead // Do a recursive search in case the redirected bbid also redirects, etc. @@ -217,7 +212,9 @@ export async function getEntity( * @returns {Promise} The returned Promise returns the entity's * parent default alias */ -export async function getEntityParentAlias(orm, entityType, bbid) { +export async function getEntityParentAlias( + orm: ORM, entityType: EntityTypeString, bbid: string +): Promise { const rawSql = ` SELECT alias.name, alias.sort_name, diff --git a/src/func/imports/create-import.js b/src/func/imports/create-import.ts similarity index 93% rename from src/func/imports/create-import.js rename to src/func/imports/create-import.ts index a441a011..5116346f 100644 --- a/src/func/imports/create-import.js +++ b/src/func/imports/create-import.ts @@ -17,10 +17,11 @@ */ -import {entityTypes, getAdditionalEntityProps} from '../entity'; +import {ENTITY_TYPES, type EntityTypeString} from '../../types/entity'; import _ from 'lodash'; import {camelToSnake} from '../../util'; +import {getAdditionalEntityProps} from '../entity'; import {getOriginSourceId} from './misc'; import {updateAliasSet} from '../alias'; import {updateDisambiguation} from '../disambiguation'; @@ -41,7 +42,7 @@ function createImportDataRecord(transacting, dataSets, importData) { const {entityType} = importData; // Safe check if entityType is one among the expected - if (!_.includes(entityTypes, entityType)) { + if (!ENTITY_TYPES.includes(entityType)) { throw new Error('Invalid entity type'); } @@ -66,10 +67,10 @@ function createImportDataRecord(transacting, dataSets, importData) { .returning('id'); } -function createImportHeader(transacting, record, entityType) { +function createImportHeader(transacting, record, entityType: EntityTypeString) { // Safe check if entityType is one among the expected - if (!_.includes(entityTypes, entityType)) { + if (!ENTITY_TYPES.includes(entityType)) { throw new Error('Invalid entity type'); } @@ -121,8 +122,7 @@ export function createImport(orm, importData) { transacting, camelToSnake({ aliasSetId: aliasSet && aliasSet.get('id'), - disambiguationId: - disambiguationObj && disambiguationObj.get('id'), + disambiguationId: disambiguationObj && disambiguationObj.get('id'), identifierSetId: identifierSet && identifierSet.get('id'), ...entityDataSets }), diff --git a/src/func/imports/recent-imports.ts b/src/func/imports/recent-imports.ts index 010f9791..3c45c657 100644 --- a/src/func/imports/recent-imports.ts +++ b/src/func/imports/recent-imports.ts @@ -17,9 +17,9 @@ */ import * as _ from 'lodash'; -import type {EntityTypeString} from '../../types/entity'; +import {ENTITY_TYPES, type EntityTypeString} from '../../types/entity'; +import type {ORM} from '../..'; import type {Transaction} from '../types'; -import {entityTypes} from '../entity'; import {getAliasByIds} from '../alias'; import moment from 'moment'; import {originSourceMapping} from './misc'; @@ -60,9 +60,9 @@ async function getRecentImportUtilData( 'bookbrainz.import.id' ); - /* Contruct importHolder object (holds importIds classified by their types) + /* Construct importHolder object (holds importIds classified by their types) object of form {type: []} */ - const importHolder = _.values(entityTypes).reduce( + const importHolder = ENTITY_TYPES.reduce( (holder: Record, type: string) => _.assign(holder, {[type]: []}), {} ); @@ -83,17 +83,16 @@ async function getRecentImportUtilData( }, {importHolder, originIdMap: {}, timeStampMap: {}}); } -function getRecentImportsByType(transacting, type, importIds) { +function getRecentImportsByType(transacting: Transaction, type: EntityTypeString, importIds: string[]) { return transacting.select('*') .from(`bookbrainz.${_.snakeCase(type)}_import`) .whereIn('import_id', importIds); } export async function getRecentImports( - orm: Record, transacting: Transaction, limit = 10, - offset = 0 + orm: ORM, transacting: Transaction, limit = 10, offset = 0 ) { - /* Fetch most recent ImportIds classified by importTypes + /* Fetch most recent ImportIds classified by entity types => importHolder - holds recentImports classified by entity type => timeStampMap - holds value importedAt value in object with importId as key @@ -103,12 +102,7 @@ export async function getRecentImports( const {importHolder: recentImportIdsByType, timeStampMap, originIdMap} = await getRecentImportUtilData(transacting, limit, offset); - - /* Qs. What are all import types? Ans => Extract the values from entityTypes - [Author, Edition, EditionGroup, Publisher, Work] */ - const importTypes: Array = _.values(entityTypes); - - /* Fetch imports for each importType using their importIds + /* Fetch imports for each entity type using their importIds We first pass type and id to fetch function, await all those promises and then flatten the array containing those results. Classification by type is important as only by knowing types we can access dataId and @@ -116,7 +110,7 @@ export async function getRecentImports( */ const recentImports = _.flatten( await Promise.all( - importTypes.map(type => + ENTITY_TYPES.map(type => getRecentImportsByType(transacting, type, recentImportIdsByType[type])) ) @@ -131,7 +125,7 @@ export async function getRecentImports( /* Add timestamp, source and defaultAlias to the recentImports Previously while getting utils data we fetched a mapping of importId to - timestamp and oringId. + timestamp and originId. We also fetched map of aliasId to alias object. Now using those we populate our final object */ // First, get the origin mapping => {originId: name} From a2307f578486d6f44245e7861485833e12718ce4 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:29:33 +0200 Subject: [PATCH 10/32] fix(import): add missing ORM parameter --- src/func/imports/create-import.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/func/imports/create-import.ts b/src/func/imports/create-import.ts index 5116346f..16a8e8c6 100644 --- a/src/func/imports/create-import.ts +++ b/src/func/imports/create-import.ts @@ -88,7 +88,7 @@ async function updateEntityDataSets(orm, transacting, importData) { if (languages) { entityDataSet.languageSetId = - await updateLanguageSet(transacting, null, languages); + await updateLanguageSet(orm, transacting, null, languages); } if (releaseEvents) { From a93bc48a15d8aa77b3b3ff834c1be9b4cde45971 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:34:26 +0200 Subject: [PATCH 11/32] other(ts): add type definitions to import creation functions --- src/func/imports/create-import.ts | 26 ++++++++++++++++++-------- src/types/parser.ts | 7 +++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/func/imports/create-import.ts b/src/func/imports/create-import.ts index 16a8e8c6..8f93d4e5 100644 --- a/src/func/imports/create-import.ts +++ b/src/func/imports/create-import.ts @@ -18,7 +18,10 @@ import {ENTITY_TYPES, type EntityTypeString} from '../../types/entity'; +import type {ParsedEdition, ParsedEntity} from '../../types/parser'; +import type {ORM} from '../..'; +import type {Transaction} from '../types'; import _ from 'lodash'; import {camelToSnake} from '../../util'; import {getAdditionalEntityProps} from '../entity'; @@ -30,15 +33,15 @@ import {updateLanguageSet} from '../language'; import {updateReleaseEventSet} from '../releaseEvent'; -function createImportRecord(transacting, data) { +function createImportRecord(transacting: Transaction, data) { return transacting.insert(data).into('bookbrainz.import').returning('id'); } -function createLinkTableRecord(transacting, record) { +function createLinkTableRecord(transacting: Transaction, record) { return transacting.insert(record).into('bookbrainz.link_import'); } -function createImportDataRecord(transacting, dataSets, importData) { +function createImportDataRecord(transacting: Transaction, dataSets, importData: ParsedEntity) { const {entityType} = importData; // Safe check if entityType is one among the expected @@ -67,7 +70,7 @@ function createImportDataRecord(transacting, dataSets, importData) { .returning('id'); } -function createImportHeader(transacting, record, entityType: EntityTypeString) { +function createImportHeader(transacting: Transaction, record, entityType: EntityTypeString) { // Safe check if entityType is one among the expected if (!ENTITY_TYPES.includes(entityType)) { @@ -79,12 +82,19 @@ function createImportHeader(transacting, record, entityType: EntityTypeString) { return transacting.insert(record).into(table).returning('import_id'); } -async function updateEntityDataSets(orm, transacting, importData) { +type EntityDataSetIds = Partial<{ + languageSetId: number; + releaseEventSetId: number; +}>; + +async function updateEntityDataSets( + orm: ORM, transacting: Transaction, importData: ParsedEntity +): Promise { // Extract all entity data sets related fields - const {languages, releaseEvents} = importData; + const {languages, releaseEvents} = importData as ParsedEdition; // Create an empty entityDataSet - const entityDataSet = {}; + const entityDataSet: EntityDataSetIds = {}; if (languages) { entityDataSet.languageSetId = @@ -102,7 +112,7 @@ async function updateEntityDataSets(orm, transacting, importData) { return entityDataSet; } -export function createImport(orm, importData) { +export function createImport(orm: ORM, importData: ParsedEntity) { return orm.bookshelf.transaction(async (transacting) => { const {alias, identifiers, disambiguation, entityType, source} = importData; diff --git a/src/types/parser.ts b/src/types/parser.ts index 662a3119..bfeaa617 100644 --- a/src/types/parser.ts +++ b/src/types/parser.ts @@ -21,6 +21,7 @@ * TODO: Investigate whether these are also useful elsewhere, e.g. for form validation. */ +import {FormLanguageT, FormReleaseEventT} from '../func/types'; import {AliasWithDefaultT} from './aliases'; import {EntityTypeString} from './entity'; import {IdentifierT} from './identifiers'; @@ -58,8 +59,14 @@ export type ParsedAuthor = ParsedBaseEntity & { type?: 'Person'; }; +export type ParsedEdition = ParsedBaseEntity & { + languages?: FormLanguageT[]; + releaseEvents?: FormReleaseEventT[]; +}; + export type ParsedWork = ParsedBaseEntity; export type ParsedEntity = | ParsedAuthor + | ParsedEdition | ParsedWork; From df6ddcb9989202ff190349405d56d06848a2c1a5 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 7 Aug 2023 23:01:43 +0200 Subject: [PATCH 12/32] other(ts): create a return type for `getEntitySetMetadataByType` Consequent usage of this type seems to also have revealed a bug... --- src/func/entity-sets.ts | 9 ++++++--- src/func/entity.ts | 10 +++++++++- src/func/imports/approve-import.ts | 6 +++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/func/entity-sets.ts b/src/func/entity-sets.ts index d7cd9fd6..38877089 100644 --- a/src/func/entity-sets.ts +++ b/src/func/entity-sets.ts @@ -24,11 +24,13 @@ import { createNewSetWithItems, getAddedItems, getComparisonFunc, getRemovedItems, getUnchangedItems } from './set'; +import {type EntitySetMetadataT} from './entity'; +import type {ORM} from '..'; function updateEntitySet( transacting: Transaction, oldSet: any, newItems: Array, - derivedSet: any, orm: Record + derivedSet: EntitySetMetadataT, orm: ORM ): Promise { const oldItems = oldSet ? oldSet.related(derivedSet.propName).toJSON() : []; @@ -61,8 +63,8 @@ function updateEntitySet( export async function updateEntitySets( - derivedSets: Array | null | undefined, currentEntity: any, - entityData: any, transacting: Transaction, orm: Record + derivedSets: EntitySetMetadataT[] | null | undefined, currentEntity: any, + entityData: any, transacting: Transaction, orm: ORM ): Promise | null | undefined> { // If no entity sets, return null if (!derivedSets) { @@ -77,6 +79,7 @@ export async function updateEntitySets( return Promise.resolve(null); } + // TODO: Find out why we expect a non-existing `model` property here!? const oldSetRecord = await derivedSet.model.forge({ id: currentEntity[derivedSet.name].id }).fetch({ diff --git a/src/func/entity.ts b/src/func/entity.ts index 2fd64c1f..47b3b174 100644 --- a/src/func/entity.ts +++ b/src/func/entity.ts @@ -79,11 +79,19 @@ export function getAdditionalEntityProps( } } +export type EntitySetMetadataT = { + entityIdField: string; + idField: string; + mutableFields?: string[]; + name: string; + propName: string; +}; + /** * @param {string} entityType - Entity type string * @returns {Object} - Returns entitySetMetadata (derivedSets) */ -export function getEntitySetMetadataByType(entityType: EntityTypeString): Array> { +export function getEntitySetMetadataByType(entityType: EntityTypeString): EntitySetMetadataT[] { if (entityType === 'Edition') { return [ { diff --git a/src/func/imports/approve-import.ts b/src/func/imports/approve-import.ts index 27608246..325a8e43 100644 --- a/src/func/imports/approve-import.ts +++ b/src/func/imports/approve-import.ts @@ -20,6 +20,7 @@ import * as _ from 'lodash'; import { getAdditionalEntityProps, getEntityModelByType, getEntitySetMetadataByType } from '../entity'; +import type {ORM} from '../..'; import type {Transaction} from '../types'; import {createNote} from '../note'; import {deleteImport} from './delete-import'; @@ -27,7 +28,7 @@ import {incrementEditorEditCountById} from '../editor'; interface approveEntityPropsType { - orm: any, + orm: ORM, transacting: Transaction, importEntity: any, editorId: string @@ -62,8 +63,7 @@ export async function approveImport( const additionalProps = getAdditionalEntityProps(importEntity, entityType); // Collect the entity sets from the importEntity - const entitySetMetadata: Array = - getEntitySetMetadataByType(entityType); + const entitySetMetadata = getEntitySetMetadataByType(entityType); const entitySets = entitySetMetadata.reduce( (set, {entityIdField}) => _.assign(set, {[entityIdField]: importEntity[entityIdField]}) From 53805a0f4487114ccbb425f11d2da5c78e719d78 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 17:15:24 +0200 Subject: [PATCH 13/32] chore(deps): install types for pg --- package.json | 1 + yarn.lock | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/package.json b/package.json index b1df4e21..c5f45bd7 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "@babel/register": "^7.13.16", "@types/lodash": "^4.14.168", "@types/node": "^18.11.18", + "@types/pg": "^8.6.0", "@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/parser": "^5.48.2", "babel-plugin-lodash": "^3.3.4", diff --git a/yarn.lock b/yarn.lock index bc5037ac..e70f5c10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,11 +1081,25 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== +"@types/node@*": + version "20.5.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.7.tgz#4b8ecac87fbefbc92f431d09c30e176fc0a7c377" + integrity sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA== + "@types/node@^18.11.18": version "18.11.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== +"@types/pg@^8.6.0": + version "8.10.2" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.10.2.tgz#7814d1ca02c8071f4d0864c1b17c589b061dba43" + integrity sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw== + dependencies: + "@types/node" "*" + pg-protocol "*" + pg-types "^4.0.1" + "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" @@ -2977,6 +2991,11 @@ object.values@^1.1.5: define-properties "^1.1.4" es-abstract "^1.20.4" +obuf@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3120,11 +3139,21 @@ pg-int8@1.0.1: resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== +pg-numeric@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pg-numeric/-/pg-numeric-1.0.2.tgz#816d9a44026086ae8ae74839acd6a09b0636aa3a" + integrity sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw== + pg-pool@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.2.tgz#ed1bed1fb8d79f1c6fd5fb1c99e990fbf9ddf178" integrity sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w== +pg-protocol@*: + version "1.6.0" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.0.tgz#4c91613c0315349363af2084608db843502f8833" + integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q== + pg-protocol@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0" @@ -3141,6 +3170,19 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" +pg-types@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-4.0.1.tgz#31857e89d00a6c66b06a14e907c3deec03889542" + integrity sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g== + dependencies: + pg-int8 "1.0.1" + pg-numeric "1.0.2" + postgres-array "~3.0.1" + postgres-bytea "~3.0.0" + postgres-date "~2.0.1" + postgres-interval "^3.0.0" + postgres-range "^1.1.1" + pg@^8.6.0: version "8.8.0" resolved "https://registry.yarnpkg.com/pg/-/pg-8.8.0.tgz#a77f41f9d9ede7009abfca54667c775a240da686" @@ -3198,16 +3240,33 @@ postgres-array@~2.0.0: resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== +postgres-array@~3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-3.0.2.tgz#68d6182cb0f7f152a7e60dc6a6889ed74b0a5f98" + integrity sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog== + postgres-bytea@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== +postgres-bytea@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-3.0.0.tgz#9048dc461ac7ba70a6a42d109221619ecd1cb089" + integrity sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw== + dependencies: + obuf "~1.1.2" + postgres-date@~1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== +postgres-date@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-2.0.1.tgz#638b62e5c33764c292d37b08f5257ecb09231457" + integrity sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw== + postgres-interval@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" @@ -3215,6 +3274,16 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +postgres-interval@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-3.0.0.tgz#baf7a8b3ebab19b7f38f07566c7aab0962f0c86a" + integrity sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw== + +postgres-range@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.3.tgz#9ccd7b01ca2789eb3c2e0888b3184225fa859f76" + integrity sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" From 959cc8e5b4eea4bc39d2876eee4c1cc77c6a6f86 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 17:20:41 +0200 Subject: [PATCH 14/32] other(ts): migrate recursivelyGetAreaParentsWithNames to TypeScript --- src/func/{area.js => area.ts} | 19 +++++++++++++++---- test/func/area.js | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) rename src/func/{area.js => area.ts} (69%) diff --git a/src/func/area.js b/src/func/area.ts similarity index 69% rename from src/func/area.js rename to src/func/area.ts index 273cca26..f225365d 100644 --- a/src/func/area.js +++ b/src/func/area.ts @@ -1,14 +1,17 @@ +import type Bookshelf from '@metabrainz/bookshelf'; +import type {ORM} from '..'; +import type {QueryResult} from 'pg'; /** * Recursively fetches an area's parents (with "has part" link) * Adapted from https://github.com/metabrainz/musicbrainz-server/blob/f79b6d0d2d4bd67254cc34426f17cf8eb21ec5bb/lib/MusicBrainz/Server/Data/Utils.pm#L255-L273 - * @param {object} orm - the BookBrainz ORM, initialized during app setup - * @param {string} areaId - The entity model name. + * @param {object} orm - the BookBrainz ORM, initialized during app setup, or a Bookshelf instance + * @param {string} areaId - The BBID (= MBID) of the area * @param {boolean} checkAllLevels - By default limits to the area types Country, Subdivision and City * @returns {Promise} The returned Promise returns the entity's * parent default alias */ -export async function recursivelyGetAreaParentsWithNames(orm, areaId, checkAllLevels = false) { +export async function recursivelyGetAreaParentsWithNames(orm: ORM | Bookshelf, areaId: string, checkAllLevels = false) { const levelsCondition = checkAllLevels ? '' : 'WHERE area.type IN (1, 2, 3)'; const rawSql = ` @@ -30,9 +33,17 @@ export async function recursivelyGetAreaParentsWithNames(orm, areaId, checkAllLe `; // Query the database to get the area parents recursively - const queryResult = await (orm.bookshelf || orm).knex.raw(rawSql); + const knex = 'bookshelf' in orm ? orm.bookshelf.knex : orm.knex; + const queryResult = await knex.raw>(rawSql); if (!Array.isArray(queryResult.rows)) { return []; } return queryResult.rows; } + +type AreaDescendantRow = { + descendant: string; + parent: string; + depth: number; + name: string; +}; diff --git a/test/func/area.js b/test/func/area.js index cf1063df..aa8b46db 100644 --- a/test/func/area.js +++ b/test/func/area.js @@ -1,6 +1,6 @@ import bookbrainzData from '../bookshelf'; import chai from 'chai'; -import {recursivelyGetAreaParentsWithNames} from '../../src/func/area'; +import {recursivelyGetAreaParentsWithNames} from '../../lib/func/area'; import {truncateTables} from '../../lib/util'; import uuid from 'node-uuid'; From 289a68efaef4823b3d6b74bf2dbd122345dfe26c Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 17:28:27 +0200 Subject: [PATCH 15/32] other(ts): rename last remaining .js module in func/ to .ts --- src/func/imports/{index.js => index.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/func/imports/{index.js => index.ts} (100%) diff --git a/src/func/imports/index.js b/src/func/imports/index.ts similarity index 100% rename from src/func/imports/index.js rename to src/func/imports/index.ts From aa539c290456a0d85bd5cd937cd95440e0fb2db9 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 18:16:46 +0200 Subject: [PATCH 16/32] other(ts): convert utils to TypeScript Replace deprecated usage of `String.substr`. --- src/{util.js => util.ts} | 65 +++++++++++++++++++++------------------- test/util.js | 2 +- 2 files changed, 35 insertions(+), 32 deletions(-) rename src/{util.js => util.ts} (80%) diff --git a/src/util.js b/src/util.ts similarity index 80% rename from src/util.js rename to src/util.ts index 952e9531..38dc913f 100644 --- a/src/util.js +++ b/src/util.ts @@ -17,16 +17,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import type Bookshelf from '@metabrainz/bookshelf'; +import type {Transaction} from './func/types'; import _ from 'lodash'; import {diff} from 'deep-diff'; -export function snakeToCamel(attrs) { +export function snakeToCamel(attrs: S) { return _.reduce(attrs, (result, val, key) => { - let newKey; + let newKey: string; if (key.indexOf('_') === 0) { - newKey = `_${_.camelCase(key.substr(1))}`; + newKey = `_${_.camelCase(key.substring(1))}`; } else { newKey = _.camelCase(key); @@ -35,15 +37,15 @@ export function snakeToCamel(attrs) { result[newKey] = val; return result; }, - {}); + {} as C); } -export function snakeToCamelID(attrs) { +export function snakeToCamelID(attrs: S) { return _.reduce(attrs, (result, val, key) => { - let newKey; + let newKey: string; if (key.indexOf('_') === 0) { - newKey = `_${_.camelCase(key.substr(1))}`; + newKey = `_${_.camelCase(key.substring(1))}`; } else { newKey = _.camelCase(key); @@ -56,15 +58,15 @@ export function snakeToCamelID(attrs) { result[newKey] = val; return result; }, - {}); + {} as C); } -export function camelToSnake(attrs) { +export function camelToSnake(attrs: C) { return _.reduce(attrs, (result, val, key) => { - let newKey; + let newKey: string; if (key.indexOf('_') === 0) { - newKey = `_${_.snakeCase(key.substr(1))}`; + newKey = `_${_.snakeCase(key.substring(1))}`; } else { newKey = _.snakeCase(key); @@ -73,11 +75,11 @@ export function camelToSnake(attrs) { result[newKey] = val; return result; }, - {}); + {} as S); } export class EntityTypeError extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = 'EntityTypeError'; this.message = message; @@ -93,10 +95,10 @@ export function validateEntityType(model) { } } -export async function truncateTables(Bookshelf, tables) { +export async function truncateTables(bookshelf: Bookshelf, tables: string[]) { for (const table of tables) { // eslint-disable-next-line no-await-in-loop - await Bookshelf.knex.raw(`TRUNCATE ${table} CASCADE`); + await bookshelf.knex.raw(`TRUNCATE ${table} CASCADE`); } } @@ -167,24 +169,23 @@ const DAY_STR_LENGTH = 2; * @param {number} year - A calendar year. * @param {number} [month] - A calendar month. * @param {number} [day] - A calendar day of month. - * @returns {string} The provided date formatted as an ISO 8601-2004 year or calendar - * date. + * @returns {string} The provided date formatted as an ISO 8601-2004 year or calendar date. */ -export function formatDate(year, month, day) { - if ((!year || isNaN(parseInt(year, 10))) && year !== 0) { +export function formatDate(year: number, month?: number, day?: number): string { + if ((!year || isNaN(parseInt(year as any, 10))) && year !== 0) { return null; } const isCommonEraDate = Math.sign(year) === 1 || Math.sign(year) === 0; // eslint-disable-next-line max-len const yearString = `${isCommonEraDate ? '+' : '-'}${_.padStart(Math.abs(year).toString(), YEAR_STR_LENGTH, '0')}`; - if (!month || isNaN(parseInt(month, 10))) { + if (!month || isNaN(parseInt(month as any, 10))) { return `${yearString}`; } const monthString = _.padStart(month.toString(), MONTH_STR_LENGTH, '0'); - if (!day || isNaN(parseInt(day, 10))) { + if (!day || isNaN(parseInt(day as any, 10))) { return `${yearString}-${monthString}`; } @@ -195,16 +196,15 @@ export function formatDate(year, month, day) { /** * Split ISO 8601 calendar dates or years into a numerical array. - * @param {string} date - A date of the format 'YYYY', 'YYYY-MM', or - * 'YYYY-MM-DD'. + * @param {string} date - A date of the format 'YYYY', 'YYYY-MM', or 'YYYY-MM-DD'. * @returns {number[]} Year, month, and day of month respectively. */ -export function parseDate(date) { +export function parseDate(date: string): Array { if (!date) { return [null, null, null]; } - const parts = date.toString().split('-'); + const parts: Array = date.toString().split('-'); // A leading minus sign denotes a BC date // This creates an empty part that needs to be removed, // and requires us to add the negative sign back for the year @@ -218,7 +218,7 @@ export function parseDate(date) { return [null, null, null]; } - let padding = []; + let padding: null[] = []; if (parts.length === 1) { padding = [null, null]; } @@ -226,7 +226,7 @@ export function parseDate(date) { padding = [null]; } - return parts.map((part) => { + return parts.map((part: string) => { const parsed = parseInt(part, 10); return isNaN(parsed) ? null : parsed; }).concat(padding); @@ -238,14 +238,17 @@ export function parseDate(date) { * alias set and author credit for that revision * Subsequent changes to the alias set or author credit for either entity will * only impact that entity's new revision. - * @param {object} orm - The Bookshelf ORM - * @param {object} transacting - The Bookshelf/Knex SQL transaction in progress + * @param {Bookshelf} orm - The Bookshelf ORM + * @param {Transaction} transacting - The Bookshelf/Knex SQL transaction in progress * @param {number|string} aliasSetId - The id of the new edition's alias set * @param {number|string} revisionId - The id of the new edition's revision * @param {number|string} authorCreditId - The id of the new edition's author credit * @returns {string} BBID of the newly created Edition Group */ -export async function createEditionGroupForNewEdition(orm, transacting, aliasSetId, revisionId, authorCreditId) { +export async function createEditionGroupForNewEdition( + orm: Bookshelf, transacting: Transaction, + aliasSetId: number | string, revisionId: number | string, authorCreditId: number | string +): Promise { const Entity = orm.model('Entity'); const EditionGroup = orm.model('EditionGroup'); const newEditionGroupEntity = await new Entity({type: 'EditionGroup'}) @@ -267,7 +270,7 @@ export async function createEditionGroupForNewEdition(orm, transacting, aliasSet * to be resolved as the values * @returns {Object} an object containing the same keys, but resolved promises */ -export async function promiseProps(promiseObj) { +export async function promiseProps(promiseObj: Record): Promise>> { const resolvedKeyValuePairs = await Promise.all( Object.entries(promiseObj).map( ([key, val]) => Promise.resolve(val).then((x) => [key, x]) diff --git a/test/util.js b/test/util.js index 9d5cf855..270bebcd 100644 --- a/test/util.js +++ b/test/util.js @@ -1,4 +1,4 @@ -import {formatDate, parseDate} from '../src/util'; +import {formatDate, parseDate} from '../lib/util'; import chai from 'chai'; From 2f861f8e3077f8627a7b76c5b9747738ce927035 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 18:41:30 +0200 Subject: [PATCH 17/32] other(ts): use ORM type everywhere --- src/func/alias.ts | 3 ++- src/func/annotation.ts | 5 +++-- src/func/author-credit.ts | 7 ++++--- src/func/create-entity.ts | 3 ++- src/func/disambiguation.ts | 5 +++-- src/func/editor.ts | 5 +++-- src/func/identifier.ts | 3 ++- src/func/language.ts | 5 +++-- src/func/note.ts | 5 +++-- src/func/publisher.ts | 3 ++- src/func/relationship.ts | 7 ++++--- src/func/relationshipAttributes.ts | 3 ++- src/func/releaseEvent.ts | 3 ++- src/func/set.ts | 5 +++-- src/func/work.ts | 6 ++++-- 15 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/func/alias.ts b/src/func/alias.ts index c83429e2..0a7c9c36 100644 --- a/src/func/alias.ts +++ b/src/func/alias.ts @@ -26,12 +26,13 @@ import { getUnchangedItems } from './set'; import type {EntityTypeString} from '../types/entity'; +import type {ORM} from '..'; import type {Transaction} from './types'; import {snakeToCamel} from '../util'; export async function updateAliasSet( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, oldDefaultAliasId: number | null | undefined, newSetItemsWithDefault: Array ) { diff --git a/src/func/annotation.ts b/src/func/annotation.ts index 4f6b0940..1191bcda 100644 --- a/src/func/annotation.ts +++ b/src/func/annotation.ts @@ -19,11 +19,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import type {ORM} from '..'; import type {Transaction} from './types'; /** - * @param {Object} orm - Bookbrainz orm wrapper holding all models + * @param {ORM} orm - Bookbrainz orm wrapper holding all models * @param {Transaction} transacting - The present knex transacting object * @param {Object} oldAnnotation - The old annotation object * @param {string} newContent - New annotation to be set @@ -31,7 +32,7 @@ import type {Transaction} from './types'; * @returns {Promise} - Returns Annotation object */ export function updateAnnotation( - orm: any, transacting: Transaction, oldAnnotation: any, + orm: ORM, transacting: Transaction, oldAnnotation: any, newContent: string, revision: any ) { const {Annotation} = orm; diff --git a/src/func/author-credit.ts b/src/func/author-credit.ts index 49cdd24d..593b93b8 100644 --- a/src/func/author-credit.ts +++ b/src/func/author-credit.ts @@ -18,10 +18,11 @@ import * as _ from 'lodash'; import type {AuthorCreditNameT, Transaction} from './types'; +import type {ORM} from '..'; function findAuthorCredit( - orm: any, transacting: Transaction, authorCredit: Array + orm: ORM, transacting: Transaction, authorCredit: Array ) { const tables = {cc: 'bookbrainz.author_credit'}; @@ -58,7 +59,7 @@ function findAuthorCredit( export async function fetchOrCreateCredit( - orm: any, transacting: Transaction, authorCredit: Array + orm: ORM, transacting: Transaction, authorCredit: Array ) { const result = await findAuthorCredit(orm, transacting, authorCredit); @@ -88,7 +89,7 @@ export async function fetchOrCreateCredit( } export function updateAuthorCredit( - orm: any, transacting: Transaction, oldCredit: any, + orm: ORM, transacting: Transaction, oldCredit: any, newCreditNames: Array ): Promise { /* eslint-disable consistent-return */ diff --git a/src/func/create-entity.ts b/src/func/create-entity.ts index 7e7609f4..b5388524 100644 --- a/src/func/create-entity.ts +++ b/src/func/create-entity.ts @@ -26,6 +26,7 @@ import { import type {AliasWithDefaultT} from '../types/aliases'; import type {EntityTypeString} from '../types/entity'; import type {IdentifierT} from '../types/identifiers'; +import type {ORM} from '..'; import type {Transaction} from './types'; import {createNote} from './note'; import {incrementEditorEditCountById} from './editor'; @@ -50,7 +51,7 @@ interface ExtraEntityDataType extends EntityDataType { } interface CreateEntityPropsType { - orm: any, + orm: ORM, transacting: Transaction, editorId: string, entityData: ExtraEntityDataType, diff --git a/src/func/disambiguation.ts b/src/func/disambiguation.ts index cb18bacc..8c558395 100644 --- a/src/func/disambiguation.ts +++ b/src/func/disambiguation.ts @@ -19,18 +19,19 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import type {ORM} from '..'; import type {Transaction} from './types'; /** - * @param {Object} orm - The BookBrainz orm wrapper containing all models + * @param {ORM} orm - The BookBrainz orm wrapper containing all models * @param {Transaction} transacting - The current knex transaction object * @param {Object} oldDisambiguation - The previous disambiguation object * @param {string} newComment - The new disambiguation string * @returns {Promise} - Returns Promise holding Disambiguation object */ export function updateDisambiguation( - orm: any, transacting: Transaction, oldDisambiguation: any, + orm: ORM, transacting: Transaction, oldDisambiguation: any, newComment: string ) { const {Disambiguation} = orm; diff --git a/src/func/editor.ts b/src/func/editor.ts index 0ff8a1e7..7d4ecfeb 100644 --- a/src/func/editor.ts +++ b/src/func/editor.ts @@ -19,19 +19,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import {ORM} from '..'; import type {Transaction} from './types'; /** * Adds 1 to the edit count of the specified editor * - * @param {object} orm - the BookBrainz ORM, initialized during app setup + * @param {ORM} orm - the BookBrainz ORM, initialized during app setup * @param {string} id - row ID of editor to be updated * @param {Transaction} transacting - Bookshelf transaction object (must be in * progress) * @returns {Promise} - Resolves to the updated editor model */ export function incrementEditorEditCountById( - orm: any, + orm: ORM, id: string, transacting: Transaction ): Promise { diff --git a/src/func/identifier.ts b/src/func/identifier.ts index 5d3480dd..02c00462 100644 --- a/src/func/identifier.ts +++ b/src/func/identifier.ts @@ -21,11 +21,12 @@ import { createNewSetWithItems, getAddedItems, getRemovedItems, getUnchangedItems } from './set'; import type {IdentifierT} from '../types/identifiers'; +import type {ORM} from '..'; import type {Transaction} from './types'; export function updateIdentifierSet( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, newSetItems: Array ): Promise { function comparisonFunc(obj: IdentifierT, other: IdentifierT) { diff --git a/src/func/language.ts b/src/func/language.ts index 18e3d2eb..40151daa 100644 --- a/src/func/language.ts +++ b/src/func/language.ts @@ -21,10 +21,11 @@ import type {FormLanguageT as Language, Transaction} from './types'; import { createNewSetWithItems, getAddedItems, getRemovedItems, getUnchangedItems } from './set'; +import type {ORM} from '..'; export function updateLanguageSet( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, newSetItems: Array ): Promise { function comparisonFunc(obj: Language, other: Language) { @@ -50,7 +51,7 @@ export function updateLanguageSet( return Promise.resolve(oldSet || null); } - // addedItems combined with unchangedItems since lanuguages are read-only + // addedItems combined with unchangedItems since languages are read-only return createNewSetWithItems( orm, transacting, LanguageSet, [...unchangedItems, ...addedItems], [], 'languages' diff --git a/src/func/note.ts b/src/func/note.ts index 9a105ae9..f9bc90d5 100644 --- a/src/func/note.ts +++ b/src/func/note.ts @@ -20,10 +20,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import type {ORM} from '..'; import type {Transaction} from './types'; /** - * @param {Object} orm - Bookbrainz orm wrapper containing all models + * @param {ORM} orm - Bookbrainz orm wrapper containing all models * @param {string} content - Note content * @param {string} editorId - Editor's Id * @param {Object} revision - Revision object created using orm.Revision model @@ -31,7 +32,7 @@ import type {Transaction} from './types'; * @returns {Object | null} Returns the created Note object or returns null */ export function createNote( - orm: any, + orm: ORM, content: string, editorId: string, revision: any, diff --git a/src/func/publisher.ts b/src/func/publisher.ts index 50ec4322..033ff3b6 100644 --- a/src/func/publisher.ts +++ b/src/func/publisher.ts @@ -21,10 +21,11 @@ import type {FormPublisherT as Publisher, Transaction} from './types'; import { createNewSetWithItems, getAddedItems, getRemovedItems, getUnchangedItems } from './set'; +import type {ORM} from '..'; export function updatePublisherSet( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, newSetItems: Array ): Promise { function comparisonFunc(obj: Publisher, other: Publisher) { diff --git a/src/func/relationship.ts b/src/func/relationship.ts index 5f4c21d6..3ac49b97 100644 --- a/src/func/relationship.ts +++ b/src/func/relationship.ts @@ -26,6 +26,7 @@ import { removeItemsFromSet } from './set'; import type {EntityTypeString} from '../types/entity'; +import type {ORM} from '..'; import {promiseProps} from '../util'; @@ -81,7 +82,7 @@ async function getMasterRelationshipSetForEntity( } async function updateRelationshipSetForEntity( - orm: any, + orm: ORM, transacting: Transaction, bbid: string, allAddedItems: Array, @@ -119,7 +120,7 @@ async function updateRelationshipSetForEntity( * these. If no entities are affected (there are no changes), an empty object * is returned. * - * @param {any} orm - an initialized instance of bookbrainz-data-js + * @param {ORM} orm - an initialized instance of bookbrainz-data-js * @param {Transaction} transacting - the current transaction * @param {any} oldSet - the RelationshipSet object for the old entity data * @param {Array} newSetItems - the edited RelationshipSet for the @@ -129,7 +130,7 @@ async function updateRelationshipSetForEntity( * map */ export function updateRelationshipSets( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, newSetItems: Array ): Promise { function comparisonFunc(obj: Relationship, other: Relationship) { diff --git a/src/func/relationshipAttributes.ts b/src/func/relationshipAttributes.ts index ec75b78d..13d70d90 100644 --- a/src/func/relationshipAttributes.ts +++ b/src/func/relationshipAttributes.ts @@ -21,10 +21,11 @@ import type {FormRelationshipAttributesT as RelationshipAttributes, Transaction} import { createNewRelationshipAttributeSetWithItems, getAddedItems, getRemovedItems, getUnchangedItems } from './set'; +import type {ORM} from '..'; export function updateRelationshipAttributeSet( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, newSetItems: Array ): Promise { function comparisonFunc(obj: RelationshipAttributes, other: RelationshipAttributes) { diff --git a/src/func/releaseEvent.ts b/src/func/releaseEvent.ts index 4faf2473..f656151b 100644 --- a/src/func/releaseEvent.ts +++ b/src/func/releaseEvent.ts @@ -22,10 +22,11 @@ import type {FormReleaseEventT as ReleaseEvent, Transaction} from './types'; import { createNewSetWithItems, getAddedItems, getRemovedItems, getUnchangedItems } from './set'; +import type {ORM} from '..'; export function updateReleaseEventSet( - orm: any, transacting: Transaction, oldSet: any, + orm: ORM, transacting: Transaction, oldSet: any, newSetItems: Array ): Promise { function comparisonFunc(obj, other) { diff --git a/src/func/set.ts b/src/func/set.ts index 91b03a25..b309bf11 100644 --- a/src/func/set.ts +++ b/src/func/set.ts @@ -18,6 +18,7 @@ import * as _ from 'lodash'; import type {FormRelationshipAttributesT as RelationshipAttributeT, SetItemT, Transaction} from './types'; +import type {ORM} from '..'; /** * Returns a function which compares two object provided to it using the @@ -113,7 +114,7 @@ export function getRemovedItems( export const removeItemsFromSet = getRemovedItems; export async function createNewSetWithItems( - orm: any, transacting: Transaction, SetModel: any, + orm: ORM, transacting: Transaction, SetModel: any, unchangedItems: Array, addedItems: Array, itemsAttribute: string, idAttribute = 'id' ): Promise { @@ -142,7 +143,7 @@ export async function createNewSetWithItems( return newSet; } export async function createNewRelationshipAttributeSetWithItems( - orm: any, transacting: Transaction, SetModel: any, + orm: ORM, transacting: Transaction, SetModel: any, unchangedItems: Array, addedItems: Array, itemsAttribute: string, idAttribute = 'id' ): Promise { diff --git a/src/func/work.ts b/src/func/work.ts index 3b36e859..472af375 100644 --- a/src/func/work.ts +++ b/src/func/work.ts @@ -17,13 +17,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import type {ORM} from '..'; + /** - * @param {object} orm - the BookBrainz ORM, initialized during app setup + * @param {ORM} orm - the BookBrainz ORM, initialized during app setup * @param {array} workBBIDs - the array containing the BBIDs of the works contained in the edition * @returns {Object} - Returns an array of objects containing the authorAlias, authorBBID of each work in an edition */ -export async function loadAuthorNames(orm: any, workBBIDs: Array) { +export async function loadAuthorNames(orm: ORM, workBBIDs: Array) { if (!workBBIDs.length) { return []; } From 436984dee2219444c7315679e948ecdaa8e85350 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 18:44:42 +0200 Subject: [PATCH 18/32] other(ts): type raw query result of `loadAuthorNames` --- src/func/work.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/func/work.ts b/src/func/work.ts index 472af375..17e06d02 100644 --- a/src/func/work.ts +++ b/src/func/work.ts @@ -18,6 +18,7 @@ */ import type {ORM} from '..'; +import type {QueryResult} from 'pg'; /** @@ -55,6 +56,12 @@ export async function loadAuthorNames(orm: ORM, workBBIDs: Array) { and work.data_id is not null and work.bbid in ${`(${workBBIDs.map(bbid => `'${bbid}'`).join(', ')})`}`; - const queryResults = await orm.bookshelf.knex.raw(sqlQuery); + const queryResults = await orm.bookshelf.knex.raw>(sqlQuery); return queryResults.rows; } + +type WorkAuthorRow = { + authorBBID: string; + authorAlias: string; + workBBID: string; +}; From a335e302fbc41b9d4413c71aa445a50cd1d3f3dc Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 18:53:45 +0200 Subject: [PATCH 19/32] other(ts): type raw query result of `getEdition(Group)sCreditedToAuthor` --- src/func/author-credit.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/func/author-credit.ts b/src/func/author-credit.ts index 593b93b8..7b02e48d 100644 --- a/src/func/author-credit.ts +++ b/src/func/author-credit.ts @@ -18,7 +18,9 @@ import * as _ from 'lodash'; import type {AuthorCreditNameT, Transaction} from './types'; +import type Bookshelf from '@metabrainz/bookshelf'; import type {ORM} from '..'; +import type {QueryResult} from 'pg'; function findAuthorCredit( @@ -123,13 +125,13 @@ export function updateAuthorCredit( /** * Fetches all the Edition entities credited to an Author (with Author Credits) - * @param {object} bookshelf - the BookBrainz ORM, initialized during app setup + * @param {Bookshelf} bookshelf - the Bookshelf instance, initialized during app setup * @param {string} authorBBID - The target Author's BBID. * @returns {Promise} The returned Promise returns the Edition BBID and default alias */ export async function getEditionsCreditedToAuthor( - bookshelf: any, authorBBID: string + bookshelf: Bookshelf, authorBBID: string ) { const rawSql = ` SELECT e.bbid , alias."name" from bookbrainz.author LEFT JOIN bookbrainz.author_credit_name acn on acn.author_bbid = author.bbid @@ -141,7 +143,7 @@ export async function getEditionsCreditedToAuthor( AND e.master = true AND e.data_id is not null `; - let queryResult; + let queryResult: QueryResult; try { queryResult = await bookshelf.knex.raw(rawSql); } @@ -157,13 +159,13 @@ export async function getEditionsCreditedToAuthor( /** * Fetches all the Edition Group entities credited to an Author (with Author Credits) - * @param {object} bookshelf - the BookBrainz ORM, initialized during app setup + * @param {Bookshelf} bookshelf - the Bookshelf instance, initialized during app setup * @param {string} authorBBID - The target Author's BBID. * @returns {Promise} The returned Promise returns the Edition Group BBID and default alias */ export async function getEditionGroupsCreditedToAuthor( - bookshelf: any, authorBBID: string + bookshelf: Bookshelf, authorBBID: string ) { const rawSql = ` SELECT eg.bbid , alias."name" from bookbrainz.author LEFT JOIN bookbrainz.author_credit_name acn on acn.author_bbid = author.bbid @@ -175,7 +177,7 @@ export async function getEditionGroupsCreditedToAuthor( AND eg.master = true AND eg.data_id is not null `; - let queryResult; + let queryResult: QueryResult; try { queryResult = await bookshelf.knex.raw(rawSql); } @@ -188,3 +190,8 @@ export async function getEditionGroupsCreditedToAuthor( } return queryResult.rows; } + +type AliasAndBBIDRow = { + name: string; + bbid: string; +}; From a553d13feab7199992bd20c69a1c8ba6af0610b6 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 18:56:25 +0200 Subject: [PATCH 20/32] other(ts): add type definitions to Alias model --- src/models/{alias.js => alias.ts} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename src/models/{alias.js => alias.ts} (91%) diff --git a/src/models/alias.js b/src/models/alias.ts similarity index 91% rename from src/models/alias.js rename to src/models/alias.ts index c90f0dea..439ea54c 100644 --- a/src/models/alias.js +++ b/src/models/alias.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function alias(bookshelf) { +export default function alias(bookshelf: Bookshelf) { const Alias = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', From e3c529f9deb0d2d92b501a81af5b56427e478488 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 21:27:39 +0200 Subject: [PATCH 21/32] other(ts): add minimal type definitions to all bookshelf models - Batch-rename all JS modules: `rename .js .ts src/models/{*,*/*}.js` - Import and add Bookshelf type to each model using global search and replace - Adapt helper function in edition.ts - Update test to refer to the generated JS files in the lib/ directory Tests are passing but the bookshelf models still cause type errors caused by their non-standard inheritance. --- .../{achievementType.js => achievementType.ts} | 3 ++- .../{achievementUnlock.js => achievementUnlock.ts} | 3 ++- src/models/{adminLog.js => adminLog.ts} | 3 ++- src/models/{aliasSet.js => aliasSet.ts} | 3 ++- src/models/{annotation.js => annotation.ts} | 3 ++- src/models/{area.js => area.ts} | 3 ++- src/models/{areaType.js => areaType.ts} | 3 ++- src/models/{authorCredit.js => authorCredit.ts} | 3 ++- .../{authorCreditName.js => authorCreditName.ts} | 3 ++- src/models/{authorType.js => authorType.ts} | 3 ++- src/models/data/{authorData.js => authorData.ts} | 3 ++- src/models/data/{editionData.js => editionData.ts} | 3 ++- .../{editionGroupData.js => editionGroupData.ts} | 3 ++- .../data/{publisherData.js => publisherData.ts} | 3 ++- src/models/data/{seriesData.js => seriesData.ts} | 3 ++- src/models/data/{workData.js => workData.ts} | 3 ++- src/models/{disambiguation.js => disambiguation.ts} | 3 ++- src/models/{editionFormat.js => editionFormat.ts} | 3 ++- .../{editionGroupType.js => editionGroupType.ts} | 3 ++- src/models/{editionStatus.js => editionStatus.ts} | 3 ++- src/models/{editor.js => editor.ts} | 3 ++- .../{editorEntityVisits.js => editorEntityVisits.ts} | 3 ++- src/models/{editorType.js => editorType.ts} | 3 ++- src/models/entities/{author.js => author.ts} | 5 ++++- src/models/entities/{edition.js => edition.ts} | 12 +++++++----- .../entities/{editionGroup.js => editionGroup.ts} | 5 ++++- src/models/entities/{publisher.js => publisher.ts} | 5 ++++- src/models/entities/{series.js => series.ts} | 5 ++++- src/models/entities/{work.js => work.ts} | 5 ++++- src/models/{entity.js => entity.ts} | 3 ++- ...ternalServiceOauth.js => externalServiceOauth.ts} | 3 ++- src/models/{gender.js => gender.ts} | 3 ++- .../headers/{authorHeader.js => authorHeader.ts} | 3 ++- .../{editionGroupHeader.js => editionGroupHeader.ts} | 3 ++- .../headers/{editionHeader.js => editionHeader.ts} | 3 ++- .../{publisherHeader.js => publisherHeader.ts} | 3 ++- .../headers/{seriesHeader.js => seriesHeader.ts} | 3 ++- src/models/headers/{workHeader.js => workHeader.ts} | 3 ++- src/models/{identifier.js => identifier.ts} | 3 ++- src/models/{identifierSet.js => identifierSet.ts} | 3 ++- src/models/{identifierType.js => identifierType.ts} | 3 ++- .../imports/{authorImport.js => authorImport.ts} | 5 ++++- .../{editionGroupImport.js => editionGroupImport.ts} | 5 ++++- .../imports/{editionImport.js => editionImport.ts} | 5 ++++- .../{publisherImport.js => publisherImport.ts} | 5 ++++- src/models/imports/{workImport.js => workImport.ts} | 5 ++++- src/models/{language.js => language.ts} | 3 ++- src/models/{languageSet.js => languageSet.ts} | 3 ++- src/models/{note.js => note.ts} | 3 ++- src/models/{publisherSet.js => publisherSet.ts} | 3 ++- src/models/{publisherType.js => publisherType.ts} | 3 ++- src/models/{relationship.js => relationship.ts} | 3 ++- ...tionshipAttribute.js => relationshipAttribute.ts} | 3 ++- ...ipAttributeSet.js => relationshipAttributeSet.ts} | 3 ++- ...extValue.js => relationshipAttributeTextValue.ts} | 3 ++- ...AttributeType.js => relationshipAttributeType.ts} | 3 ++- .../{relationshipSet.js => relationshipSet.ts} | 3 ++- .../{relationshipType.js => relationshipType.ts} | 3 ++- ...ibuteType.js => relationshipTypeAttributeType.ts} | 3 ++- src/models/{releaseEvent.js => releaseEvent.ts} | 3 ++- .../{releaseEventSet.js => releaseEventSet.ts} | 3 ++- src/models/{revision.js => revision.ts} | 3 ++- .../{authorRevision.js => authorRevision.ts} | 3 ++- ...itionGroupRevision.js => editionGroupRevision.ts} | 3 ++- .../{editionRevision.js => editionRevision.ts} | 3 ++- .../{publisherRevision.js => publisherRevision.ts} | 3 ++- .../{seriesRevision.js => seriesRevision.ts} | 3 ++- .../revisions/{workRevision.js => workRevision.ts} | 3 ++- .../{seriesOrderingType.js => seriesOrderingType.ts} | 3 ++- src/models/{titleType.js => titleType.ts} | 3 ++- src/models/{titleUnlock.js => titleUnlock.ts} | 3 ++- src/models/{userCollection.js => userCollection.ts} | 3 ++- ...Collaborator.js => userCollectionCollaborator.ts} | 3 ++- .../{userCollectionItem.js => userCollectionItem.ts} | 3 ++- src/models/{workType.js => workType.ts} | 3 ++- test/testModule.js | 2 +- 76 files changed, 176 insertions(+), 80 deletions(-) rename src/models/{achievementType.js => achievementType.ts} (90%) rename src/models/{achievementUnlock.js => achievementUnlock.ts} (91%) rename src/models/{adminLog.js => adminLog.ts} (91%) rename src/models/{aliasSet.js => aliasSet.ts} (91%) rename src/models/{annotation.js => annotation.ts} (90%) rename src/models/{area.js => area.ts} (92%) rename src/models/{areaType.js => areaType.ts} (90%) rename src/models/{authorCredit.js => authorCredit.ts} (91%) rename src/models/{authorCreditName.js => authorCreditName.ts} (90%) rename src/models/{authorType.js => authorType.ts} (90%) rename src/models/data/{authorData.js => authorData.ts} (96%) rename src/models/data/{editionData.js => editionData.ts} (94%) rename src/models/data/{editionGroupData.js => editionGroupData.ts} (93%) rename src/models/data/{publisherData.js => publisherData.ts} (96%) rename src/models/data/{seriesData.js => seriesData.ts} (93%) rename src/models/data/{workData.js => workData.ts} (93%) rename src/models/{disambiguation.js => disambiguation.ts} (90%) rename src/models/{editionFormat.js => editionFormat.ts} (90%) rename src/models/{editionGroupType.js => editionGroupType.ts} (89%) rename src/models/{editionStatus.js => editionStatus.ts} (90%) rename src/models/{editor.js => editor.ts} (94%) rename src/models/{editorEntityVisits.js => editorEntityVisits.ts} (90%) rename src/models/{editorType.js => editorType.ts} (90%) rename src/models/entities/{author.js => author.ts} (93%) rename src/models/entities/{edition.js => edition.ts} (86%) rename src/models/entities/{editionGroup.js => editionGroup.ts} (93%) rename src/models/entities/{publisher.js => publisher.ts} (93%) rename src/models/entities/{series.js => series.ts} (93%) rename src/models/entities/{work.js => work.ts} (93%) rename src/models/{entity.js => entity.ts} (91%) rename src/models/{externalServiceOauth.js => externalServiceOauth.ts} (90%) rename src/models/{gender.js => gender.ts} (90%) rename src/models/headers/{authorHeader.js => authorHeader.ts} (90%) rename src/models/headers/{editionGroupHeader.js => editionGroupHeader.ts} (89%) rename src/models/headers/{editionHeader.js => editionHeader.ts} (90%) rename src/models/headers/{publisherHeader.js => publisherHeader.ts} (90%) rename src/models/headers/{seriesHeader.js => seriesHeader.ts} (90%) rename src/models/headers/{workHeader.js => workHeader.ts} (90%) rename src/models/{identifier.js => identifier.ts} (91%) rename src/models/{identifierSet.js => identifierSet.ts} (91%) rename src/models/{identifierType.js => identifierType.ts} (90%) rename src/models/imports/{authorImport.js => authorImport.ts} (90%) rename src/models/imports/{editionGroupImport.js => editionGroupImport.ts} (90%) rename src/models/imports/{editionImport.js => editionImport.ts} (90%) rename src/models/imports/{publisherImport.js => publisherImport.ts} (90%) rename src/models/imports/{workImport.js => workImport.ts} (90%) rename src/models/{language.js => language.ts} (93%) rename src/models/{languageSet.js => languageSet.ts} (91%) rename src/models/{note.js => note.ts} (91%) rename src/models/{publisherSet.js => publisherSet.ts} (91%) rename src/models/{publisherType.js => publisherType.ts} (90%) rename src/models/{relationship.js => relationship.ts} (93%) rename src/models/{relationshipAttribute.js => relationshipAttribute.ts} (92%) rename src/models/{relationshipAttributeSet.js => relationshipAttributeSet.ts} (91%) rename src/models/{relationshipAttributeTextValue.js => relationshipAttributeTextValue.ts} (94%) rename src/models/{relationshipAttributeType.js => relationshipAttributeType.ts} (89%) rename src/models/{relationshipSet.js => relationshipSet.ts} (91%) rename src/models/{relationshipType.js => relationshipType.ts} (91%) rename src/models/{relationshipTypeAttributeType.js => relationshipTypeAttributeType.ts} (89%) rename src/models/{releaseEvent.js => releaseEvent.ts} (92%) rename src/models/{releaseEventSet.js => releaseEventSet.ts} (91%) rename src/models/{revision.js => revision.ts} (92%) rename src/models/revisions/{authorRevision.js => authorRevision.ts} (95%) rename src/models/revisions/{editionGroupRevision.js => editionGroupRevision.ts} (94%) rename src/models/revisions/{editionRevision.js => editionRevision.ts} (95%) rename src/models/revisions/{publisherRevision.js => publisherRevision.ts} (94%) rename src/models/revisions/{seriesRevision.js => seriesRevision.ts} (94%) rename src/models/revisions/{workRevision.js => workRevision.ts} (95%) rename src/models/{seriesOrderingType.js => seriesOrderingType.ts} (89%) rename src/models/{titleType.js => titleType.ts} (90%) rename src/models/{titleUnlock.js => titleUnlock.ts} (91%) rename src/models/{userCollection.js => userCollection.ts} (93%) rename src/models/{userCollectionCollaborator.js => userCollectionCollaborator.ts} (90%) rename src/models/{userCollectionItem.js => userCollectionItem.ts} (90%) rename src/models/{workType.js => workType.ts} (90%) diff --git a/src/models/achievementType.js b/src/models/achievementType.ts similarity index 90% rename from src/models/achievementType.js rename to src/models/achievementType.ts index d7e17740..d5b00a6e 100644 --- a/src/models/achievementType.js +++ b/src/models/achievementType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function achievementType(bookshelf) { +export default function achievementType(bookshelf: Bookshelf) { const AchievementType = bookshelf.Model.extend({ achievementUnlocks() { return this.hasMany('AchievementUnlock', 'achievement_id'); diff --git a/src/models/achievementUnlock.js b/src/models/achievementUnlock.ts similarity index 91% rename from src/models/achievementUnlock.js rename to src/models/achievementUnlock.ts index 6308fad4..bf127970 100644 --- a/src/models/achievementUnlock.js +++ b/src/models/achievementUnlock.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function achievementUnlock(bookshelf) { +export default function achievementUnlock(bookshelf: Bookshelf) { const AchievementUnlock = bookshelf.Model.extend({ achievement() { return this.belongsTo('AchievementType', 'achievement_id'); diff --git a/src/models/adminLog.js b/src/models/adminLog.ts similarity index 91% rename from src/models/adminLog.js rename to src/models/adminLog.ts index 55f68b18..f5d167ca 100644 --- a/src/models/adminLog.js +++ b/src/models/adminLog.ts @@ -18,9 +18,10 @@ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function adminLog(bookshelf) { +export default function adminLog(bookshelf: Bookshelf) { const AdminLog = bookshelf.Model.extend({ admin() { return this.belongsTo('Editor', 'admin_id'); diff --git a/src/models/aliasSet.js b/src/models/aliasSet.ts similarity index 91% rename from src/models/aliasSet.js rename to src/models/aliasSet.ts index eec01ce8..c3603e11 100644 --- a/src/models/aliasSet.js +++ b/src/models/aliasSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function aliasSet(bookshelf) { +export default function aliasSet(bookshelf: Bookshelf) { const AliasSet = bookshelf.Model.extend({ aliases() { return this.belongsToMany( diff --git a/src/models/annotation.js b/src/models/annotation.ts similarity index 90% rename from src/models/annotation.js rename to src/models/annotation.ts index 0e12d421..59e935c4 100644 --- a/src/models/annotation.js +++ b/src/models/annotation.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function annotation(bookshelf) { +export default function annotation(bookshelf: Bookshelf) { const Annotation = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/area.js b/src/models/area.ts similarity index 92% rename from src/models/area.js rename to src/models/area.ts index 3ffeaf7c..240cfdea 100644 --- a/src/models/area.js +++ b/src/models/area.ts @@ -17,10 +17,11 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; import {recursivelyGetAreaParentsWithNames} from '../func/area'; -export default function area(bookshelf) { +export default function area(bookshelf: Bookshelf) { const Area = bookshelf.Model.extend({ areaType() { return this.belongsTo('AreaType', 'type'); diff --git a/src/models/areaType.js b/src/models/areaType.ts similarity index 90% rename from src/models/areaType.js rename to src/models/areaType.ts index 62b94f1b..f7885fa1 100644 --- a/src/models/areaType.js +++ b/src/models/areaType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function areaType(bookshelf) { +export default function areaType(bookshelf: Bookshelf) { const AreaType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/authorCredit.js b/src/models/authorCredit.ts similarity index 91% rename from src/models/authorCredit.js rename to src/models/authorCredit.ts index 04e9433b..0c681508 100644 --- a/src/models/authorCredit.js +++ b/src/models/authorCredit.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamelID} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function authorCredit(bookshelf) { +export default function authorCredit(bookshelf: Bookshelf) { const AuthorCredit = bookshelf.Model.extend({ editions() { return this.hasMany('Edition', 'author_credit_id'); diff --git a/src/models/authorCreditName.js b/src/models/authorCreditName.ts similarity index 90% rename from src/models/authorCreditName.js rename to src/models/authorCreditName.ts index 473a3356..a02c9b2e 100644 --- a/src/models/authorCreditName.js +++ b/src/models/authorCreditName.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamelID} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function authorCreditName(bookshelf) { +export default function authorCreditName(bookshelf: Bookshelf) { const AuthorCreditName = bookshelf.Model.extend({ author() { return this.belongsTo('Author', 'author_bbid', 'bbid'); diff --git a/src/models/authorType.js b/src/models/authorType.ts similarity index 90% rename from src/models/authorType.js rename to src/models/authorType.ts index 1765e1f5..aa001163 100644 --- a/src/models/authorType.js +++ b/src/models/authorType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function authorType(bookshelf) { +export default function authorType(bookshelf: Bookshelf) { const AuthorType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/data/authorData.js b/src/models/data/authorData.ts similarity index 96% rename from src/models/data/authorData.js rename to src/models/data/authorData.ts index d9502575..0136a83c 100644 --- a/src/models/data/authorData.js +++ b/src/models/data/authorData.ts @@ -18,9 +18,10 @@ import {camelToSnake, formatDate, parseDate, snakeToCamel} from '../../util'; import {getEditionGroupsCreditedToAuthor, getEditionsCreditedToAuthor} from '../../func/author-credit'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function authorData(bookshelf) { +export default function authorData(bookshelf: Bookshelf) { const AuthorData = bookshelf.Model.extend({ aliasSet() { return this.belongsTo('AliasSet', 'alias_set_id'); diff --git a/src/models/data/editionData.js b/src/models/data/editionData.ts similarity index 94% rename from src/models/data/editionData.js rename to src/models/data/editionData.ts index 29010d05..73060d24 100644 --- a/src/models/data/editionData.js +++ b/src/models/data/editionData.ts @@ -18,9 +18,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionData(bookshelf) { +export default function editionData(bookshelf: Bookshelf) { const EditionData = bookshelf.Model.extend({ aliasSet() { return this.belongsTo('AliasSet', 'alias_set_id'); diff --git a/src/models/data/editionGroupData.js b/src/models/data/editionGroupData.ts similarity index 93% rename from src/models/data/editionGroupData.js rename to src/models/data/editionGroupData.ts index 57279bc3..c19b56f0 100644 --- a/src/models/data/editionGroupData.js +++ b/src/models/data/editionGroupData.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionGroupData(bookshelf) { +export default function editionGroupData(bookshelf: Bookshelf) { const EditionGroupData = bookshelf.Model.extend({ aliasSet() { return this.belongsTo('AliasSet', 'alias_set_id'); diff --git a/src/models/data/publisherData.js b/src/models/data/publisherData.ts similarity index 96% rename from src/models/data/publisherData.js rename to src/models/data/publisherData.ts index 4187ff45..21c85f95 100644 --- a/src/models/data/publisherData.js +++ b/src/models/data/publisherData.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, formatDate, parseDate, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function publisherData(bookshelf) { +export default function publisherData(bookshelf: Bookshelf) { const PublisherData = bookshelf.Model.extend({ aliasSet() { return this.belongsTo('AliasSet', 'alias_set_id'); diff --git a/src/models/data/seriesData.js b/src/models/data/seriesData.ts similarity index 93% rename from src/models/data/seriesData.js rename to src/models/data/seriesData.ts index c895944c..5f096dc6 100644 --- a/src/models/data/seriesData.js +++ b/src/models/data/seriesData.ts @@ -18,9 +18,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function seriesData(bookshelf) { +export default function seriesData(bookshelf: Bookshelf) { const SeriesData = bookshelf.Model.extend({ aliasSet() { return this.belongsTo('AliasSet', 'alias_set_id'); diff --git a/src/models/data/workData.js b/src/models/data/workData.ts similarity index 93% rename from src/models/data/workData.js rename to src/models/data/workData.ts index 18a0dc0a..204524ed 100644 --- a/src/models/data/workData.js +++ b/src/models/data/workData.ts @@ -18,9 +18,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function workData(bookshelf) { +export default function workData(bookshelf: Bookshelf) { const WorkData = bookshelf.Model.extend({ aliasSet() { return this.belongsTo('AliasSet', 'alias_set_id'); diff --git a/src/models/disambiguation.js b/src/models/disambiguation.ts similarity index 90% rename from src/models/disambiguation.js rename to src/models/disambiguation.ts index e2a772cb..a02d0b2a 100644 --- a/src/models/disambiguation.js +++ b/src/models/disambiguation.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function disambiguation(bookshelf) { +export default function disambiguation(bookshelf: Bookshelf) { const Disambiguation = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/editionFormat.js b/src/models/editionFormat.ts similarity index 90% rename from src/models/editionFormat.js rename to src/models/editionFormat.ts index b60efa02..b7030538 100644 --- a/src/models/editionFormat.js +++ b/src/models/editionFormat.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionFormat(bookshelf) { +export default function editionFormat(bookshelf: Bookshelf) { const EditionFormat = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/editionGroupType.js b/src/models/editionGroupType.ts similarity index 89% rename from src/models/editionGroupType.js rename to src/models/editionGroupType.ts index e5eeab42..95ff31ec 100644 --- a/src/models/editionGroupType.js +++ b/src/models/editionGroupType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionGroupType(bookshelf) { +export default function editionGroupType(bookshelf: Bookshelf) { const EditionGroupType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/editionStatus.js b/src/models/editionStatus.ts similarity index 90% rename from src/models/editionStatus.js rename to src/models/editionStatus.ts index 233c525e..91319813 100644 --- a/src/models/editionStatus.js +++ b/src/models/editionStatus.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionStatus(bookshelf) { +export default function editionStatus(bookshelf: Bookshelf) { const EditionStatus = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/editor.js b/src/models/editor.ts similarity index 94% rename from src/models/editor.js rename to src/models/editor.ts index 33e49937..8eacf5fe 100644 --- a/src/models/editor.js +++ b/src/models/editor.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editor(bookshelf) { +export default function editor(bookshelf: Bookshelf) { const Editor = bookshelf.Model.extend({ achievements() { return this.hasMany('AchievementType').through('AchievementUnlock'); diff --git a/src/models/editorEntityVisits.js b/src/models/editorEntityVisits.ts similarity index 90% rename from src/models/editorEntityVisits.js rename to src/models/editorEntityVisits.ts index 2bda327a..7b0e2b6c 100644 --- a/src/models/editorEntityVisits.js +++ b/src/models/editorEntityVisits.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editorEntityVisits(bookshelf) { +export default function editorEntityVisits(bookshelf: Bookshelf) { const EditorEntityVisits = bookshelf.Model.extend({ achievement() { return this.belongsTo('Entity', 'bbid'); diff --git a/src/models/editorType.js b/src/models/editorType.ts similarity index 90% rename from src/models/editorType.js rename to src/models/editorType.ts index 84084721..5fd929e4 100644 --- a/src/models/editorType.js +++ b/src/models/editorType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editorType(bookshelf) { +export default function editorType(bookshelf: Bookshelf) { const EditorType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/entities/author.js b/src/models/entities/author.ts similarity index 93% rename from src/models/entities/author.js rename to src/models/entities/author.ts index 0af11370..2b591de5 100644 --- a/src/models/entities/author.js +++ b/src/models/entities/author.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function author(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function author(bookshelf: Bookshelf) { const AuthorData = bookshelf.model('AuthorData'); const Author = AuthorData.extend({ diff --git a/src/models/entities/edition.js b/src/models/entities/edition.ts similarity index 86% rename from src/models/entities/edition.js rename to src/models/entities/edition.ts index dbf41f1a..c015b209 100644 --- a/src/models/entities/edition.js +++ b/src/models/entities/edition.ts @@ -16,17 +16,19 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + +import type Bookshelf from '@metabrainz/bookshelf'; import _ from 'lodash'; import {createEditionGroupForNewEdition} from '../../util'; /** - * @param {any} model The ORM model of the Edition being edited/created - * @param {any} bookshelf The BoolshelfJS ORM - * @param {any} options Query options — seuch as transacting object + * @param {any} model - The ORM model of the Edition being edited/created + * @param {Bookshelf} bookshelf - The BookshelfJS ORM + * @param {any} options - Query options such as transacting object * @description Automatically create and sets a new Edition Group * if there is none selected, in the same transaction */ -async function autoCreateNewEditionGroup(model, bookshelf, options) { +async function autoCreateNewEditionGroup(model, bookshelf: Bookshelf, options) { const aliasSetId = model.get('aliasSetId'); const revisionId = model.get('revisionId'); const authorCreditId = model.get('authorCreditId'); @@ -36,7 +38,7 @@ async function autoCreateNewEditionGroup(model, bookshelf, options) { model.set('editionGroupBbid', newEditionGroupBBID); } -export default function edition(bookshelf) { +export default function edition(bookshelf: Bookshelf) { const EditionData = bookshelf.model('EditionData'); const Edition = EditionData.extend({ diff --git a/src/models/entities/editionGroup.js b/src/models/entities/editionGroup.ts similarity index 93% rename from src/models/entities/editionGroup.js rename to src/models/entities/editionGroup.ts index 8e867936..84087ed6 100644 --- a/src/models/entities/editionGroup.js +++ b/src/models/entities/editionGroup.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function editionGroup(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function editionGroup(bookshelf: Bookshelf) { const EditionGroupData = bookshelf.model('EditionGroupData'); const EditionGroup = EditionGroupData.extend({ diff --git a/src/models/entities/publisher.js b/src/models/entities/publisher.ts similarity index 93% rename from src/models/entities/publisher.js rename to src/models/entities/publisher.ts index 643e9a7d..8a3d6db0 100644 --- a/src/models/entities/publisher.js +++ b/src/models/entities/publisher.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function publisher(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function publisher(bookshelf: Bookshelf) { const PublisherData = bookshelf.model('PublisherData'); const Publisher = PublisherData.extend({ diff --git a/src/models/entities/series.js b/src/models/entities/series.ts similarity index 93% rename from src/models/entities/series.js rename to src/models/entities/series.ts index 28c49ab7..6e034fdb 100644 --- a/src/models/entities/series.js +++ b/src/models/entities/series.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function series(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function series(bookshelf: Bookshelf) { const SeriesData = bookshelf.model('SeriesData'); const Series = SeriesData.extend({ diff --git a/src/models/entities/work.js b/src/models/entities/work.ts similarity index 93% rename from src/models/entities/work.js rename to src/models/entities/work.ts index 6f1f68cc..da9f95a6 100644 --- a/src/models/entities/work.js +++ b/src/models/entities/work.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function work(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function work(bookshelf: Bookshelf) { const WorkData = bookshelf.model('WorkData'); const Work = WorkData.extend({ diff --git a/src/models/entity.js b/src/models/entity.ts similarity index 91% rename from src/models/entity.js rename to src/models/entity.ts index c711bb7c..ab9f4ff2 100644 --- a/src/models/entity.js +++ b/src/models/entity.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function entity(bookshelf) { +export default function entity(bookshelf: Bookshelf) { const Entity = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/externalServiceOauth.js b/src/models/externalServiceOauth.ts similarity index 90% rename from src/models/externalServiceOauth.js rename to src/models/externalServiceOauth.ts index 3d1a53aa..80165a12 100644 --- a/src/models/externalServiceOauth.js +++ b/src/models/externalServiceOauth.ts @@ -18,9 +18,10 @@ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function externalServiceOauth(bookshelf) { +export default function externalServiceOauth(bookshelf: Bookshelf) { const ExternalServiceOauth = bookshelf.Model.extend({ editor() { return this.belongsTo('Editor', 'editor_id'); diff --git a/src/models/gender.js b/src/models/gender.ts similarity index 90% rename from src/models/gender.js rename to src/models/gender.ts index 1c9f3b7a..471dd82d 100644 --- a/src/models/gender.js +++ b/src/models/gender.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function gender(bookshelf) { +export default function gender(bookshelf: Bookshelf) { const Gender = bookshelf.Model.extend({ format: camelToSnake, parse: snakeToCamel, diff --git a/src/models/headers/authorHeader.js b/src/models/headers/authorHeader.ts similarity index 90% rename from src/models/headers/authorHeader.js rename to src/models/headers/authorHeader.ts index 5623eef9..566e7088 100644 --- a/src/models/headers/authorHeader.js +++ b/src/models/headers/authorHeader.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function authorHeader(bookshelf) { +export default function authorHeader(bookshelf: Bookshelf) { const AuthorHeader = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/headers/editionGroupHeader.js b/src/models/headers/editionGroupHeader.ts similarity index 89% rename from src/models/headers/editionGroupHeader.js rename to src/models/headers/editionGroupHeader.ts index a4514177..21562200 100644 --- a/src/models/headers/editionGroupHeader.js +++ b/src/models/headers/editionGroupHeader.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionGroupHeader(bookshelf) { +export default function editionGroupHeader(bookshelf: Bookshelf) { const EditionGroupHeader = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/headers/editionHeader.js b/src/models/headers/editionHeader.ts similarity index 90% rename from src/models/headers/editionHeader.js rename to src/models/headers/editionHeader.ts index 65659402..00fd9eb9 100644 --- a/src/models/headers/editionHeader.js +++ b/src/models/headers/editionHeader.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionHeader(bookshelf) { +export default function editionHeader(bookshelf: Bookshelf) { const EditionHeader = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/headers/publisherHeader.js b/src/models/headers/publisherHeader.ts similarity index 90% rename from src/models/headers/publisherHeader.js rename to src/models/headers/publisherHeader.ts index 06fd2a02..6e200a5f 100644 --- a/src/models/headers/publisherHeader.js +++ b/src/models/headers/publisherHeader.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function publisherHeader(bookshelf) { +export default function publisherHeader(bookshelf: Bookshelf) { const PublisherHeader = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/headers/seriesHeader.js b/src/models/headers/seriesHeader.ts similarity index 90% rename from src/models/headers/seriesHeader.js rename to src/models/headers/seriesHeader.ts index 047e480c..3a0e0f6b 100644 --- a/src/models/headers/seriesHeader.js +++ b/src/models/headers/seriesHeader.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function seriesHeader(bookshelf) { +export default function seriesHeader(bookshelf: Bookshelf) { const SeriesHeader = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/headers/workHeader.js b/src/models/headers/workHeader.ts similarity index 90% rename from src/models/headers/workHeader.js rename to src/models/headers/workHeader.ts index 4cfa4881..a079d1c3 100644 --- a/src/models/headers/workHeader.js +++ b/src/models/headers/workHeader.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function workHeader(bookshelf) { +export default function workHeader(bookshelf: Bookshelf) { const WorkHeader = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'bbid', diff --git a/src/models/identifier.js b/src/models/identifier.ts similarity index 91% rename from src/models/identifier.js rename to src/models/identifier.ts index 8f83a374..56c16550 100644 --- a/src/models/identifier.js +++ b/src/models/identifier.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function identifier(bookshelf) { +export default function identifier(bookshelf: Bookshelf) { const Identifier = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/identifierSet.js b/src/models/identifierSet.ts similarity index 91% rename from src/models/identifierSet.js rename to src/models/identifierSet.ts index e86e8a65..c3dbd4a1 100644 --- a/src/models/identifierSet.js +++ b/src/models/identifierSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function identifierSet(bookshelf) { +export default function identifierSet(bookshelf: Bookshelf) { const IdentifierSet = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/identifierType.js b/src/models/identifierType.ts similarity index 90% rename from src/models/identifierType.js rename to src/models/identifierType.ts index 7ed03f97..8e6c265b 100644 --- a/src/models/identifierType.js +++ b/src/models/identifierType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function identifierType(bookshelf) { +export default function identifierType(bookshelf: Bookshelf) { const IdentifierType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/imports/authorImport.js b/src/models/imports/authorImport.ts similarity index 90% rename from src/models/imports/authorImport.js rename to src/models/imports/authorImport.ts index 0da918b9..7a603671 100644 --- a/src/models/imports/authorImport.js +++ b/src/models/imports/authorImport.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function author(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function author(bookshelf: Bookshelf) { const AuthorData = bookshelf.model('AuthorData'); const AuthorImport = AuthorData.extend({ diff --git a/src/models/imports/editionGroupImport.js b/src/models/imports/editionGroupImport.ts similarity index 90% rename from src/models/imports/editionGroupImport.js rename to src/models/imports/editionGroupImport.ts index 3dc6622b..99c44e85 100644 --- a/src/models/imports/editionGroupImport.js +++ b/src/models/imports/editionGroupImport.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function editionGroup(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function editionGroup(bookshelf: Bookshelf) { const EditionGroupData = bookshelf.model('EditionGroupData'); const EditionGroupImport = EditionGroupData.extend({ diff --git a/src/models/imports/editionImport.js b/src/models/imports/editionImport.ts similarity index 90% rename from src/models/imports/editionImport.js rename to src/models/imports/editionImport.ts index 36ba7aae..11d8647c 100644 --- a/src/models/imports/editionImport.js +++ b/src/models/imports/editionImport.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function edition(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function edition(bookshelf: Bookshelf) { const EditionData = bookshelf.model('EditionData'); const EditionImport = EditionData.extend({ diff --git a/src/models/imports/publisherImport.js b/src/models/imports/publisherImport.ts similarity index 90% rename from src/models/imports/publisherImport.js rename to src/models/imports/publisherImport.ts index 55d947ed..0401c90a 100644 --- a/src/models/imports/publisherImport.js +++ b/src/models/imports/publisherImport.ts @@ -16,7 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function publisher(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function publisher(bookshelf: Bookshelf) { const PublisherData = bookshelf.model('PublisherData'); const PublisherImport = PublisherData.extend({ diff --git a/src/models/imports/workImport.js b/src/models/imports/workImport.ts similarity index 90% rename from src/models/imports/workImport.js rename to src/models/imports/workImport.ts index e4f69b1e..2cbf9461 100644 --- a/src/models/imports/workImport.js +++ b/src/models/imports/workImport.ts @@ -15,7 +15,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -export default function work(bookshelf) { +import type Bookshelf from '@metabrainz/bookshelf'; + + +export default function work(bookshelf: Bookshelf) { const WorkData = bookshelf.model('WorkData'); const WorkImport = WorkData.extend({ diff --git a/src/models/language.js b/src/models/language.ts similarity index 93% rename from src/models/language.js rename to src/models/language.ts index c599779f..ad29d931 100644 --- a/src/models/language.js +++ b/src/models/language.ts @@ -17,6 +17,7 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; import _ from 'lodash'; @@ -44,7 +45,7 @@ function parseWithISOFields(attrs) { )); } -export default function language(bookshelf) { +export default function language(bookshelf: Bookshelf) { const Language = bookshelf.Model.extend({ format: formatWithISOFields, idAttribute: 'id', diff --git a/src/models/languageSet.js b/src/models/languageSet.ts similarity index 91% rename from src/models/languageSet.js rename to src/models/languageSet.ts index 3addca9e..117f40b8 100644 --- a/src/models/languageSet.js +++ b/src/models/languageSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function languageSet(bookshelf) { +export default function languageSet(bookshelf: Bookshelf) { const LanguageSet = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/note.js b/src/models/note.ts similarity index 91% rename from src/models/note.js rename to src/models/note.ts index 4cebbf2d..72cd0faa 100644 --- a/src/models/note.js +++ b/src/models/note.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function note(bookshelf) { +export default function note(bookshelf: Bookshelf) { const Note = bookshelf.Model.extend({ author() { return this.belongsTo('Editor', 'author_id'); diff --git a/src/models/publisherSet.js b/src/models/publisherSet.ts similarity index 91% rename from src/models/publisherSet.js rename to src/models/publisherSet.ts index 774c8eb8..487cec99 100644 --- a/src/models/publisherSet.js +++ b/src/models/publisherSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function publisherSet(bookshelf) { +export default function publisherSet(bookshelf: Bookshelf) { const PublisherSet = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/publisherType.js b/src/models/publisherType.ts similarity index 90% rename from src/models/publisherType.js rename to src/models/publisherType.ts index 060f4dbf..e7eccfc6 100644 --- a/src/models/publisherType.js +++ b/src/models/publisherType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function publisherType(bookshelf) { +export default function publisherType(bookshelf: Bookshelf) { const PublisherType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/relationship.js b/src/models/relationship.ts similarity index 93% rename from src/models/relationship.js rename to src/models/relationship.ts index b9d0cf19..2fab3afe 100644 --- a/src/models/relationship.js +++ b/src/models/relationship.ts @@ -18,9 +18,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationship(bookshelf) { +export default function relationship(bookshelf: Bookshelf) { const Relationship = bookshelf.Model.extend({ attributeSet() { return this.belongsTo('RelationshipAttributeSet', 'attribute_set_id'); diff --git a/src/models/relationshipAttribute.js b/src/models/relationshipAttribute.ts similarity index 92% rename from src/models/relationshipAttribute.js rename to src/models/relationshipAttribute.ts index 63764beb..786c6db7 100644 --- a/src/models/relationshipAttribute.js +++ b/src/models/relationshipAttribute.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipAttribute(bookshelf) { +export default function relationshipAttribute(bookshelf: Bookshelf) { const RelationshipAttribute = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/relationshipAttributeSet.js b/src/models/relationshipAttributeSet.ts similarity index 91% rename from src/models/relationshipAttributeSet.js rename to src/models/relationshipAttributeSet.ts index 0c86d747..b887732b 100644 --- a/src/models/relationshipAttributeSet.js +++ b/src/models/relationshipAttributeSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipAttributeSet(bookshelf) { +export default function relationshipAttributeSet(bookshelf: Bookshelf) { const RelationshipAttributeSet = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/relationshipAttributeTextValue.js b/src/models/relationshipAttributeTextValue.ts similarity index 94% rename from src/models/relationshipAttributeTextValue.js rename to src/models/relationshipAttributeTextValue.ts index 25be40a6..f0627d6f 100644 --- a/src/models/relationshipAttributeTextValue.js +++ b/src/models/relationshipAttributeTextValue.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipAttributeTextValue(bookshelf) { +export default function relationshipAttributeTextValue(bookshelf: Bookshelf) { const RelationshipAttributeTextValue = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/relationshipAttributeType.js b/src/models/relationshipAttributeType.ts similarity index 89% rename from src/models/relationshipAttributeType.js rename to src/models/relationshipAttributeType.ts index 9183b7ce..cdac62ef 100644 --- a/src/models/relationshipAttributeType.js +++ b/src/models/relationshipAttributeType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipAttributeType(bookshelf) { +export default function relationshipAttributeType(bookshelf: Bookshelf) { const RelationshipAttributeType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/relationshipSet.js b/src/models/relationshipSet.ts similarity index 91% rename from src/models/relationshipSet.js rename to src/models/relationshipSet.ts index 21dedef6..2be14f59 100644 --- a/src/models/relationshipSet.js +++ b/src/models/relationshipSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipSet(bookshelf) { +export default function relationshipSet(bookshelf: Bookshelf) { const RelationshipSet = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/relationshipType.js b/src/models/relationshipType.ts similarity index 91% rename from src/models/relationshipType.js rename to src/models/relationshipType.ts index a94cb5b2..8b98d8a8 100644 --- a/src/models/relationshipType.js +++ b/src/models/relationshipType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipType(bookshelf) { +export default function relationshipType(bookshelf: Bookshelf) { const RelationshipType = bookshelf.Model.extend({ attributeTypes() { return this.belongsToMany('RelationshipAttributeType') diff --git a/src/models/relationshipTypeAttributeType.js b/src/models/relationshipTypeAttributeType.ts similarity index 89% rename from src/models/relationshipTypeAttributeType.js rename to src/models/relationshipTypeAttributeType.ts index 8c8ac6db..2b35e657 100644 --- a/src/models/relationshipTypeAttributeType.js +++ b/src/models/relationshipTypeAttributeType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function relationshipTypeAttributeType(bookshelf) { +export default function relationshipTypeAttributeType(bookshelf: Bookshelf) { const RelationshipTypeAttributeType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/releaseEvent.js b/src/models/releaseEvent.ts similarity index 92% rename from src/models/releaseEvent.js rename to src/models/releaseEvent.ts index 50fc4828..f1bbaa23 100644 --- a/src/models/releaseEvent.js +++ b/src/models/releaseEvent.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, formatDate, parseDate, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function releaseEvent(bookshelf) { +export default function releaseEvent(bookshelf: Bookshelf) { const ReleaseEvent = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/releaseEventSet.js b/src/models/releaseEventSet.ts similarity index 91% rename from src/models/releaseEventSet.js rename to src/models/releaseEventSet.ts index 087ab0a3..426a5491 100644 --- a/src/models/releaseEventSet.js +++ b/src/models/releaseEventSet.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function releaseEventSet(bookshelf) { +export default function releaseEventSet(bookshelf: Bookshelf) { const ReleaseEventSet = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/revision.js b/src/models/revision.ts similarity index 92% rename from src/models/revision.js rename to src/models/revision.ts index b1ac5045..87aca1b2 100644 --- a/src/models/revision.js +++ b/src/models/revision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function revision(bookshelf) { +export default function revision(bookshelf: Bookshelf) { const Revision = bookshelf.Model.extend({ author() { return this.belongsTo('Editor', 'author_id'); diff --git a/src/models/revisions/authorRevision.js b/src/models/revisions/authorRevision.ts similarity index 95% rename from src/models/revisions/authorRevision.js rename to src/models/revisions/authorRevision.ts index f3769b54..c6851a86 100644 --- a/src/models/revisions/authorRevision.js +++ b/src/models/revisions/authorRevision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, diffRevisions, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function authorRevision(bookshelf) { +export default function authorRevision(bookshelf: Bookshelf) { const AuthorRevision = bookshelf.Model.extend({ data() { return this.belongsTo('AuthorData', 'data_id'); diff --git a/src/models/revisions/editionGroupRevision.js b/src/models/revisions/editionGroupRevision.ts similarity index 94% rename from src/models/revisions/editionGroupRevision.js rename to src/models/revisions/editionGroupRevision.ts index b4b19ef3..e831c040 100644 --- a/src/models/revisions/editionGroupRevision.js +++ b/src/models/revisions/editionGroupRevision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, diffRevisions, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionGroupRevision(bookshelf) { +export default function editionGroupRevision(bookshelf: Bookshelf) { const EditionGroupRevision = bookshelf.Model.extend({ data() { return this.belongsTo('EditionGroupData', 'data_id'); diff --git a/src/models/revisions/editionRevision.js b/src/models/revisions/editionRevision.ts similarity index 95% rename from src/models/revisions/editionRevision.js rename to src/models/revisions/editionRevision.ts index 9cc8eba3..76bd5895 100644 --- a/src/models/revisions/editionRevision.js +++ b/src/models/revisions/editionRevision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, diffRevisions, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function editionRevision(bookshelf) { +export default function editionRevision(bookshelf: Bookshelf) { const EditionRevision = bookshelf.Model.extend({ data() { return this.belongsTo('EditionData', 'data_id'); diff --git a/src/models/revisions/publisherRevision.js b/src/models/revisions/publisherRevision.ts similarity index 94% rename from src/models/revisions/publisherRevision.js rename to src/models/revisions/publisherRevision.ts index 9a4399e1..6cef28e3 100644 --- a/src/models/revisions/publisherRevision.js +++ b/src/models/revisions/publisherRevision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, diffRevisions, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function publisherRevision(bookshelf) { +export default function publisherRevision(bookshelf: Bookshelf) { const PublisherRevision = bookshelf.Model.extend({ data() { return this.belongsTo('PublisherData', 'data_id'); diff --git a/src/models/revisions/seriesRevision.js b/src/models/revisions/seriesRevision.ts similarity index 94% rename from src/models/revisions/seriesRevision.js rename to src/models/revisions/seriesRevision.ts index 3458d3c4..f4234921 100644 --- a/src/models/revisions/seriesRevision.js +++ b/src/models/revisions/seriesRevision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, diffRevisions, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function seriesRevision(bookshelf) { +export default function seriesRevision(bookshelf: Bookshelf) { const SeriesRevision = bookshelf.Model.extend({ data() { return this.belongsTo('SeriesData', 'data_id'); diff --git a/src/models/revisions/workRevision.js b/src/models/revisions/workRevision.ts similarity index 95% rename from src/models/revisions/workRevision.js rename to src/models/revisions/workRevision.ts index 5d3861a5..67a02694 100644 --- a/src/models/revisions/workRevision.js +++ b/src/models/revisions/workRevision.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, diffRevisions, snakeToCamel} from '../../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function workRevision(bookshelf) { +export default function workRevision(bookshelf: Bookshelf) { const WorkRevision = bookshelf.Model.extend({ data() { return this.belongsTo('WorkData', 'data_id'); diff --git a/src/models/seriesOrderingType.js b/src/models/seriesOrderingType.ts similarity index 89% rename from src/models/seriesOrderingType.js rename to src/models/seriesOrderingType.ts index cbcb32e3..4e7d5012 100644 --- a/src/models/seriesOrderingType.js +++ b/src/models/seriesOrderingType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function seriesOrderingType(bookshelf) { +export default function seriesOrderingType(bookshelf: Bookshelf) { const SeriesOrderingType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/titleType.js b/src/models/titleType.ts similarity index 90% rename from src/models/titleType.js rename to src/models/titleType.ts index 849eddba..c67d8ed9 100644 --- a/src/models/titleType.js +++ b/src/models/titleType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function titleType(bookshelf) { +export default function titleType(bookshelf: Bookshelf) { const TitleType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/src/models/titleUnlock.js b/src/models/titleUnlock.ts similarity index 91% rename from src/models/titleUnlock.js rename to src/models/titleUnlock.ts index 3bc71730..282520da 100644 --- a/src/models/titleUnlock.js +++ b/src/models/titleUnlock.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function titleUnlock(bookshelf) { +export default function titleUnlock(bookshelf: Bookshelf) { const TitleUnlock = bookshelf.Model.extend({ editor() { return this.belongsTo('Editor', 'editor_id'); diff --git a/src/models/userCollection.js b/src/models/userCollection.ts similarity index 93% rename from src/models/userCollection.js rename to src/models/userCollection.ts index a2152c7f..924b42e8 100644 --- a/src/models/userCollection.js +++ b/src/models/userCollection.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function userCollection(bookshelf) { +export default function userCollection(bookshelf: Bookshelf) { const UserCollection = bookshelf.Model.extend({ collaborators() { return this.hasMany('UserCollectionCollaborator', 'collection_id'); diff --git a/src/models/userCollectionCollaborator.js b/src/models/userCollectionCollaborator.ts similarity index 90% rename from src/models/userCollectionCollaborator.js rename to src/models/userCollectionCollaborator.ts index e01eaf46..fced97db 100644 --- a/src/models/userCollectionCollaborator.js +++ b/src/models/userCollectionCollaborator.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function userCollectionCollaborator(bookshelf) { +export default function userCollectionCollaborator(bookshelf: Bookshelf) { const UserCollectionCollaborator = bookshelf.Model.extend({ collaborator() { return this.belongsTo('Editor', 'collaborator_id'); diff --git a/src/models/userCollectionItem.js b/src/models/userCollectionItem.ts similarity index 90% rename from src/models/userCollectionItem.js rename to src/models/userCollectionItem.ts index 34f267b9..7fddb274 100644 --- a/src/models/userCollectionItem.js +++ b/src/models/userCollectionItem.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function userCollectionItem(bookshelf) { +export default function userCollectionItem(bookshelf: Bookshelf) { const UserCollectionItem = bookshelf.Model.extend({ entity() { return this.belongsTo('Entity', 'bbid'); diff --git a/src/models/workType.js b/src/models/workType.ts similarity index 90% rename from src/models/workType.js rename to src/models/workType.ts index dd6f9704..b314dfef 100644 --- a/src/models/workType.js +++ b/src/models/workType.ts @@ -17,9 +17,10 @@ */ import {camelToSnake, snakeToCamel} from '../util'; +import type Bookshelf from '@metabrainz/bookshelf'; -export default function workType(bookshelf) { +export default function workType(bookshelf: Bookshelf) { const WorkType = bookshelf.Model.extend({ format: camelToSnake, idAttribute: 'id', diff --git a/test/testModule.js b/test/testModule.js index a967ad58..d473f1b9 100644 --- a/test/testModule.js +++ b/test/testModule.js @@ -29,7 +29,7 @@ const {expect} = chai; describe('Module', () => { it('should return one model for each file in the models directory', () => { - const modelsDirectory = path.join(__dirname, '../src/models'); + const modelsDirectory = path.join(__dirname, '../lib/models'); const modelFiles = glob.sync('**/*.js', {cwd: modelsDirectory}); const modelNames = modelFiles.map( (file) => _.upperFirst(path.basename(file, path.extname(file))) From 0335676df64b6f6312a2394679debf804c4fb14d Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 21:32:01 +0200 Subject: [PATCH 22/32] other(ts): use the vague `Bookshelf.ModelSubclass` type for set models --- src/func/set.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/func/set.ts b/src/func/set.ts index b309bf11..58d37f20 100644 --- a/src/func/set.ts +++ b/src/func/set.ts @@ -18,6 +18,7 @@ import * as _ from 'lodash'; import type {FormRelationshipAttributesT as RelationshipAttributeT, SetItemT, Transaction} from './types'; +import type Bookshelf from '@metabrainz/bookshelf'; import type {ORM} from '..'; /** @@ -113,8 +114,9 @@ export function getRemovedItems( export const removeItemsFromSet = getRemovedItems; +// TODO: unchangedItems: ItemWithIdT, addedItems: ItemT export async function createNewSetWithItems( - orm: ORM, transacting: Transaction, SetModel: any, + orm: ORM, transacting: Transaction, SetModel: Bookshelf.ModelSubclass, unchangedItems: Array, addedItems: Array, itemsAttribute: string, idAttribute = 'id' ): Promise { @@ -143,7 +145,7 @@ export async function createNewSetWithItems( return newSet; } export async function createNewRelationshipAttributeSetWithItems( - orm: ORM, transacting: Transaction, SetModel: any, + orm: ORM, transacting: Transaction, SetModel: Bookshelf.ModelSubclass, unchangedItems: Array, addedItems: Array, itemsAttribute: string, idAttribute = 'id' ): Promise { From b699ab0cc9f3afaab0059fc730e77afff85c7843 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Fri, 1 Sep 2023 22:03:43 +0200 Subject: [PATCH 23/32] refactor(models): replace `Model.forge()` with `new Model()` https://bookshelfjs.org/api.html#Model-static-forge > A simple helper function to instantiate a new Model without needing new. Using the constructor syntax avoids a few type errors with chained calls. --- src/func/author-credit.ts | 7 ++++--- src/func/create-entity.ts | 4 ++-- src/func/entity.ts | 4 ++-- src/func/imports/approve-import.ts | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/func/author-credit.ts b/src/func/author-credit.ts index 7b02e48d..92d2d0db 100644 --- a/src/func/author-credit.ts +++ b/src/func/author-credit.ts @@ -63,14 +63,15 @@ function findAuthorCredit( export async function fetchOrCreateCredit( orm: ORM, transacting: Transaction, authorCredit: Array ) { + const {AuthorCredit} = orm; const result = await findAuthorCredit(orm, transacting, authorCredit); if (result) { - return orm.AuthorCredit.forge({id: result.id}) - .fetch({transacting, withRelated: 'names'}); + return new AuthorCredit({id: result.id}) + .fetch({transacting, withRelated: ['names']}); } - const newCredit = await new orm.AuthorCredit( + const newCredit = await new AuthorCredit( {authorCount: authorCredit.length} ).save(null, {transacting}); diff --git a/src/func/create-entity.ts b/src/func/create-entity.ts index b5388524..1771994b 100644 --- a/src/func/create-entity.ts +++ b/src/func/create-entity.ts @@ -130,9 +130,9 @@ export async function createEntity({ revisionId: revisionRecord && revisionRecord.get('id') }, entitySets, additionalProps); - const model = getEntityModelByType(orm, entityType); + const Model = getEntityModelByType(orm, entityType); - const entityModel = await model.forge(propsToSet) + const entityModel = await new Model(propsToSet) .save(null, { method: 'insert', transacting diff --git a/src/func/entity.ts b/src/func/entity.ts index 47b3b174..ac01d865 100644 --- a/src/func/entity.ts +++ b/src/func/entity.ts @@ -202,8 +202,8 @@ export async function getEntity( // if bbid appears in entity_redirect table, use that bbid instead // Do a recursive search in case the redirected bbid also redirects, etc. const finalBBID = await recursivelyGetRedirectBBID(orm, bbid); - const model = getEntityModelByType(orm, entityType); - const entity = await model.forge({bbid: finalBBID}) + const Model = getEntityModelByType(orm, entityType); + const entity = await new Model({bbid: finalBBID}) .fetch({ require: true, withRelated: relations diff --git a/src/func/imports/approve-import.ts b/src/func/imports/approve-import.ts index 325a8e43..c55f84b0 100644 --- a/src/func/imports/approve-import.ts +++ b/src/func/imports/approve-import.ts @@ -81,9 +81,9 @@ export async function approveImport( revisionId: revisionRecord && revisionRecord.get('id') }, entitySets, additionalProps); - const model = getEntityModelByType(orm, entityType); + const Model = getEntityModelByType(orm, entityType); - const entityModel = await model.forge(propsToSet) + const entityModel = await new Model(propsToSet) .save(null, { method: 'insert', transacting From ee7e0625c37dd34789ec5a51a60deee4c009209c Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:44:35 +0200 Subject: [PATCH 24/32] chore: use consistent type import syntax Co-authored-by: Monkey Do --- src/func/entity-sets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/func/entity-sets.ts b/src/func/entity-sets.ts index 38877089..f6ba622b 100644 --- a/src/func/entity-sets.ts +++ b/src/func/entity-sets.ts @@ -24,7 +24,7 @@ import { createNewSetWithItems, getAddedItems, getComparisonFunc, getRemovedItems, getUnchangedItems } from './set'; -import {type EntitySetMetadataT} from './entity'; +import type {EntitySetMetadataT} from './entity'; import type {ORM} from '..'; From f9b5d4f965840aff14de31ab3731bca9b726fab1 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:41:50 +0200 Subject: [PATCH 25/32] refactor: rename FormAliasT to NewOrExistingAliasT --- src/func/alias.ts | 8 ++++---- src/func/types.ts | 4 ++-- src/types/aliases.ts | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/func/alias.ts b/src/func/alias.ts index 0a7c9c36..5410e7f7 100644 --- a/src/func/alias.ts +++ b/src/func/alias.ts @@ -18,7 +18,7 @@ */ import * as _ from 'lodash'; -import type {AliasWithDefaultT, FormAliasT} from '../types/aliases'; +import type {AliasWithDefaultT, NewOrExistingAliasT} from '../types/aliases'; import { createNewSetWithItems, getAddedItems, @@ -36,7 +36,7 @@ export async function updateAliasSet( oldDefaultAliasId: number | null | undefined, newSetItemsWithDefault: Array ) { - function comparisonFunc(obj: FormAliasT, other: FormAliasT) { + function comparisonFunc(obj: NewOrExistingAliasT, other: NewOrExistingAliasT) { return ( obj.name === other.name && obj.sortName === other.sortName && @@ -47,10 +47,10 @@ export async function updateAliasSet( const {AliasSet} = orm; - const newSetItems: Array = + const newSetItems: Array = newSetItemsWithDefault.map((item) => _.omit(item, 'default')); - const oldSetItems: Array = + const oldSetItems: Array = oldSet ? oldSet.related('aliases').toJSON() : []; if (_.isEmpty(oldSetItems) && _.isEmpty(newSetItems)) { diff --git a/src/func/types.ts b/src/func/types.ts index c762e3fa..57f5b7db 100644 --- a/src/func/types.ts +++ b/src/func/types.ts @@ -17,9 +17,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import type {FormAliasT} from '../types/aliases'; import type {IdentifierT} from '../types/identifiers'; import type {Knex} from 'knex'; +import type {NewOrExistingAliasT} from '../types/aliases'; export type Transaction = Knex.Transaction; @@ -58,7 +58,7 @@ export interface FormReleaseEventT { // TODO: Although FormRelationshipAttributesT is missing from this type, createNewRelationshipAttributeSetWithItems // (which requires SetItemT inputs) causes no type errors? export type SetItemT = - FormAliasT | IdentifierT | FormLanguageT | FormRelationshipT | + NewOrExistingAliasT | IdentifierT | FormLanguageT | FormRelationshipT | FormPublisherT | FormReleaseEventT; export interface AuthorCreditNameT { diff --git a/src/types/aliases.ts b/src/types/aliases.ts index ed9aed0d..cc47eaaf 100644 --- a/src/types/aliases.ts +++ b/src/types/aliases.ts @@ -33,12 +33,11 @@ export type LazyLoadedAliasT = AliasWithIdT & LazyLoaded<{ language: LanguageWithIdT, }>; -// TODO: find a better name -export type FormAliasT = AliasT & { +export type NewOrExistingAliasT = AliasT & { id?: number; }; -export type AliasWithDefaultT = FormAliasT & { +export type AliasWithDefaultT = NewOrExistingAliasT & { default?: boolean, }; From 5a519ec9e7e166e97833e9ed987c646da5b73139 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:11:19 +0200 Subject: [PATCH 26/32] refactor(util): make date number parsing better readable Co-authored-by: Monkey Do --- src/util.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util.ts b/src/util.ts index 38dc913f..150ee8ee 100644 --- a/src/util.ts +++ b/src/util.ts @@ -172,20 +172,20 @@ const DAY_STR_LENGTH = 2; * @returns {string} The provided date formatted as an ISO 8601-2004 year or calendar date. */ export function formatDate(year: number, month?: number, day?: number): string { - if ((!year || isNaN(parseInt(year as any, 10))) && year !== 0) { + if ((!year || isNaN(_.toNumber(year))) && year !== 0) { return null; } const isCommonEraDate = Math.sign(year) === 1 || Math.sign(year) === 0; // eslint-disable-next-line max-len const yearString = `${isCommonEraDate ? '+' : '-'}${_.padStart(Math.abs(year).toString(), YEAR_STR_LENGTH, '0')}`; - if (!month || isNaN(parseInt(month as any, 10))) { + if (!month || isNaN(_.toNumber(month))) { return `${yearString}`; } const monthString = _.padStart(month.toString(), MONTH_STR_LENGTH, '0'); - if (!day || isNaN(parseInt(day as any, 10))) { + if (!day || isNaN(_.toNumber(day))) { return `${yearString}-${monthString}`; } @@ -197,7 +197,7 @@ export function formatDate(year: number, month?: number, day?: number): string { /** * Split ISO 8601 calendar dates or years into a numerical array. * @param {string} date - A date of the format 'YYYY', 'YYYY-MM', or 'YYYY-MM-DD'. - * @returns {number[]} Year, month, and day of month respectively. + * @returns {Array} - Year, month, and day of month respectively. */ export function parseDate(date: string): Array { if (!date) { From 10f9b55806c65c66f55061400fe2427937b63502 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:26:17 +0200 Subject: [PATCH 27/32] fix(ts): call instance method instead of class method Co-authored-by: Monkey Do --- src/models/data/publisherData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/data/publisherData.ts b/src/models/data/publisherData.ts index 21c85f95..14c89b3f 100644 --- a/src/models/data/publisherData.ts +++ b/src/models/data/publisherData.ts @@ -37,7 +37,7 @@ export default function publisherData(bookshelf: Bookshelf) { editions(options) { const Edition = bookshelf.model('Edition'); const bbid = this.get('bbid'); - return Edition.query((qb) => { + return new Edition().query((qb) => { qb .leftJoin( 'bookbrainz.publisher_set', From 7843a11389cda5aa3ae9f647738bfc9721979621 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:42:22 +0200 Subject: [PATCH 28/32] other(ts): expect errors for bookshelf's non-standard class inheritance --- src/models/revisions/authorRevision.ts | 2 ++ src/models/revisions/editionGroupRevision.ts | 2 ++ src/models/revisions/editionRevision.ts | 2 ++ src/models/revisions/publisherRevision.ts | 2 ++ src/models/revisions/seriesRevision.ts | 2 ++ src/models/revisions/workRevision.ts | 2 ++ 6 files changed, 12 insertions(+) diff --git a/src/models/revisions/authorRevision.ts b/src/models/revisions/authorRevision.ts index c6851a86..2815a490 100644 --- a/src/models/revisions/authorRevision.ts +++ b/src/models/revisions/authorRevision.ts @@ -50,6 +50,8 @@ export default function authorRevision(bookshelf: Bookshelf) { return null; } + // @ts-expect-error - The bookshelf type declarations do not support our models which do not use ES + // class inheritance. So we either have to adapt all models or switch to a different ORM (BB-729). return new AuthorRevision() .where('bbid', this.get('bbid')) .query('whereIn', 'id', parentIds) diff --git a/src/models/revisions/editionGroupRevision.ts b/src/models/revisions/editionGroupRevision.ts index e831c040..9b54e5ec 100644 --- a/src/models/revisions/editionGroupRevision.ts +++ b/src/models/revisions/editionGroupRevision.ts @@ -50,6 +50,8 @@ export default function editionGroupRevision(bookshelf: Bookshelf) { return null; } + // @ts-expect-error - The bookshelf type declarations do not support our models which do not use ES + // class inheritance. So we either have to adapt all models or switch to a different ORM (BB-729). return new EditionGroupRevision() .where('bbid', this.get('bbid')) .query('whereIn', 'id', parentIds) diff --git a/src/models/revisions/editionRevision.ts b/src/models/revisions/editionRevision.ts index 76bd5895..7690e3bc 100644 --- a/src/models/revisions/editionRevision.ts +++ b/src/models/revisions/editionRevision.ts @@ -52,6 +52,8 @@ export default function editionRevision(bookshelf: Bookshelf) { return null; } + // @ts-expect-error - The bookshelf type declarations do not support our models which do not use ES + // class inheritance. So we either have to adapt all models or switch to a different ORM (BB-729). return new EditionRevision() .where('bbid', this.get('bbid')) .query('whereIn', 'id', parentIds) diff --git a/src/models/revisions/publisherRevision.ts b/src/models/revisions/publisherRevision.ts index 6cef28e3..8e7357b7 100644 --- a/src/models/revisions/publisherRevision.ts +++ b/src/models/revisions/publisherRevision.ts @@ -50,6 +50,8 @@ export default function publisherRevision(bookshelf: Bookshelf) { return null; } + // @ts-expect-error - The bookshelf type declarations do not support our models which do not use ES + // class inheritance. So we either have to adapt all models or switch to a different ORM (BB-729). return new PublisherRevision() .where('bbid', this.get('bbid')) .query('whereIn', 'id', parentIds) diff --git a/src/models/revisions/seriesRevision.ts b/src/models/revisions/seriesRevision.ts index f4234921..7a21d002 100644 --- a/src/models/revisions/seriesRevision.ts +++ b/src/models/revisions/seriesRevision.ts @@ -54,6 +54,8 @@ export default function seriesRevision(bookshelf: Bookshelf) { return null; } + // @ts-expect-error - The bookshelf type declarations do not support our models which do not use ES + // class inheritance. So we either have to adapt all models or switch to a different ORM (BB-729). return new SeriesRevision() .where('bbid', this.get('bbid')) .query('whereIn', 'id', parentIds) diff --git a/src/models/revisions/workRevision.ts b/src/models/revisions/workRevision.ts index 67a02694..6ae860d3 100644 --- a/src/models/revisions/workRevision.ts +++ b/src/models/revisions/workRevision.ts @@ -50,6 +50,8 @@ export default function workRevision(bookshelf: Bookshelf) { return null; } + // @ts-expect-error - The bookshelf type declarations do not support our models which do not use ES + // class inheritance. So we either have to adapt all models or switch to a different ORM (BB-729). return new WorkRevision() .where('bbid', this.get('bbid')) .query('whereIn', 'id', parentIds) From dfc71e4ff6b154fc0a51e443c3738af4ee8b94fd Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Thu, 21 Sep 2023 23:24:52 +0200 Subject: [PATCH 29/32] other(ts): use parsed entity types in more places and enhance them --- src/func/create-entity.ts | 1 + src/func/entity.ts | 12 +++++------ src/types/parser.ts | 42 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/func/create-entity.ts b/src/func/create-entity.ts index 1771994b..e50e392c 100644 --- a/src/func/create-entity.ts +++ b/src/func/create-entity.ts @@ -58,6 +58,7 @@ interface CreateEntityPropsType { entityType: EntityTypeString } +// TODO: function seems to be unused across all BB repos, ignore its errors (and delete it?) export async function createEntity({ editorId, entityData, orm, transacting }: CreateEntityPropsType) { diff --git a/src/func/entity.ts b/src/func/entity.ts index ac01d865..dbad68e8 100644 --- a/src/func/entity.ts +++ b/src/func/entity.ts @@ -19,6 +19,7 @@ */ import * as _ from 'lodash'; +import type {ParsedAuthor, ParsedEdition, ParsedEntity, ParsedPublisher, ParsedSeries} from '../types/parser'; import type {AliasWithIdT} from '../types/aliases'; import type {EntityTypeString} from '../types/entity'; import type {ORM} from '..'; @@ -32,13 +33,12 @@ import {parseDate} from '../util'; * @returns {Object} - Returns all the additional entity specific data */ export function getAdditionalEntityProps( - // TODO: `entityData` should have the type ParsedEntity later which is currently causing lots of type issues. - entityData: Record, entityType: EntityTypeString + entityData: ParsedEntity, entityType: EntityTypeString ) { switch (entityType) { case 'Author': { const {typeId, genderId, beginAreaId, beginDate, endDate, - ended, endAreaId} = entityData; + ended, endAreaId} = entityData as ParsedAuthor; const [beginYear, beginMonth, beginDay] = parseDate(beginDate); const [endYear, endMonth, endDay] = parseDate(endDate); @@ -50,13 +50,13 @@ export function getAdditionalEntityProps( } case 'Edition': - return _.pick(entityData, [ + return _.pick(entityData as ParsedEdition, [ 'editionGroupBbid', 'width', 'height', 'depth', 'weight', 'pages', 'formatId', 'statusId' ]); case 'Publisher': { - const {typeId, areaId, beginDate, endDate, ended} = entityData; + const {typeId, areaId, beginDate, endDate, ended} = entityData as ParsedPublisher; const [beginYear, beginMonth, beginDay] = parseDate(beginDate); const [endYear, endMonth, endDay] = parseDate(endDate); @@ -72,7 +72,7 @@ export function getAdditionalEntityProps( return _.pick(entityData, ['typeId']); case 'Series': - return _.pick(entityData, ['entityType', 'orderingTypeId']); + return _.pick(entityData as ParsedSeries, ['entityType', 'orderingTypeId']); default: return null; diff --git a/src/types/parser.ts b/src/types/parser.ts index bfeaa617..12b9221a 100644 --- a/src/types/parser.ts +++ b/src/types/parser.ts @@ -28,6 +28,7 @@ import {IdentifierT} from './identifiers'; type ParsedBaseEntity = { + // TODO: Stop using this field to store the type of the parsed entity as Series uses this for the type of its items! entityType: EntityTypeString; // TODO: rename array property to `aliases`, also for consistency with e.g. `EntityDataType` alias: AliasWithDefaultT[]; @@ -56,17 +57,54 @@ type ParsedBaseEntity = { export type ParsedAuthor = ParsedBaseEntity & { beginDate?: string; endDate?: string; - type?: 'Person'; + ended: boolean; + type?: string; + typeId?: number; + genderId?: number; + beginAreaId?: number; + endAreaId?: number; }; export type ParsedEdition = ParsedBaseEntity & { + editionGroupBbid: string; + width?: number; + height?: number; + depth?: number; + weight?: number; + pages?: number; + formatId?: number; + statusId?: number; languages?: FormLanguageT[]; releaseEvents?: FormReleaseEventT[]; }; -export type ParsedWork = ParsedBaseEntity; +export type ParsedEditionGroup = ParsedBaseEntity & { + typeId?: number; +}; + +export type ParsedPublisher = ParsedBaseEntity & { + typeId?: number; + areaId?: number; + beginDate?: string; + endDate?: string; + ended: boolean; +}; + +export type ParsedSeries = ParsedBaseEntity & { + + /** Type of the items in the series. */ + entityType: EntityTypeString; + orderingTypeId: number; +}; + +export type ParsedWork = ParsedBaseEntity & { + typeId?: number; +}; export type ParsedEntity = | ParsedAuthor | ParsedEdition + | ParsedEditionGroup + | ParsedPublisher + | ParsedSeries | ParsedWork; From 1e155abdc91446b02abd0638f19d09d68689b5f9 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:07:16 +0200 Subject: [PATCH 30/32] fix(import): avoid conflicting usage of entityType by using QueuedEntity Type was copied from bookbrainz-utils, where it has been removed. --- src/func/imports/create-import.ts | 16 ++++++++-------- src/types/parser.ts | 11 +++++++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/func/imports/create-import.ts b/src/func/imports/create-import.ts index 8f93d4e5..21073c03 100644 --- a/src/func/imports/create-import.ts +++ b/src/func/imports/create-import.ts @@ -18,7 +18,7 @@ import {ENTITY_TYPES, type EntityTypeString} from '../../types/entity'; -import type {ParsedEdition, ParsedEntity} from '../../types/parser'; +import type {ParsedEdition, ParsedEntity, QueuedEntity} from '../../types/parser'; import type {ORM} from '../..'; import type {Transaction} from '../types'; @@ -41,7 +41,7 @@ function createLinkTableRecord(transacting: Transaction, record) { return transacting.insert(record).into('bookbrainz.link_import'); } -function createImportDataRecord(transacting: Transaction, dataSets, importData: ParsedEntity) { +function createImportDataRecord(transacting: Transaction, dataSets, importData: QueuedEntity) { const {entityType} = importData; // Safe check if entityType is one among the expected @@ -56,7 +56,7 @@ function createImportDataRecord(transacting: Transaction, dataSets, importData: and not directly dates, so we omit them. */ const additionalEntityProps = _.omit( - getAdditionalEntityProps(importData, entityType), + getAdditionalEntityProps(importData.data, entityType), ['beginDate', 'endDate'] ); @@ -112,17 +112,17 @@ async function updateEntityDataSets( return entityDataSet; } -export function createImport(orm: ORM, importData: ParsedEntity) { +export function createImport(orm: ORM, importData: QueuedEntity) { return orm.bookshelf.transaction(async (transacting) => { - const {alias, identifiers, disambiguation, entityType, source} = - importData; + const {entityType} = importData; + const {alias, identifiers, disambiguation, source} = importData.data; const [aliasSet, identifierSet, disambiguationObj, entityDataSets] = await Promise.all([ updateAliasSet(orm, transacting, null, null, alias), updateIdentifierSet(orm, transacting, null, identifiers), updateDisambiguation(orm, transacting, null, disambiguation), - updateEntityDataSets(orm, transacting, importData) + updateEntityDataSets(orm, transacting, importData.data) ]); // Create entityTypedataId @@ -165,7 +165,7 @@ export function createImport(orm: ORM, importData: ParsedEntity) { const linkTableData = camelToSnake({ importId, - importMetadata: importData.metadata, + importMetadata: importData.data.metadata, lastEdited: importData.lastEdited, originId: importData.originId, originSourceId diff --git a/src/types/parser.ts b/src/types/parser.ts index 12b9221a..9fcf38f4 100644 --- a/src/types/parser.ts +++ b/src/types/parser.ts @@ -28,8 +28,6 @@ import {IdentifierT} from './identifiers'; type ParsedBaseEntity = { - // TODO: Stop using this field to store the type of the parsed entity as Series uses this for the type of its items! - entityType: EntityTypeString; // TODO: rename array property to `aliases`, also for consistency with e.g. `EntityDataType` alias: AliasWithDefaultT[]; annotation?: string; @@ -108,3 +106,12 @@ export type ParsedEntity = | ParsedPublisher | ParsedSeries | ParsedWork; + +// TODO: drop redundant properties which are present in `data` and at the top level +export type QueuedEntity = { + data: ParsedEntity; + entityType: EntityTypeString; + source: string; + lastEdited?: string; + originId?: string; +}; From cdf0dcc843fa9f8272f0402cd693d780a6afd981 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:40:20 +0200 Subject: [PATCH 31/32] other(ts): create type definitions for import tables --- src/func/imports/create-import.ts | 7 +-- src/types/imports.ts | 74 +++++++++++++++++++++++++++++++ src/types/parser.ts | 16 +------ 3 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 src/types/imports.ts diff --git a/src/func/imports/create-import.ts b/src/func/imports/create-import.ts index 21073c03..7f782b74 100644 --- a/src/func/imports/create-import.ts +++ b/src/func/imports/create-import.ts @@ -18,6 +18,7 @@ import {ENTITY_TYPES, type EntityTypeString} from '../../types/entity'; +import type {ImportMetadataT, _ImportMetadataT, _ImportT} from '../../types/imports'; import type {ParsedEdition, ParsedEntity, QueuedEntity} from '../../types/parser'; import type {ORM} from '../..'; @@ -33,11 +34,11 @@ import {updateLanguageSet} from '../language'; import {updateReleaseEventSet} from '../releaseEvent'; -function createImportRecord(transacting: Transaction, data) { +function createImportRecord(transacting: Transaction, data: _ImportT[]) { return transacting.insert(data).into('bookbrainz.import').returning('id'); } -function createLinkTableRecord(transacting: Transaction, record) { +function createLinkTableRecord(transacting: Transaction, record: _ImportMetadataT[]) { return transacting.insert(record).into('bookbrainz.link_import'); } @@ -163,7 +164,7 @@ export function createImport(orm: ORM, importData: QueuedEntity) { throw new Error(`Error during getting source id - ${err}`); } - const linkTableData = camelToSnake({ + const linkTableData = camelToSnake<_ImportMetadataT, ImportMetadataT>({ importId, importMetadata: importData.data.metadata, lastEdited: importData.lastEdited, diff --git a/src/types/imports.ts b/src/types/imports.ts new file mode 100644 index 00000000..2b67505f --- /dev/null +++ b/src/types/imports.ts @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 David Kellner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import {EntityTypeString} from './entity'; +import {IdentifierT} from './identifiers'; +import {WithId} from './utils'; + + +// TODO: Drop type once we merge the `import` table into the `entity` table +export type _ImportT = { + type: EntityTypeString; +}; + +export type _ImportWithIdT = WithId<_ImportT>; + +export type AdditionalImportDataT = { + identifiers?: IdentifierT[]; + links: Array<{ + title: string; + url: string; + }>; + // TODO: find correct type in OL samples + originId?: object[]; + relationships: Array<{ + type: string; + value: string; + }>; + [custom: string]: any; +}; + +/** Type for the `link_import` table, which should be renamed for clarity (TODO). */ +export type ImportMetadataT = { + importId: number; + originSourceId: number; + originId: string; + + /** TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT timezone('UTC'::TEXT, now()) */ + importedAt?: string; + + /** TIMESTAMP WITHOUT TIME ZONE */ + lastEdited: string; + + /** UUID */ + entityId?: string; + + /** JSONB */ + importMetadata: AdditionalImportDataT; +}; + +/** Snake case variant of `ImportMetadataT`. */ +export type _ImportMetadataT = { + import_id: number; + origin_source_id: number; + origin_id: string; + imported_at?: any; + last_edited: any; + entity_id?: string; + import_metadata: any; +}; diff --git a/src/types/parser.ts b/src/types/parser.ts index 9fcf38f4..3d7f53df 100644 --- a/src/types/parser.ts +++ b/src/types/parser.ts @@ -22,6 +22,7 @@ */ import {FormLanguageT, FormReleaseEventT} from '../func/types'; +import {AdditionalImportDataT} from './imports'; import {AliasWithDefaultT} from './aliases'; import {EntityTypeString} from './entity'; import {IdentifierT} from './identifiers'; @@ -33,20 +34,7 @@ type ParsedBaseEntity = { annotation?: string; disambiguation?: string; identifiers: IdentifierT[]; - metadata: { - identifiers?: IdentifierT[]; - links: Array<{ - title: string; - url: string; - }>; - // TODO: find correct type in OL samples - originId?: object[]; - relationships: Array<{ - type: string; - value: string; - }>; - [custom: string]: any; - }; + metadata: AdditionalImportDataT; source: string; lastEdited?: string; originId?: string; From d0c965860237355b51fc038b0482f3238da04c93 Mon Sep 17 00:00:00 2001 From: David Kellner <52860029+kellnerd@users.noreply.github.com> Date: Mon, 16 Oct 2023 17:07:50 +0200 Subject: [PATCH 32/32] fix(import): properly extract id from knex query result Finally the first test import of a random OpenLibrary author succeeded! --- src/func/imports/create-import.ts | 14 ++++++++------ src/func/imports/misc.ts | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/func/imports/create-import.ts b/src/func/imports/create-import.ts index 7f782b74..8caf0d57 100644 --- a/src/func/imports/create-import.ts +++ b/src/func/imports/create-import.ts @@ -127,9 +127,9 @@ export function createImport(orm: ORM, importData: QueuedEntity) { ]); // Create entityTypedataId - let dataId = null; + let dataId: number = null; try { - [dataId] = await createImportDataRecord( + const [idObj] = await createImportDataRecord( transacting, camelToSnake({ aliasSetId: aliasSet && aliasSet.get('id'), @@ -139,28 +139,30 @@ export function createImport(orm: ORM, importData: QueuedEntity) { }), importData ); + dataId = _.get(idObj, 'id'); } catch (err) { throw new Error(`Error during dataId creation ${err}`); } // Create import entity - let importId = null; + let importId: number = null; try { - [importId] = - await createImportRecord(transacting, [{type: entityType}]); + const [idObj] = await createImportRecord(transacting, [{type: entityType}]); + importId = _.get(idObj, 'id'); } catch (err) { throw new Error(`Error during creation of importId ${err}`); } // Get origin_source - let originSourceId = null; + let originSourceId: number = null; try { originSourceId = await getOriginSourceId(transacting, source); } catch (err) { + // TODO: useless, we are only catching our self-thrown errors here throw new Error(`Error during getting source id - ${err}`); } diff --git a/src/func/imports/misc.ts b/src/func/imports/misc.ts index 606c6bbc..107be615 100644 --- a/src/func/imports/misc.ts +++ b/src/func/imports/misc.ts @@ -57,9 +57,10 @@ export async function getOriginSourceId( // Create the data source if it does not exist if (!originSourceId) { try { - [originSourceId] = await transacting.insert([{name: source}]) + const [idObj] = await transacting.insert([{name: source}]) .into('bookbrainz.origin_source') .returning('id'); + originSourceId = _.get(idObj, 'id'); } catch (err) { // Should error loudly if anything goes wrong