Skip to content

Commit

Permalink
feat: use listr and ensure changeset creation before migration export…
Browse files Browse the repository at this point in the history
… [PHX-2614] (#2033)

* feat: use listr and ensure changeset creation before migration export [NONE]
  • Loading branch information
marcolink committed May 8, 2023
1 parent bb79cdc commit 76f7351
Show file tree
Hide file tree
Showing 6 changed files with 580 additions and 501 deletions.
125 changes: 83 additions & 42 deletions lib/cmds/merge_cmds/export.ts
@@ -1,4 +1,6 @@
import chalk from 'chalk'
import { type PlainClientAPI } from 'contentful-management'
import Listr from 'listr'
import path from 'path'
import type { Argv } from 'yargs'
import {
Expand All @@ -8,10 +10,10 @@ import {
import { getAppActionId, type Host } from '../../utils/app-actions-config'
import { handleAsyncError as handle } from '../../utils/async'
import { ensureDir, getPath, writeFileP } from '../../utils/fs'
import { success } from '../../utils/log'
import { success, error } from '../../utils/log'
import { mergeErrors } from '../../utils/merge/errors'
import { prepareMergeCommand } from '../../utils/merge/prepare-merge-command'
import { MergeContext } from '../../utils/merge/types'
import { mergeErrors } from '../../utils/merge/errors'

module.exports.command = 'export'

Expand Down Expand Up @@ -52,6 +54,18 @@ interface ExportMigrationOptions {
outputFile?: string
}

type Context = {
api: PlainClientAPI
appDefinitionId: string
createChangesetActionId: string
exportActionId: string
sourceEnvironmentId: string
targetEnvironmentId: string
spaceId: string
changesetRef?: string
migration?: string
}

export const callExportAppAction = async ({
api,
appDefinitionId,
Expand All @@ -68,45 +82,69 @@ export const callExportAppAction = async ({
sourceEnvironmentId: string
targetEnvironmentId: string
spaceId: string
}) => {
let changesetRef: string

try {
changesetRef = await callCreateChangeset({
api,
appDefinitionId,
appActionId: createChangesetActionId,
parameters: {
sourceEnvironmentId,
targetEnvironmentId
},
spaceId,
// We use the target environment as this environment needs to have the merge app installed
// and the context environment might not have it installed and not need it. Using directly
// the target env saves us installations. We could have used the source environment also.
environmentId: targetEnvironmentId
})
} catch (e) {
throw new Error(mergeErrors['ErrorInDiffCreation'])
}
}): Promise<Context> => {
return new Listr([
{
title: 'Compute differences',
// use wrapTask
task: async (ctx: Context, task) => {
task.output = chalk`calculating differences`
const actionResponse = await callCreateChangeset({
api: ctx.api,
appDefinitionId: ctx.appDefinitionId,
appActionId: ctx.createChangesetActionId,
parameters: {
sourceEnvironmentId: ctx.sourceEnvironmentId,
targetEnvironmentId: ctx.targetEnvironmentId
},
spaceId: ctx.spaceId,
// We use the target environment as this environment needs to have the merge app installed
// and the context environment might not have it installed and not need it. Using directly
// the target env saves us installations. We could have used the source environment also.
environmentId: ctx.targetEnvironmentId
}).catch(() => {
throw new Error(mergeErrors['ErrorInDiffCreation'])
})

try {
const { migration } = await getExportMigration({
api,
appDefinitionId,
appActionId: exportActionId,
changesetRef,
spaceId,
targetEnvironmentId: targetEnvironmentId
})
task.output = chalk`fetching differences`
// wait for changeset to be settled
await new Promise(resolve => setTimeout(resolve, 1000 * 8))
// eslint-disable-next-line require-atomic-updates
ctx.changesetRef = actionResponse
// ctx.changesetRef = actionResponse.sys.id
}
},
{
title: chalk`Create migration`,
task: async ctx => {
const { migration } = await getExportMigration({
api: ctx.api,
appDefinitionId: ctx.appDefinitionId,
appActionId: ctx.exportActionId,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
changesetRef: ctx.changesetRef!,
spaceId: ctx.spaceId,
targetEnvironmentId: ctx.targetEnvironmentId
}).catch(e => {
if ((e as Error)?.message === 'PollTimeout') {
throw new Error(mergeErrors['ExportPollTimeout'])
}
throw new Error(mergeErrors['MigrationCouldNotBeExported'])
})

return migration
} catch (e) {
if ((e as Error)?.message === 'PollTimeout') {
throw new Error(mergeErrors['ExportPollTimeout'])
// eslint-disable-next-line require-atomic-updates
ctx.migration = migration
}
}
throw new Error(mergeErrors['MigrationCouldNotBeExported'])
}
]).run({
api,
appDefinitionId: appDefinitionId,
createChangesetActionId,
exportActionId,
sourceEnvironmentId,
targetEnvironmentId,
spaceId: spaceId
})
}

const exportEnvironmentMigration = async ({
Expand Down Expand Up @@ -141,7 +179,7 @@ const exportEnvironmentMigration = async ({
)
}

const migration = await callExportAppAction({
const result = await callExportAppAction({
api: client,
appDefinitionId: mergeAppId,
createChangesetActionId: getAppActionId('create-changeset', host as Host),
Expand All @@ -151,9 +189,12 @@ const exportEnvironmentMigration = async ({
spaceId: activeSpaceId
})

await writeFileP(outputTarget, migration)

success(`✅ Migration exported to ${outputTarget}.`)
if (result.migration) {
await writeFileP(outputTarget, result.migration)
success(`✅ Migration exported to ${outputTarget}.`)
} else {
error(`failed to save migration file!`)
}
}

module.exports.handler = handle(exportEnvironmentMigration)
51 changes: 31 additions & 20 deletions lib/cmds/merge_cmds/show.ts
Expand Up @@ -3,15 +3,17 @@ import {
callAppAction,
isResultWithError
} from '@contentful/app-action-utils'
import chalk from 'chalk'
import { PlainClientAPI } from 'contentful-management'
import Listr from 'listr'
import type { Argv } from 'yargs'
import { getAppActionId, Host } from '../../utils/app-actions-config'
import { handleAsyncError as handle } from '../../utils/async'
import { ContentTypeApiHelper } from '../../utils/merge/content-type-api-helper'
import { mergeErrors } from '../../utils/merge/errors'
import { prepareMergeCommand } from '../../utils/merge/prepare-merge-command'
import { printChangesetMessages } from '../../utils/merge/print-changeset-messages'
import { ChangesetItem, MergeContext } from '../../utils/merge/types'
import { mergeErrors } from '../../utils/merge/errors'

module.exports.command = 'show'

Expand Down Expand Up @@ -130,28 +132,37 @@ const showEnvironmentChangeset = async ({
yes
}
)
const asyncOperations = await new Listr([
{
title: chalk`Create diff for source: {bold.yellow ${sourceEnvironmentId}} and target: {bold.yellow ${targetEnvironmentId}}`,
task: async (ctx, task) => {
const { targetContentType, changeset } =
await getChangesetAndTargetContentType({
client,
activeSpaceId,
host: host as Host,
appDefinitionId: mergeAppId,
sourceEnvironmentId,
targetEnvironmentId
})

const { targetContentType, changeset } =
await getChangesetAndTargetContentType({
client,
activeSpaceId,
host: host as Host,
appDefinitionId: mergeAppId,
sourceEnvironmentId,
targetEnvironmentId
})
// We show only content type changes for now.
// This filter can be removed once we support editor interfaces.
const contentTypeChangeset = changeset.filter(
changesetItem => changesetItem.entity.sys.linkType === 'ContentType'
)

// We show only content type changes for now.
// This filter can be removed once we support editor interfaces.
const contentTypeChangeset = changeset.filter(
changesetItem => changesetItem.entity.sys.linkType === 'ContentType'
)
task.title = chalk`🎉 Created diff for source: {bold.yellow ${sourceEnvironmentId}} and target: {bold.yellow ${targetEnvironmentId}}`
ctx.output = printChangesetMessages(
targetContentType,
contentTypeChangeset
)
}
}
]).run({ output: '' })

const message = printChangesetMessages(
targetContentType,
contentTypeChangeset
)
console.log(message)
console.log('\n')
console.log(asyncOperations.output)
}

module.exports.handler = handle(showEnvironmentChangeset)
18 changes: 18 additions & 0 deletions lib/utils/app-actions.ts
Expand Up @@ -53,6 +53,24 @@ export const callCreateChangeset = async ({
return createResponse.sys.id
}

export const callCreateChangesetWithResponse = async ({
api,
appDefinitionId,
appActionId,
parameters,
environmentId,
spaceId
}: CallCreateChangesetParams) => {
const params = {
appDefinitionId,
appActionId,
...(environmentId ? { environmentId } : {}),
...(spaceId ? { spaceId } : {})
}

return await api.appActionCall.createWithResponse(params, { parameters })
}

type GetExportMigrationParams = {
api: PlainClientAPI
appDefinitionId: string
Expand Down

0 comments on commit 76f7351

Please sign in to comment.