Skip to content
Snippets Groups Projects
migration.service.ts 3.49 KiB
Newer Older
  • Learn to ignore specific revisions
  • Bastien DUMONT's avatar
    Bastien DUMONT committed
    import * as Sentry from '@sentry/react'
    
    import { Client, Q, QueryResult } from 'cozy-client'
    
    import { SCHEMAS_DOCTYPE } from 'doctypes'
    import { InitStepsErrors, ReleaseNotes, Schema } from 'models'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { logDuration } from 'utils/duration'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import logApp from 'utils/logger'
    import { migrate, migrationLog } from './migration'
    
    import {
      MIGRATION_RESULT_COMPLETE,
      MIGRATION_RESULT_FAILED,
    } from './migration.data'
    
    import { Migration } from './migration.type'
    
    
    export class MigrationService {
      private readonly _client: Client
    
      private readonly _setInitStepError: React.Dispatch<
    
        React.SetStateAction<InitStepsErrors | null>
      >
      constructor(
        _client: Client,
    
        _setInitStepError: React.Dispatch<
    
          React.SetStateAction<InitStepsErrors | null>
        >
      ) {
    
        this._client = _client
    
        this._setInitStepError = _setInitStepError
    
      /**
       * Return schema version
       * @param _client cozyClient
       * @returns Promise<number> Version number of schema
       */
      public async currentSchemaVersion(_client: Client): Promise<number> {
    
        const query = Q(SCHEMAS_DOCTYPE)
    
        const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1))
        return data?.data[0]?.version || 0
      }
    
      public async runMigrations(migrations: Migration[]): Promise<ReleaseNotes> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        logApp.info('[Migration] Running migrations...')
    
        let releaseStatus = false
        const releaseNotes: ReleaseNotes = {
          show: releaseStatus,
          notes: [
            {
              title: '',
              description: '',
            },
          ],
    
        const currentVersion = await this.currentSchemaVersion(this._client)
        const targetVersion = migrations[migrations.length - 1].targetSchemaVersion
    
        // Prevent Migration service to run every migration if not needed
        if (currentVersion != targetVersion) {
          const startMigrationIndex =
            migrations.length - (targetVersion - currentVersion)
          const migrationsToRun = migrations.splice(startMigrationIndex)
    
          for (const migration of migrationsToRun) {
            // First attempt
    
            const migrationResult = await migrate(migration, this._client)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logApp.info(migrationLog(migration, migrationResult))
    
            if (migrationResult.type === MIGRATION_RESULT_FAILED) {
              // Retry in case of failure
              const result = await migrate(migration, this._client)
              if (result.type === MIGRATION_RESULT_FAILED) {
                // Error in case of second failure
    
                this._setInitStepError(InitStepsErrors.MIGRATION_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                logApp.error(migrationLog(migration, result))
                Sentry.captureException(migrationLog(migration, result))
    
                throw new Error()
              } else {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                logApp.info(migrationLog(migration, result))
    
            if (
              migration.releaseNotes !== null &&
              migrationResult.type === MIGRATION_RESULT_COMPLETE
            ) {
              releaseNotes.notes.push(migration.releaseNotes)
              releaseStatus = true
    
              if (migration.redirectLink) {
                releaseNotes.redirectLink = migration.redirectLink
              }
    
          releaseNotes.show = releaseStatus
          // In case of first instance, don't show release notes
          if (startMigrationIndex === 0) releaseNotes.show = false
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logApp.info('[Migration] Done')
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logApp.info('[Migration] Skipped Migration Process, already up-to-date')
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        logDuration('[Migration] Finished in', startTime)
        return releaseNotes