import { DeepPartial } from '@ulangi/extended-types' ; import { Definition } from '@ulangi/ulangi-common/interfaces' ; import * as knex from 'knex' ; import * as _ from 'lodash' ; import { DefinitionRowConverter } from '../converters/DefinitionRowConverter' ; import { TableName } from '../enums/TableName' ; import { DefinitionRowForInsert } from '../interfaces/DefinitionRow' ; import { DefinitionRowPreparer } from '../preparers/DefinitionRowPreparer' ; import { DefinitionRowResolver } from '../resolvers/DefinitionRowResolver' ; import { promisifyQuery } from '../utils/promisifyQuery' ; export class DefinitionModel { private definitionRowPreparer = new DefinitionRowPreparer ( ) ; private definitionRowResolver = new DefinitionRowResolver ( ) ; private definitionRowConverter = new DefinitionRowConverter ( ) ; public getDefinitionsPerVocabularyId ( db , userId , vocabularyIds , stripUnknown ) { return new Promise ( async ( resolve , reject ) : Promise < void > => { try { const result = await promisifyQuery ( db . select ( ) . from ( TableName . DEFINITION ) . where ( 'userId' , userId ) . whereIn ( 'vocabularyId' , vocabularyIds . slice ( ) ) ) ; const definitionRows = this . definitionRowResolver . resolveArray ( result , stripUnknown ) ; const definitionRowsPerVocabularyId = _ . groupBy ( definitionRows , ( definitionRow ) : string => definitionRow . vocabularyId ) ; const definitionsPerVocabularyId = _ . mapValues ( definitionRowsPerVocabularyId , ( definitionRows ) : Definition [ ] => { return definitionRows . map ( ( definitionRow ) : Definition => this . definitionRowConverter . convertToDefinition ( definitionRow ) ) ; } ) ; resolve ( { definitionsPerVocabularyId } ) ; } catch ( error ) { reject ( error ) ; } } ) ; } public upsertDefinitions ( db , userId , definitionVocabularyIdPairs ) { return new Promise ( async ( resolve , reject ) : Promise < void > => { try { const queries = [ ] ; queries . push ( this . insertOrIgnoreDefinitions ( db , userId , definitionVocabularyIdPairs . filter ( ( pair ) : pair is [ Definition , string ] => { const [ definition , vocabularyId ] = pair ; return this . definitionRowPreparer . canPrepareInsert ( userId , definition , vocabularyId ) ; } ) ) ) ; queries . push ( this . updateDefinitions ( db , userId , definitionVocabularyIdPairs ) ) ; await Promise . all ( queries ) ; resolve ( ) ; } catch ( error ) { reject ( error ) ; } } ) ; } private insertOrIgnoreDefinitions ( db , userId , definitionVocabularyIdPairs ) { return new Promise ( async ( resolve , reject ) : Promise < void > => { try { const queries = [ ] ; if ( definitionVocabularyIdPairs . length > 0 ) { const definitionRows = definitionVocabularyIdPairs . map ( ( [ definition , vocabularyId ] ) : DefinitionRowForInsert => { return this . definitionRowPreparer . prepareInsert ( userId , definition , vocabularyId ) ; } ) ; const { sql , bindings } = db . insert ( definitionRows ) . into ( TableName . DEFINITION ) . toSQL ( ) ; queries . push ( promisifyQuery ( db . raw ( sql . replace ( 'insert' , 'insert ignore' ) , bindings ) ) ) ; } await Promise . all ( queries ) ; resolve ( ) ; } catch ( error ) { reject ( error ) ; } } ) ; } private updateDefinitions ( db , userId , definitionVocabularyIdPairs ) { return Promise . all ( definitionVocabularyIdPairs . map ( ( [ definition , vocabularyId ] ) : Promise < void > => { return this . updateDefinition ( db , userId , definition , vocabularyId ) ; } ) ) ; } private updateDefinition ( db , userId , definition , vocabularyId ) { return new Promise ( async ( resolve , reject ) : Promise < void > => { try { const queries = [ ] ; const definitionRow = this . definitionRowPreparer . prepareUpdate ( userId , definition , vocabularyId ) ; const definitionId = definitionRow . definitionId ; const updateFields = _ . omit ( definitionRow , [ 'userId' , 'definitionId' , ] ) ; if ( ! _ . isEmpty ( updateFields ) ) { queries . push ( promisifyQuery ( db . update ( updateFields ) . table ( TableName . DEFINITION ) . where ( { userId , definitionId , } ) ) ) ; } await Promise . all ( queries ) ; resolve ( ) ; } catch ( error ) { reject ( error ) ; } } ) ; } }