diff --git a/README.md b/README.md index e16671a..857d196 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ $ npm install -g @tiagonapoli/vtex-scripts $ vtex-dev COMMAND running command... $ vtex-dev (-v|--version|version) -@tiagonapoli/vtex-scripts/0.0.7 linux-x64 node-v12.16.2 +@tiagonapoli/vtex-scripts/0.0.7 darwin-x64 node-v12.18.0 $ vtex-dev --help [COMMAND] USAGE $ vtex-dev COMMAND @@ -27,6 +27,7 @@ USAGE * [`vtex-dev app:bundle APPID`](#vtex-dev-appbundle-appid) +* [`vtex-dev app:imageToMedia [WORKSPACE]`](#vtex-dev-appimagetomedia-workspace) * [`vtex-dev app:types APPID`](#vtex-dev-apptypes-appid) * [`vtex-dev help [COMMAND]`](#vtex-dev-help-command) @@ -50,6 +51,23 @@ EXAMPLES vtex-dev app:bundle vtex.render-server@8.x --linked ``` +## `vtex-dev app:imageToMedia [WORKSPACE]` + +Migrate image content to media content + +``` +USAGE + $ vtex-dev app:imageToMedia [WORKSPACE] + +OPTIONS + -h, --help show CLI help + -o, --override Override existing media content if an image with the same treePath can be migrated + +EXAMPLES + vtex-dev workspace:migrateToImage + vtex-dev workspace:migrateToImage "myworkspace" +``` + ## `vtex-dev app:types APPID` Download app types diff --git a/package.json b/package.json index 2299c47..bd1135f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tiagonapoli/vtex-scripts", - "version": "0.0.7", + "version": "0.0.8-beta.0", "author": "Tiago NĂ¡poli ", "license": "ISC", "files": [ diff --git a/src/clients/HttpClient.ts b/src/clients/HttpClient.ts index 7b02858..01c271a 100644 --- a/src/clients/HttpClient.ts +++ b/src/clients/HttpClient.ts @@ -53,4 +53,9 @@ export class HttpClient { console.debug(`GET stream from ${this.buildFullURL(url)}`) return this.http.get(url, { ...config, responseType: 'stream' }) } + + public putRaw = (url: string, data?: any, config: AxiosRequestConfig = {}): Promise => { + console.debug(`PUT to ${this.buildFullURL(url)}`) + return this.http.put(url, data, config) + } } diff --git a/src/clients/Templates.ts b/src/clients/Templates.ts new file mode 100644 index 0000000..5a87de7 --- /dev/null +++ b/src/clients/Templates.ts @@ -0,0 +1,52 @@ +import { IOContext } from '@vtex/api' +import { AxiosRequestConfig } from 'axios' +import { HttpClient } from './HttpClient' + +const createRoutes = ({ account, workspace }: IOContext) => { + const routes = { + Templates: (argWorkspace: string) => + `/${account}/${argWorkspace || workspace}/buckets/vtex.pages-graphql/userData/files/store/templates.json`, + } + return routes +} + +export class Templates { + private http: HttpClient + private _routes: ReturnType + + constructor(ctx: IOContext, options?: AxiosRequestConfig) { + const { authToken } = ctx + this.http = new HttpClient({ + ...options, + authToken, + baseURL: `https://infra.io.vtex.com/vbase/v2`, + }) + this._routes = createRoutes(ctx) + } + + private get routes() { + return this._routes + } + + public getWorkspaceTemplate = (workspace?: string) => { + return this.http.get(this.routes.Templates(workspace), { + headers: { + Accept: 'application/json', + }, + }) + } + + public updateWorkspaceTemplate = (migratedTemplate: TemplatesJSON, workspace?: string) => { + return this.http.putRaw(this.routes.Templates(workspace), migratedTemplate, { + headers: { + 'Content-Type': 'application/json', + }, + }) + } +} + +export type TemplatesJSON = Record + +export interface TreePathContainer { + treePathContentMap: Record +} diff --git a/src/commands/app/imageToMedia.ts b/src/commands/app/imageToMedia.ts new file mode 100644 index 0000000..80b1b7d --- /dev/null +++ b/src/commands/app/imageToMedia.ts @@ -0,0 +1,100 @@ +import { flags as oclifFlags } from '@oclif/command' +import { AxiosError } from 'axios' +import { createContext } from '../../clients' +import { Templates, TemplatesJSON } from '../../clients/Templates' +import { CustomCommand } from '../../oclif/CustomCommand' +import { VtexConfig } from '../../VtexConfig' + +function objectHasImageContent(object: any) { + return valueHasImageContent(JSON.stringify(object)) +} + +function valueHasImageContent(value: string) { + return value.search('/image') !== -1 +} + +function replaceImageTreepath(imageTreePath: string) { + return imageTreePath.replace('/image', '/media') +} + +function getMediaTemplates(jsonTemplates: TemplatesJSON, overrideMedia: boolean) { + const mediaTemplates = jsonTemplates + + if (!objectHasImageContent(jsonTemplates)) { + return null + } + + Object.entries(jsonTemplates).forEach(([pageId, container]) => { + if (!objectHasImageContent(container)) { + return + } + + const migratedContainer = container + + Object.entries(container.treePathContentMap).forEach(([treePath, contentValue]) => { + // If it doesn't have an image content or (already has a media content defined and cannot override), just pass + if (!valueHasImageContent(treePath) || (valueHasImageContent(replaceImageTreepath(treePath)) && !overrideMedia)) { + return + } + + const migratedTreePath = replaceImageTreepath(treePath) + migratedContainer.treePathContentMap[migratedTreePath] = contentValue + }) + + mediaTemplates[pageId] = migratedContainer + }) + + return mediaTemplates +} + +export default class AppBundle extends CustomCommand { + static description = 'Migrate image content to media content' + + static examples = ['vtex-dev workspace:migrateToImage', 'vtex-dev workspace:migrateToImage "myworkspace"'] + + static flags = { + help: oclifFlags.help({ char: 'h' }), + override: oclifFlags.boolean({ + char: 'o', + description: 'Override existing media content if an image with the same treePath can be migrated', + default: false, + }), + } + + static args = [{ name: 'workspace', required: false }] + + async run() { + const { flags, args } = this.parse(AppBundle) + const { workspace: workspaceArg } = args + const { override: overrideMedia } = flags + const ioContext = createContext(VtexConfig) + + const { account, workspace } = ioContext + const templates = new Templates(createContext(VtexConfig), { timeout: 30000 }) + + console.log(`Downloading template from account ${account} and workspace ${workspaceArg || workspace}`) + + try { + const jsonTemplates = await templates.getWorkspaceTemplate(workspaceArg) + + console.log(`Finding image content...`) + + const mediaTemplates = getMediaTemplates(jsonTemplates, overrideMedia) + + if (!mediaTemplates) { + console.log('There was no image content to migrate') + return + } + + console.log(`Migrating image content to media...`) + + await templates.updateWorkspaceTemplate(mediaTemplates, workspaceArg) + + console.log('All done!') + return + } catch (err) { + const axiosErr: AxiosError = err + console.log(axiosErr.response.status, axiosErr.response.statusText, axiosErr.message, axiosErr.stack) + } + } +} diff --git a/yarn.lock b/yarn.lock index 67582fc..0d872ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5830,7 +5830,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"stats-lite@github:vtex/node-stats-lite#dist": +stats-lite@vtex/node-stats-lite#dist: version "2.2.0" resolved "https://codeload.github.com/vtex/node-stats-lite/tar.gz/1b0d39cc41ef7aaecfd541191f877887a2044797" dependencies: