mirror of
https://github.com/strapi/strapi.git
synced 2025-08-10 09:47:46 +00:00
Merge branch 'ts-support-2/update-type-gen' into ts-support/controllers
This commit is contained in:
commit
26a138b704
@ -4,8 +4,8 @@ import { extname } from 'path';
|
|||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { isEmpty, uniq, last, isNumber, difference, set, omit } from 'lodash/fp';
|
import { isEmpty, uniq, last, isNumber, difference, set, omit } from 'lodash/fp';
|
||||||
import { diff as semverDiff } from 'semver';
|
import { diff as semverDiff } from 'semver';
|
||||||
import type { Schema, Utils } from '@strapi/strapi';
|
|
||||||
|
|
||||||
|
import type { Schema, Utils } from '@strapi/strapi';
|
||||||
import type {
|
import type {
|
||||||
IAsset,
|
IAsset,
|
||||||
IDestinationProvider,
|
IDestinationProvider,
|
||||||
@ -23,11 +23,12 @@ import type {
|
|||||||
TransferFilters,
|
TransferFilters,
|
||||||
TransferFilterPreset,
|
TransferFilterPreset,
|
||||||
StreamItem,
|
StreamItem,
|
||||||
|
SchemaDiffHandler,
|
||||||
|
SchemaDiffHandlerContext,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import type { Diff } from '../utils/json';
|
import type { Diff } from '../utils/json';
|
||||||
|
|
||||||
import { compareSchemas, validateProvider } from './validation';
|
import { compareSchemas, validateProvider } from './validation';
|
||||||
import { filter, map } from '../utils/stream';
|
|
||||||
|
|
||||||
import { TransferEngineError, TransferEngineValidationError } from './errors';
|
import { TransferEngineError, TransferEngineValidationError } from './errors';
|
||||||
import {
|
import {
|
||||||
@ -36,6 +37,7 @@ import {
|
|||||||
ErrorDiagnosticSeverity,
|
ErrorDiagnosticSeverity,
|
||||||
} from './diagnostic';
|
} from './diagnostic';
|
||||||
import { DataTransferError } from '../errors';
|
import { DataTransferError } from '../errors';
|
||||||
|
import * as utils from '../utils';
|
||||||
|
|
||||||
export const TRANSFER_STAGES: ReadonlyArray<TransferStage> = Object.freeze([
|
export const TRANSFER_STAGES: ReadonlyArray<TransferStage> = Object.freeze([
|
||||||
'entities',
|
'entities',
|
||||||
@ -103,6 +105,16 @@ class TransferEngine<
|
|||||||
|
|
||||||
diagnostics: IDiagnosticReporter;
|
diagnostics: IDiagnosticReporter;
|
||||||
|
|
||||||
|
#handlers: {
|
||||||
|
schemaDiff: SchemaDiffHandler[];
|
||||||
|
} = {
|
||||||
|
schemaDiff: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
onSchemaDiff(handler: SchemaDiffHandler) {
|
||||||
|
this.#handlers?.schemaDiff?.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
// Save the currently open stream so that we can access it at any time
|
// Save the currently open stream so that we can access it at any time
|
||||||
#currentStream?: Writable;
|
#currentStream?: Writable;
|
||||||
|
|
||||||
@ -122,8 +134,8 @@ class TransferEngine<
|
|||||||
/**
|
/**
|
||||||
* Report a fatal error and throw it
|
* Report a fatal error and throw it
|
||||||
*/
|
*/
|
||||||
#panic(error: Error) {
|
panic(error: Error) {
|
||||||
this.#reportError(error, 'fatal');
|
this.reportError(error, 'fatal');
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -131,7 +143,7 @@ class TransferEngine<
|
|||||||
/**
|
/**
|
||||||
* Report an error diagnostic
|
* Report an error diagnostic
|
||||||
*/
|
*/
|
||||||
#reportError(error: Error, severity: ErrorDiagnosticSeverity) {
|
reportError(error: Error, severity: ErrorDiagnosticSeverity) {
|
||||||
this.diagnostics.report({
|
this.diagnostics.report({
|
||||||
kind: 'error',
|
kind: 'error',
|
||||||
details: {
|
details: {
|
||||||
@ -147,7 +159,7 @@ class TransferEngine<
|
|||||||
/**
|
/**
|
||||||
* Report a warning diagnostic
|
* Report a warning diagnostic
|
||||||
*/
|
*/
|
||||||
#reportWarning(message: string, origin?: string) {
|
reportWarning(message: string, origin?: string) {
|
||||||
this.diagnostics.report({
|
this.diagnostics.report({
|
||||||
kind: 'warning',
|
kind: 'warning',
|
||||||
details: { createdAt: new Date(), message, origin },
|
details: { createdAt: new Date(), message, origin },
|
||||||
@ -157,7 +169,7 @@ class TransferEngine<
|
|||||||
/**
|
/**
|
||||||
* Report an info diagnostic
|
* Report an info diagnostic
|
||||||
*/
|
*/
|
||||||
#reportInfo(message: string, params?: unknown) {
|
reportInfo(message: string, params?: unknown) {
|
||||||
this.diagnostics.report({
|
this.diagnostics.report({
|
||||||
kind: 'info',
|
kind: 'info',
|
||||||
details: { createdAt: new Date(), message, params },
|
details: { createdAt: new Date(), message, params },
|
||||||
@ -183,11 +195,11 @@ class TransferEngine<
|
|||||||
const chainTransforms: StreamItem[] = [];
|
const chainTransforms: StreamItem[] = [];
|
||||||
for (const transform of transforms) {
|
for (const transform of transforms) {
|
||||||
if ('filter' in transform) {
|
if ('filter' in transform) {
|
||||||
chainTransforms.push(filter(transform.filter));
|
chainTransforms.push(utils.stream.filter(transform.filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('map' in transform) {
|
if ('map' in transform) {
|
||||||
chainTransforms.push(map(transform.map));
|
chainTransforms.push(utils.stream.map(transform.map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chainTransforms.length) {
|
if (chainTransforms.length) {
|
||||||
@ -397,11 +409,11 @@ class TransferEngine<
|
|||||||
const path = diff.path.join('.');
|
const path = diff.path.join('.');
|
||||||
|
|
||||||
if (diff.kind === 'added') {
|
if (diff.kind === 'added') {
|
||||||
return `${path} exists in destination schema but not in source schema`;
|
return `${path} exists in destination schema but not in source schema and the data will not be transferred.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff.kind === 'deleted') {
|
if (diff.kind === 'deleted') {
|
||||||
return `${path} exists in source schema but not in destination schema`;
|
return `${path} exists in source schema but not in destination schema and the data will not be transferred.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff.kind === 'modified') {
|
if (diff.kind === 'modified') {
|
||||||
@ -496,7 +508,7 @@ class TransferEngine<
|
|||||||
|
|
||||||
results.forEach((state) => {
|
results.forEach((state) => {
|
||||||
if (state.status === 'rejected') {
|
if (state.status === 'rejected') {
|
||||||
this.#reportWarning(state.reason, `transfer(${stage})`);
|
this.reportWarning(state.reason, `transfer(${stage})`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -523,7 +535,7 @@ class TransferEngine<
|
|||||||
.on('error', (e) => {
|
.on('error', (e) => {
|
||||||
updateEndTime();
|
updateEndTime();
|
||||||
this.#emitStageUpdate('error', stage);
|
this.#emitStageUpdate('error', stage);
|
||||||
this.#reportError(e, 'error');
|
this.reportError(e, 'error');
|
||||||
destination.destroy(e);
|
destination.destroy(e);
|
||||||
reject(e);
|
reject(e);
|
||||||
})
|
})
|
||||||
@ -539,7 +551,11 @@ class TransferEngine<
|
|||||||
|
|
||||||
// Cause an ongoing transfer to abort gracefully
|
// Cause an ongoing transfer to abort gracefully
|
||||||
async abortTransfer(): Promise<void> {
|
async abortTransfer(): Promise<void> {
|
||||||
this.#currentStream?.destroy(new TransferEngineError('fatal', 'Transfer aborted.'));
|
const err = new TransferEngineError('fatal', 'Transfer aborted.');
|
||||||
|
if (!this.#currentStream) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
this.#currentStream.destroy(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
@ -566,7 +582,7 @@ class TransferEngine<
|
|||||||
|
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
if (result.status === 'rejected') {
|
if (result.status === 'rejected') {
|
||||||
this.#panic(result.reason);
|
this.panic(result.reason);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -582,7 +598,7 @@ class TransferEngine<
|
|||||||
|
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
if (result.status === 'rejected') {
|
if (result.status === 'rejected') {
|
||||||
this.#panic(result.reason);
|
this.panic(result.reason);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -626,15 +642,47 @@ class TransferEngine<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { sourceSchema, destinationSchema } = await this.#getSchemas();
|
const sourceSchemas = (await this.sourceProvider.getSchemas?.()) as SchemaMap;
|
||||||
|
const destinationSchemas = (await this.destinationProvider.getSchemas?.()) as SchemaMap;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (sourceSchema && destinationSchema) {
|
if (sourceSchemas && destinationSchemas) {
|
||||||
this.#assertSchemasMatching(sourceSchema, destinationSchema);
|
this.#assertSchemasMatching(sourceSchemas, destinationSchemas);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
// if this is a schema matching error, allow handlers to resolve it
|
||||||
this.#panic(error);
|
if (error instanceof TransferEngineValidationError && error.details?.details?.diffs) {
|
||||||
|
const schemaDiffs = error.details?.details?.diffs as Record<string, Diff[]>;
|
||||||
|
|
||||||
|
const context: SchemaDiffHandlerContext = {
|
||||||
|
ignoredDiffs: {},
|
||||||
|
diffs: schemaDiffs,
|
||||||
|
source: this.sourceProvider,
|
||||||
|
destination: this.destinationProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if we don't have any handlers, throw the original error
|
||||||
|
if (isEmpty(this.#handlers.schemaDiff)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.middleware.runMiddleware<SchemaDiffHandlerContext>(
|
||||||
|
context,
|
||||||
|
this.#handlers.schemaDiff
|
||||||
|
);
|
||||||
|
|
||||||
|
// if there are any remaining diffs that weren't ignored
|
||||||
|
const unresolvedDiffs = utils.json.diff(context.diffs, context.ignoredDiffs);
|
||||||
|
if (unresolvedDiffs.length) {
|
||||||
|
this.panic(
|
||||||
|
new TransferEngineValidationError('Unresolved differences in schema', {
|
||||||
|
check: 'schema.changes',
|
||||||
|
unresolvedDiffs,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
@ -675,7 +723,7 @@ class TransferEngine<
|
|||||||
e instanceof Error &&
|
e instanceof Error &&
|
||||||
(!lastDiagnostic || lastDiagnostic.kind !== 'error' || lastDiagnostic.details.error !== e)
|
(!lastDiagnostic || lastDiagnostic.kind !== 'error' || lastDiagnostic.details.error !== e)
|
||||||
) {
|
) {
|
||||||
this.#reportError(e, (e as DataTransferError).severity || 'fatal');
|
this.reportError(e, (e as DataTransferError).severity || 'fatal');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollback the destination provider if an exception is thrown during the transfer
|
// Rollback the destination provider if an exception is thrown during the transfer
|
||||||
@ -699,9 +747,9 @@ class TransferEngine<
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Error happening during the before transfer step should be considered fatal errors
|
// Error happening during the before transfer step should be considered fatal errors
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
this.#panic(error);
|
this.panic(error);
|
||||||
} else {
|
} else {
|
||||||
this.#panic(
|
this.panic(
|
||||||
new Error(`Unknwon error when executing "beforeTransfer" on the ${origin} provider`)
|
new Error(`Unknwon error when executing "beforeTransfer" on the ${origin} provider`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -171,56 +171,17 @@ class RemoteStrapiSourceProvider implements ISourceProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initTransfer(): Promise<string> {
|
async initTransfer(): Promise<string> {
|
||||||
return new Promise<string>((resolve, reject) => {
|
const query = this.dispatcher?.dispatchCommand({
|
||||||
this.ws
|
command: 'init',
|
||||||
?.on('unexpected-response', (_req, res) => {
|
|
||||||
if (res.statusCode === 401) {
|
|
||||||
return reject(
|
|
||||||
new ProviderInitializationError(
|
|
||||||
'Failed to initialize the connection: Authentication Error'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.statusCode === 403) {
|
|
||||||
return reject(
|
|
||||||
new ProviderInitializationError(
|
|
||||||
'Failed to initialize the connection: Authorization Error'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.statusCode === 404) {
|
|
||||||
return reject(
|
|
||||||
new ProviderInitializationError(
|
|
||||||
'Failed to initialize the connection: Data transfer is not enabled on the remote host'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return reject(
|
|
||||||
new ProviderInitializationError(
|
|
||||||
`Failed to initialize the connection: Unexpected server response ${res.statusCode}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
?.once('open', async () => {
|
|
||||||
const query = this.dispatcher?.dispatchCommand({
|
|
||||||
command: 'init',
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = (await query) as Server.Payload<Server.InitMessage>;
|
|
||||||
|
|
||||||
if (!res?.transferID) {
|
|
||||||
return reject(
|
|
||||||
new ProviderTransferError('Init failed, invalid response from the server')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(res.transferID);
|
|
||||||
})
|
|
||||||
.once('error', reject);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const res = (await query) as Server.Payload<Server.InitMessage>;
|
||||||
|
|
||||||
|
if (!res?.transferID) {
|
||||||
|
throw new ProviderTransferError('Init failed, invalid response from the server');
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.transferID;
|
||||||
}
|
}
|
||||||
|
|
||||||
async bootstrap(): Promise<void> {
|
async bootstrap(): Promise<void> {
|
||||||
|
@ -3,3 +3,4 @@ export * as stream from './stream';
|
|||||||
export * as json from './json';
|
export * as json from './json';
|
||||||
export * as schema from './schema';
|
export * as schema from './schema';
|
||||||
export * as transaction from './transaction';
|
export * as transaction from './transaction';
|
||||||
|
export * as middleware from './middleware';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user