Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/spicy-toys-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@aws-amplify/schema-generator': patch
---

Handle schema errors
52 changes: 52 additions & 0 deletions packages/schema-generator/src/generate_schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { beforeEach, describe, it, mock } from 'node:test';
import { SchemaGenerator, parseDatabaseUrl } from './generate_schema.js';
import assert from 'node:assert';
import {
EmptySchemaError,
InvalidSchemaError,
TypescriptDataSchemaGenerator,
TypescriptDataSchemaGeneratorConfig,
} from '@aws-amplify/graphql-schema-generator';
import fs from 'fs/promises';
import { AmplifyUserError } from '@aws-amplify/platform-core';

const mockGenerateMethod =
mock.fn<(config: TypescriptDataSchemaGeneratorConfig) => Promise<string>>();
Expand Down Expand Up @@ -164,4 +167,53 @@ void describe('SchemaGenerator', () => {
'Unable to parse the database URL. Unsupported database engine: incorrect',
});
});

void it('should throw error if database schema is incorrect', async () => {
mockGenerateMethod.mock.mockImplementationOnce(() => {
throw new InvalidSchemaError([{}], ['missingColumn']);
});
const schemaGenerator = new SchemaGenerator();
await assert.rejects(
() =>
schemaGenerator.generate({
connectionUri: {
secretName: 'FAKE_SECRET_NAME',
value: 'mysql://user:password@hostname:3306/db',
},
out: 'schema.ts',
}),
(error: AmplifyUserError) => {
assert.strictEqual(error.name, 'DatabaseSchemaError');
assert.strictEqual(
error.message,
'Imported SQL schema is invalid. Imported schema is missing columns: missingColumn'
);
assert.strictEqual(error.resolution, 'Check the database schema.');
return true;
}
);
});

void it('should throw error if database schema is empty', async () => {
mockGenerateMethod.mock.mockImplementationOnce(() => {
throw new EmptySchemaError();
});
const schemaGenerator = new SchemaGenerator();
await assert.rejects(
() =>
schemaGenerator.generate({
connectionUri: {
secretName: 'FAKE_SECRET_NAME',
value: 'mysql://user:password@hostname:3306/db',
},
out: 'schema.ts',
}),
(error: AmplifyUserError) => {
assert.strictEqual(error.name, 'DatabaseSchemaError');
assert.strictEqual(error.message, 'Imported SQL schema is empty.');
assert.strictEqual(error.resolution, 'Check the database schema.');
return true;
}
);
});
});
21 changes: 20 additions & 1 deletion packages/schema-generator/src/generate_schema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TypescriptDataSchemaGenerator } from '@aws-amplify/graphql-schema-generator';
import {
EmptySchemaError,
InvalidSchemaError,
TypescriptDataSchemaGenerator,
} from '@aws-amplify/graphql-schema-generator';
import fs from 'fs/promises';
import { AmplifyUserError } from '@aws-amplify/platform-core';

Expand All @@ -16,6 +20,7 @@ export type SchemaGeneratorConfig = {

type AmplifyGenerateSchemaError =
| 'DatabaseConnectionError'
| 'DatabaseSchemaError'
| 'DatabaseUnsupportedEngineError'
| 'DatabaseUrlParseError';

Expand All @@ -38,6 +43,20 @@ export class SchemaGenerator {
});
await fs.writeFile(props.out, schema);
} catch (err) {
if (
err instanceof EmptySchemaError ||
err instanceof InvalidSchemaError
) {
throw new AmplifyUserError<AmplifyGenerateSchemaError>(
'DatabaseSchemaError',
{
// the message already contains descriptive error.
message: err.message,
resolution: 'Check the database schema.',
},
err
);
}
const databaseError = err as DatabaseConnectError;
if (databaseError.code === 'ETIMEDOUT') {
throw new AmplifyUserError<AmplifyGenerateSchemaError>(
Expand Down