Newer
Older
import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client'
import { SCHEMAS_DOCTYPE } from 'doctypes'
import { Schema } from 'models'
import {
MIGRATION_RESULT_COMPLETE,
MIGRATION_RESULT_FAILED,
MIGRATION_RESULT_NOOP,
SCHEMA_INITIAL_VERSION,
} from './migration.data'
import {
Migration,
MigrationQueryOptions,
MigrationResult,
} from './migration.type'
function migrationNoop(): MigrationResult {
return { type: MIGRATION_RESULT_NOOP, errors: [] }
}
/**
* Return schema version
* @param _client cozyClient
* @returns Promise<number> Version number of schema
*/
async function currentSchemaVersion(_client: Client): Promise<number> {
const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1))
return data.data[0].version
}
/**
* Retrieve all documents of a given doctype
* @param _client cozyClient
* @returns all documents of given doctype
*/

Hugo NOUTS
committed
async function getDocs(
_client: Client,
doctype: string,
options?: MigrationQueryOptions
): Promise<any> {
let query: QueryDefinition
if (options?.scope === 'conso') {

Hugo NOUTS
committed
query = Q(doctype)
.where({})
.indexFields(['year', 'month', 'day'])
.sortBy([{ year: 'desc' }, { month: 'desc' }, { day: 'desc' }])
.limitBy(options.limit)
} else {
query = Q(doctype)
}
const data: QueryResult<any[]> = await _client.query(query)
return data.data
}
/**
* Update schema version
*/
async function updateSchemaVersion(
_client: Client,
targetSchemaVersion: number
): Promise<void> {
const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1))
const doc = data.data[0]
doc.version = targetSchemaVersion
await _client.save(doc)
}
/**
* Save updated docs
* @returns Promise<MigrationResult>
*/
// eslint-disable-next-line @typescript-eslint/require-await
async function save(_client: Client, docs: any[]): Promise<MigrationResult> {
if (doc.deleteAction) {
await _client.destroy(doc)
} else if (doc.createAction) {
await _client.create(doc.doctype, doc)
} else {
await _client.save(doc)
}
migrationResult.type = migrationResult.errors.length
? MIGRATION_RESULT_FAILED
: MIGRATION_RESULT_COMPLETE
}
return migrationResult
}
/**
* Does schema doctype exist
* @param _client cozyClient
* @returns Promise<number> Version number of schema
*/
const schemaExist = async (_client: Client): Promise<boolean> => {
const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1))
return data.data.length > 0 ? true : false
}
export const initSchemaDoctype = async (
_client: Client,
targetVersion = SCHEMA_INITIAL_VERSION
) => {
logApp.info(`[Migration] Init schema doctype to version ${targetVersion}`)
})
}
/**
* Run migration
*/
export async function migrate(
migration: Migration,
_client: Client
): Promise<MigrationResult> {
if (migration.isEmpty) {
updateSchemaVersion(_client, migration.targetSchemaVersion)
return {
errors: [],
type: 'MigrationComplete',
}
}
if (!(await schemaExist(_client))) {
await initSchemaDoctype(_client)
}
if ((await currentSchemaVersion(_client)) !== migration.baseSchemaVersion) {
return migrationNoop()
} else {
let result: MigrationResult
try {

Hugo NOUTS
committed
_client,
migration.docTypes,
migration.queryOptions
)
if (migration.isDeprecated) {
result = migrationNoop()
} else if (docToUpdate.length && !migration.isCreate) {
const migratedDocs = migration.run(_client, docToUpdate)
if (migratedDocs.length) {
result = await save(_client, migratedDocs)
} else {
result = migrationNoop()
}
} else {
result = migrationNoop()
}

Hugo SUBTIL
committed
// Handle new doctype creation
if (migration.isCreate && !migration.isDeprecated) {
migration.run(_client, docToUpdate)

Hugo SUBTIL
committed
result = { type: MIGRATION_RESULT_COMPLETE, errors: [] }
}
case MIGRATION_RESULT_FAILED:
throw new Error('Migration failed')
case MIGRATION_RESULT_NOOP:
case MIGRATION_RESULT_COMPLETE:
await updateSchemaVersion(_client, migration.targetSchemaVersion)
break
}
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
}
}
return result
}
}
/**
* Handle migration logging
* @param migration Migration
* @param result MigratioNResult
* @returns string
*/
export function migrationLog(
migration: Migration,
result: MigrationResult
): string {
let globalResult
switch (result.type) {
case MIGRATION_RESULT_NOOP:
globalResult = 'NOOP'
break
case MIGRATION_RESULT_COMPLETE:
globalResult = 'Complete'
break
case MIGRATION_RESULT_FAILED:
globalResult = 'Failed'
break
default:
globalResult = 'Unexpected error'
}
return `--- ${migration.description} => ${globalResult}`
}