diff --git a/.github/actions/run-api-tests/script.sh b/.github/actions/run-api-tests/script.sh index b571e7303c..b4f09a241c 100755 --- a/.github/actions/run-api-tests/script.sh +++ b/.github/actions/run-api-tests/script.sh @@ -8,5 +8,6 @@ export JWT_SECRET="aSecret" opts=($DB_OPTIONS) +yarn run -s build:ts yarn run -s test:generate-app "${opts[@]}" yarn run -s test:api --no-generate-app diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 768fbea22f..f683c85b0a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -133,8 +133,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -173,8 +171,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=mysql --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -212,8 +208,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=mysql --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -236,8 +230,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests env: SQLITE_PKG: ${{ matrix.sqlite_pkg }} @@ -284,8 +276,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -328,8 +318,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=mysql --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -356,8 +344,6 @@ jobs: path: '**/node_modules' key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} - run: yarn install --frozen-lockfile - - name: Build TypeScript packages - run: yarn build:t - uses: ./.github/actions/run-api-tests env: SQLITE_PKG: ${{ matrix.sqlite_pkg }} diff --git a/packages/core/data-transfer/src/strapi/remote/constants.ts b/packages/core/data-transfer/src/strapi/remote/constants.ts new file mode 100644 index 0000000000..6f913eeba6 --- /dev/null +++ b/packages/core/data-transfer/src/strapi/remote/constants.ts @@ -0,0 +1 @@ +export const TRANSFER_URL = '/transfer'; diff --git a/packages/core/data-transfer/src/strapi/remote/index.ts b/packages/core/data-transfer/src/strapi/remote/index.ts index af2810cd4c..719e07906a 100644 --- a/packages/core/data-transfer/src/strapi/remote/index.ts +++ b/packages/core/data-transfer/src/strapi/remote/index.ts @@ -1,2 +1,3 @@ export * as controllers from './controllers'; export * as routes from './routes'; +export * as constants from './constants'; diff --git a/packages/core/data-transfer/src/strapi/remote/routes.ts b/packages/core/data-transfer/src/strapi/remote/routes.ts index 34fc5585e7..746bed09bb 100644 --- a/packages/core/data-transfer/src/strapi/remote/routes.ts +++ b/packages/core/data-transfer/src/strapi/remote/routes.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line node/no-extraneous-import import type { Context } from 'koa'; +import { TRANSFER_URL } from './constants'; import { createTransferHandler } from './handlers'; // Extend Strapi interface type to access the admin routes' API @@ -28,7 +29,7 @@ declare module '@strapi/strapi' { export const registerAdminTransferRoute = (strapi: Strapi.Strapi) => { strapi.admin.routes.push({ method: 'GET', - path: '/transfer', + path: TRANSFER_URL, handler: createTransferHandler(), config: { auth: false }, }); diff --git a/packages/core/strapi/bin/strapi.js b/packages/core/strapi/bin/strapi.js index 471be826f6..63ceeee992 100755 --- a/packages/core/strapi/bin/strapi.js +++ b/packages/core/strapi/bin/strapi.js @@ -262,8 +262,16 @@ program program .command('transfer') .description('Transfer data from one source to another') - .addOption(new Option('--from ', `Source of your data`).default('local')) - .addOption(new Option('--to ', `Destination of your data`).default('remote')) + .addOption(new Option('--from ', `URL of remote Strapi instance to get data from.`)) + .addOption(new Option('--to ', `URL of remote Strapi instance to send data to`)) + .hook('preAction', async (thisCommand) => { + const opts = thisCommand.opts(); + + if (!opts.from && !opts.to) { + console.error('At least one source (from) or destination (to) option must be provided'); + process.exit(1); + } + }) .allowExcessArguments(false) .action(getLocalScript('transfer/transfer')); diff --git a/packages/core/strapi/lib/commands/__tests__/data-transfer/transfer.test.js b/packages/core/strapi/lib/commands/__tests__/data-transfer/transfer.test.js index ff6d3c6d45..3ae7131ffd 100644 --- a/packages/core/strapi/lib/commands/__tests__/data-transfer/transfer.test.js +++ b/packages/core/strapi/lib/commands/__tests__/data-transfer/transfer.test.js @@ -32,7 +32,9 @@ const expectExit = async (code, fn) => { const transferCommand = require('../../transfer/transfer'); -const logger = jest.spyOn(console, 'error').mockImplementation(() => {}); +jest.spyOn(console, 'error').mockImplementation(() => {}); +jest.spyOn(console, 'warn').mockImplementation(() => {}); +jest.spyOn(console, 'log').mockImplementation(() => {}); jest.mock('../../transfer/utils'); @@ -45,7 +47,7 @@ describe('Transfer', () => { it('uses destination url provided by user without authentication', async () => { await expectExit(1, async () => { - await transferCommand({ from: 'local', to: destinationUrl }); + await transferCommand({ from: undefined, to: destinationUrl }); }); expect( @@ -61,7 +63,7 @@ describe('Transfer', () => { it('uses restore as the default strategy', async () => { await expectExit(1, async () => { - await transferCommand({ from: 'local', to: destinationUrl }); + await transferCommand({ from: undefined, to: destinationUrl }); }); expect( @@ -74,7 +76,7 @@ describe('Transfer', () => { }); it('uses destination url provided by user without authentication', async () => { await expectExit(1, async () => { - await transferCommand({ from: 'local', to: destinationUrl }); + await transferCommand({ from: undefined, to: destinationUrl }); }); expect( @@ -88,7 +90,7 @@ describe('Transfer', () => { it('uses restore as the default strategy', async () => { await expectExit(1, async () => { - await transferCommand({ from: 'local', to: destinationUrl }); + await transferCommand({ from: undefined, to: destinationUrl }); }); expect( @@ -102,18 +104,10 @@ describe('Transfer', () => { it('uses local strapi instance when local specified', async () => { await expectExit(1, async () => { - await transferCommand({ from: 'local', to: destinationUrl }); + await transferCommand({ from: undefined, to: destinationUrl }); }); expect(mockDataTransfer.strapi.providers.createLocalStrapiSourceProvider).toHaveBeenCalled(); expect(utils.createStrapiInstance).toHaveBeenCalled(); }); - - it('Logs an error when the source provider does not exist', async () => { - await expectExit(1, async () => { - await transferCommand({ from: 'test', to: destinationUrl }); - }); - - expect(logger).toHaveBeenCalledWith("Couldn't create providers"); - }); }); diff --git a/packages/core/strapi/lib/commands/transfer/transfer.js b/packages/core/strapi/lib/commands/transfer/transfer.js index b875defa86..eacea973d9 100644 --- a/packages/core/strapi/lib/commands/transfer/transfer.js +++ b/packages/core/strapi/lib/commands/transfer/transfer.js @@ -2,7 +2,11 @@ const { createTransferEngine } = require('@strapi/data-transfer/lib/engine'); const { - providers: { createRemoteStrapiDestinationProvider, createLocalStrapiSourceProvider }, + providers: { + createRemoteStrapiDestinationProvider, + createLocalStrapiSourceProvider, + createLocalStrapiDestinationProvider, + }, } = require('@strapi/data-transfer/lib/strapi'); const { isObject } = require('lodash/fp'); const chalk = require('chalk'); @@ -13,17 +17,17 @@ const { DEFAULT_IGNORED_CONTENT_TYPES, } = require('./utils'); -/** - * @typedef TransferCommandOptions Options given to the CLI import command - * - * @property {string} [from] The source strapi project - * @property {string} [to] The destination strapi project - */ - const logger = console; /** - * Import command. + * @typedef TransferCommandOptions Options given to the CLI transfer command + * + * @property {string|undefined} [to] The url of a remote Strapi to use as remote destination + * @property {string|undefined} [from] The url of a remote Strapi to use as remote source + */ + +/** + * Transfer command. * * It transfers data from a local file to a local strapi instance * @@ -40,11 +44,33 @@ module.exports = async (opts) => { let source; let destination; - if (opts.from === 'local') { - source = createSourceProvider(strapi); + + if (!opts.from && !opts.to) { + logger.error('At least one source (from) or destination (to) option must be provided'); + process.exit(1); } - if (opts.to) { - destination = createDestinationProvider({ + + // if no URL provided, use local Strapi + if (!opts.from) { + source = createLocalStrapiSourceProvider({ + getStrapi: () => strapi, + }); + } + // if URL provided, set up a remote source provider + else { + logger.error(`Remote Strapi source provider not yet implemented`); + process.exit(1); + } + + // if no URL provided, use local Strapi + if (!opts.to) { + destination = createLocalStrapiDestinationProvider({ + getStrapi: () => strapi, + }); + } + // if URL provided, set up a remote destination provider + else { + destination = createRemoteStrapiDestinationProvider({ url: opts.to, auth: false, strategy: 'restore', @@ -53,8 +79,9 @@ module.exports = async (opts) => { }, }); } + if (!source || !destination) { - logger.error("Couldn't create providers"); + logger.error('Could not create providers'); process.exit(1); } @@ -83,7 +110,7 @@ module.exports = async (opts) => { }); try { - logger.log(`Starting export...`); + logger.log(`Starting transfer...`); const results = await engine.transfer(); @@ -93,25 +120,8 @@ module.exports = async (opts) => { logger.log(`${chalk.bold('Transfer process has been completed successfully!')}`); process.exit(0); } catch (e) { - logger.error('Transfer process failed unexpectedly:', e); + logger.error('Transfer process failed unexpectedly'); + logger.error(e); process.exit(1); } }; - -/** - * It creates a local strapi destination provider - */ -const createSourceProvider = (strapi) => { - return createLocalStrapiSourceProvider({ - async getStrapi() { - return strapi; - }, - }); -}; - -/** - * It creates a remote strapi destination provider based on the given options - */ -const createDestinationProvider = (opts) => { - return createRemoteStrapiDestinationProvider(opts); -}; diff --git a/packages/core/strapi/package.json b/packages/core/strapi/package.json index e4cd6b2980..110a2505c7 100644 --- a/packages/core/strapi/package.json +++ b/packages/core/strapi/package.json @@ -81,6 +81,7 @@ "@koa/cors": "3.4.3", "@koa/router": "10.1.1", "@strapi/admin": "4.5.5", + "@strapi/data-transfer": "4.5.5", "@strapi/database": "4.5.5", "@strapi/data-transfer": "4.5.5", "@strapi/generate-new": "4.5.5",