@energica-city/shared-amplify-utils
Version:
Shared utilities for AWS Amplify projects
148 lines • 23.7 kB
JavaScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { createGraphQLErrorHandler } from './GraphQLErrorHandler';
import { logger } from '../../log';
// Mock dependencies
vi.mock('../../log', () => ({
logger: {
error: vi.fn(),
warn: vi.fn(),
},
}));
// Mock the real error library to control its behavior for tests
vi.mock('../../error', async (importOriginal) => {
const actual = (await importOriginal());
return {
...actual,
extractErrorMessage: vi.fn(error => {
if (error instanceof Error)
return error.message;
return String(error);
}),
createErrorContext: vi.fn(context => context),
};
});
// Helper to create an error that mimics our library's errors
function createLibraryError(message, context = {}) {
const error = new Error(message);
// This is the key property our handler looks for
error.__fromErrorLibrary = true;
// Attach context properties
for (const [key, value] of Object.entries(context)) {
error[key] = value;
}
return error;
}
describe('GraphQL Error Handler Middleware', () => {
const mockGqlInput = {
event: {
arguments: { id: '123' },
identity: { sub: 'user-sub', issuer: 'cognito' },
info: {
fieldName: 'getUser',
parentTypeName: 'Query',
variables: { id: '123' },
},
source: null,
request: { headers: {} },
},
context: {
awsRequestId: 'gql-request-id',
functionName: 'getUser-resolver',
},
};
beforeEach(() => {
vi.clearAllMocks();
});
it('should create a middleware function', () => {
const handler = createGraphQLErrorHandler();
expect(handler).toBeInstanceOf(Function);
});
it('should let successful results pass through', async () => {
const errorHandler = createGraphQLErrorHandler();
const next = vi.fn().mockResolvedValue({ id: '123', name: 'Test User' });
const result = await errorHandler(mockGqlInput, next);
expect(result).toEqual({ id: '123', name: 'Test User' });
expect(logger.error).not.toHaveBeenCalled();
expect(next).toHaveBeenCalledTimes(1);
});
it('should catch, log, and re-throw errors from our library', async () => {
const errorHandler = createGraphQLErrorHandler({ includeStackTrace: true });
const libraryError = createLibraryError('Database connection failed', {
db: 'users-db',
});
const next = vi.fn().mockRejectedValue(libraryError);
await expect(errorHandler(mockGqlInput, next)).rejects.toThrow('Database connection failed');
expect(logger.error).toHaveBeenCalledWith('GraphQL resolver error: Database connection failed', expect.objectContaining({
db: 'users-db',
graphql: {
typeName: 'Query',
fieldName: 'getUser',
arguments: { id: '123' },
},
identity: mockGqlInput.event.identity,
stack: expect.any(String),
}));
});
it('should handle non-standard errors by wrapping them', async () => {
const errorHandler = createGraphQLErrorHandler();
const nonStandardError = new Error('Something unexpected happened');
const next = vi.fn().mockRejectedValue(nonStandardError);
await expect(errorHandler(mockGqlInput, next)).rejects.toThrow('Non-standard error thrown to GraphQL error handler');
expect(logger.warn).toHaveBeenCalledWith('Non-standard error thrown, wrapping with throwError', { error: nonStandardError });
expect(logger.error).toHaveBeenCalledWith('GraphQL resolver error: Non-standard error thrown to GraphQL error handler', expect.objectContaining({
errorType: 'object',
originalError: nonStandardError,
}));
});
it('should not include stack trace in logs when disabled', async () => {
const errorHandler = createGraphQLErrorHandler({
includeStackTrace: false,
});
const libraryError = createLibraryError('Test error');
const next = vi.fn().mockRejectedValue(libraryError);
await expect(errorHandler(mockGqlInput, next)).rejects.toThrow();
const errorLog = logger.error.mock.calls[0][1];
expect(errorLog).not.toHaveProperty('stack');
});
it('should include default context in logs', async () => {
const errorHandler = createGraphQLErrorHandler({
defaultContext: { service: 'user-api', version: '1.0.1' },
});
const libraryError = createLibraryError('Test error');
const next = vi.fn().mockRejectedValue(libraryError);
await expect(errorHandler(mockGqlInput, next)).rejects.toThrow();
expect(logger.error).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
service: 'user-api',
version: '1.0.1',
}));
});
it('should handle failed middleware context', async () => {
const errorHandler = createGraphQLErrorHandler();
const errorWithMiddlewareContext = createLibraryError('Auth failed', {
middlewareName: 'auth-middleware',
middlewareChain: ['error-handler', 'auth-middleware'],
});
const next = vi.fn().mockRejectedValue(errorWithMiddlewareContext);
await expect(errorHandler(mockGqlInput, next)).rejects.toThrow();
expect(logger.error).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
failedMiddleware: 'auth-middleware',
middlewareChain: 'error-handler -> auth-middleware',
}));
});
it('should correctly extract AppSync event info', async () => {
const errorHandler = createGraphQLErrorHandler();
const libraryError = createLibraryError('Input validation failed');
const next = vi.fn().mockRejectedValue(libraryError);
await expect(errorHandler(mockGqlInput, next)).rejects.toThrow();
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('Input validation failed'), expect.objectContaining({
graphql: {
typeName: 'Query',
fieldName: 'getUser',
arguments: { id: '123' },
},
identity: { sub: 'user-sub', issuer: 'cognito' },
requestId: 'gql-request-id',
}));
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR3JhcGhRTEVycm9ySGFuZGxlci50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbWlkZGxld2FyZS9ncmFwaHFsL0dyYXBoUUxFcnJvckhhbmRsZXIudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUM5RCxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBT25DLG9CQUFvQjtBQUNwQixFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQzFCLE1BQU0sRUFBRTtRQUNOLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ2QsSUFBSSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUU7S0FDZDtDQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUosZ0VBQWdFO0FBQ2hFLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLEtBQUssRUFBQyxjQUFjLEVBQUMsRUFBRTtJQUM1QyxNQUFNLE1BQU0sR0FBRyxDQUFDLE1BQU0sY0FBYyxFQUFFLENBTXJDLENBQUM7SUFDRixPQUFPO1FBQ0wsR0FBRyxNQUFNO1FBQ1QsbUJBQW1CLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUNqQyxJQUFJLEtBQUssWUFBWSxLQUFLO2dCQUFFLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQztZQUNqRCxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN2QixDQUFDLENBQUM7UUFDRixrQkFBa0IsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDO0tBQzlDLENBQUM7QUFDSixD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RCxNQUFNLFlBQVksR0FBRyxrQkFBa0IsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1FBQ25FLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUVyRCxNQUFNLE1BQU0sQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBRWpFLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsb0JBQW9CLENBQ3ZDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyx5QkFBeUIsQ0FBQyxFQUNsRCxNQUFNLENBQUMsZ0JBQWdCLENBQUM7WUFDdEIsT0FBTyxFQUFFO2dCQUNQLFFBQVEsRUFBRSxPQUFPO2dCQUNqQixTQUFTLEVBQUUsU0FBUztnQkFDcEIsU0FBUyxFQUFFLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRTthQUN6QjtZQUNELFFBQVEsRUFBRSxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRTtZQUNoRCxTQUFTLEVBQUUsZ0JBQWdCO1NBQzVCLENBQUMsQ0FDSCxDQUFDO0lBQ0osQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlc2NyaWJlLCBpdCwgZXhwZWN0LCBiZWZvcmVFYWNoLCB2aSB9IGZyb20gJ3ZpdGVzdCc7XG5pbXBvcnQgeyBjcmVhdGVHcmFwaFFMRXJyb3JIYW5kbGVyIH0gZnJvbSAnLi9HcmFwaFFMRXJyb3JIYW5kbGVyJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL2xvZyc7XG5pbXBvcnQge1xuICB0aHJvd0Vycm9yLFxuICBleHRyYWN0RXJyb3JNZXNzYWdlLFxuICBjcmVhdGVFcnJvckNvbnRleHQsXG59IGZyb20gJy4uLy4uL2Vycm9yJztcblxuLy8gTW9jayBkZXBlbmRlbmNpZXNcbnZpLm1vY2soJy4uLy4uL2xvZycsICgpID0+ICh7XG4gIGxvZ2dlcjoge1xuICAgIGVycm9yOiB2aS5mbigpLFxuICAgIHdhcm46IHZpLmZuKCksXG4gIH0sXG59KSk7XG5cbi8vIE1vY2sgdGhlIHJlYWwgZXJyb3IgbGlicmFyeSB0byBjb250cm9sIGl0cyBiZWhhdmlvciBmb3IgdGVzdHNcbnZpLm1vY2soJy4uLy4uL2Vycm9yJywgYXN5bmMgaW1wb3J0T3JpZ2luYWwgPT4ge1xuICBjb25zdCBhY3R1YWwgPSAoYXdhaXQgaW1wb3J0T3JpZ2luYWwoKSkgYXMge1xuICAgIGV4dHJhY3RFcnJvck1lc3NhZ2U6IChlcnJvcjogdW5rbm93bikgPT4gc3RyaW5nO1xuICAgIGNyZWF0ZUVycm9yQ29udGV4dDogKFxuICAgICAgY29udGV4dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgKSA9PiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xuICB9O1xuICByZXR1cm4ge1xuICAgIC4uLmFjdHVhbCxcbiAgICBleHRyYWN0RXJyb3JNZXNzYWdlOiB2aS5mbihlcnJvciA9PiB7XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIGVycm9yLm1lc3NhZ2U7XG4gICAgICByZXR1cm4gU3RyaW5nKGVycm9yKTtcbiAgICB9KSxcbiAgICBjcmVhdGVFcnJvckNvbnRleHQ6IHZpLmZuKGNvbnRleHQgPT4gY29udGV4dCksXG4gIH07XG59KTtcblxuLy8gSGVscGVyIHRvIGNyZWF0ZSBhbiBlcnJvciB0aGF0IG1pbWljcyBvdXIgbGlicmFyeSdzIGVycm9yc1xuZnVuY3Rpb24gY3JlYXRlTGlicmFyeUVycm9yKFxuICBtZXNzYWdlOiBzdHJpbmcsXG4gIGNvbnRleHQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge30sXG4pIHtcbiAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IobWVzc2FnZSkgYXMgRXJyb3IgJiB7XG4gICAgX19mcm9tRXJyb3JMaWJyYXJ5PzogYm9vbGVhbjtcbiAgICBjb250ZXh0PzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbiAgfTtcbiAgLy8gVGhpcyBpcyB0aGUga2V5IHByb3BlcnR5IG91ciBoYW5kbGVyIGxvb2tzIGZvclxuICBlcnJvci5fX2Zyb21FcnJvckxpYnJhcnkgPSB0cnVlO1xuICAvLyBBdHRhY2ggY29udGV4dCBwcm9wZXJ0aWVzXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGNvbnRleHQpKSB7XG4gICAgZXJyb3Jba2V5XSA9IHZhbHVlO1xuICB9XG4gIHJldHVybiBlcnJvcjtcbn1cblxuZGVzY3JpYmUoJ0dyYXBoUUwgRXJyb3IgSGFuZGxlciBNaWRkbGV3YXJlJywgKCkgPT4ge1xuICBjb25zdCBtb2NrR3FsSW5wdXQgPSB7XG4gICAgZXZlbnQ6IHtcbiAgICAgIGFyZ3VtZW50czogeyBpZDogJzEyMycgfSxcbiAgICAgIGlkZW50aXR5OiB7IHN1YjogJ3VzZXItc3ViJywgaXNzdWVyOiAnY29nbml0bycgfSxcbiAgICAgIGluZm86IHtcbiAgICAgICAgZmllbGROYW1lOiAnZ2V0VXNlcicsXG4gICAgICAgIHBhcmVudFR5cGVOYW1lOiAnUXVlcnknLFxuICAgICAgICB2YXJpYWJsZXM6IHsgaWQ6ICcxMjMnIH0sXG4gICAgICB9LFxuICAgICAgc291cmNlOiBudWxsLFxuICAgICAgcmVxdWVzdDogeyBoZWFkZXJzOiB7fSB9LFxuICAgIH0sXG4gICAgY29udGV4dDoge1xuICAgICAgYXdzUmVxdWVzdElkOiAnZ3FsLXJlcXVlc3QtaWQnLFxuICAgICAgZnVuY3Rpb25OYW1lOiAnZ2V0VXNlci1yZXNvbHZlcicsXG4gICAgfSxcbiAgfTtcblxuICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICB2aS5jbGVhckFsbE1vY2tzKCk7XG4gIH0pO1xuXG4gIGl0KCdzaG91bGQgY3JlYXRlIGEgbWlkZGxld2FyZSBmdW5jdGlvbicsICgpID0+IHtcbiAgICBjb25zdCBoYW5kbGVyID0gY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcigpO1xuICAgIGV4cGVjdChoYW5kbGVyKS50b0JlSW5zdGFuY2VPZihGdW5jdGlvbik7XG4gIH0pO1xuXG4gIGl0KCdzaG91bGQgbGV0IHN1Y2Nlc3NmdWwgcmVzdWx0cyBwYXNzIHRocm91Z2gnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3JIYW5kbGVyID0gY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcigpO1xuICAgIGNvbnN0IG5leHQgPSB2aS5mbigpLm1vY2tSZXNvbHZlZFZhbHVlKHsgaWQ6ICcxMjMnLCBuYW1lOiAnVGVzdCBVc2VyJyB9KTtcblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGVycm9ySGFuZGxlcihtb2NrR3FsSW5wdXQsIG5leHQpO1xuXG4gICAgZXhwZWN0KHJlc3VsdCkudG9FcXVhbCh7IGlkOiAnMTIzJywgbmFtZTogJ1Rlc3QgVXNlcicgfSk7XG4gICAgZXhwZWN0KGxvZ2dlci5lcnJvcikubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICBleHBlY3QobmV4dCkudG9IYXZlQmVlbkNhbGxlZFRpbWVzKDEpO1xuICB9KTtcblxuICBpdCgnc2hvdWxkIGNhdGNoLCBsb2csIGFuZCByZS10aHJvdyBlcnJvcnMgZnJvbSBvdXIgbGlicmFyeScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBlcnJvckhhbmRsZXIgPSBjcmVhdGVHcmFwaFFMRXJyb3JIYW5kbGVyKHsgaW5jbHVkZVN0YWNrVHJhY2U6IHRydWUgfSk7XG4gICAgY29uc3QgbGlicmFyeUVycm9yID0gY3JlYXRlTGlicmFyeUVycm9yKCdEYXRhYmFzZSBjb25uZWN0aW9uIGZhaWxlZCcsIHtcbiAgICAgIGRiOiAndXNlcnMtZGInLFxuICAgIH0pO1xuICAgIGNvbnN0IG5leHQgPSB2aS5mbigpLm1vY2tSZWplY3RlZFZhbHVlKGxpYnJhcnlFcnJvcik7XG5cbiAgICBhd2FpdCBleHBlY3QoZXJyb3JIYW5kbGVyKG1vY2tHcWxJbnB1dCwgbmV4dCkpLnJlamVjdHMudG9UaHJvdyhcbiAgICAgICdEYXRhYmFzZSBjb25uZWN0aW9uIGZhaWxlZCcsXG4gICAgKTtcblxuICAgIGV4cGVjdChsb2dnZXIuZXJyb3IpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKFxuICAgICAgJ0dyYXBoUUwgcmVzb2x2ZXIgZXJyb3I6IERhdGFiYXNlIGNvbm5lY3Rpb24gZmFpbGVkJyxcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgZGI6ICd1c2Vycy1kYicsXG4gICAgICAgIGdyYXBocWw6IHtcbiAgICAgICAgICB0eXBlTmFtZTogJ1F1ZXJ5JyxcbiAgICAgICAgICBmaWVsZE5hbWU6ICdnZXRVc2VyJyxcbiAgICAgICAgICBhcmd1bWVudHM6IHsgaWQ6ICcxMjMnIH0sXG4gICAgICAgIH0sXG4gICAgICAgIGlkZW50aXR5OiBtb2NrR3FsSW5wdXQuZXZlbnQuaWRlbnRpdHksXG4gICAgICAgIHN0YWNrOiBleHBlY3QuYW55KFN0cmluZyksXG4gICAgICB9KSxcbiAgICApO1xuICB9KTtcblxuICBpdCgnc2hvdWxkIGhhbmRsZSBub24tc3RhbmRhcmQgZXJyb3JzIGJ5IHdyYXBwaW5nIHRoZW0nLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3JIYW5kbGVyID0gY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcigpO1xuICAgIGNvbnN0IG5vblN0YW5kYXJkRXJyb3IgPSBuZXcgRXJyb3IoJ1NvbWV0aGluZyB1bmV4cGVjdGVkIGhhcHBlbmVkJyk7XG4gICAgY29uc3QgbmV4dCA9IHZpLmZuKCkubW9ja1JlamVjdGVkVmFsdWUobm9uU3RhbmRhcmRFcnJvcik7XG5cbiAgICBhd2FpdCBleHBlY3QoZXJyb3JIYW5kbGVyKG1vY2tHcWxJbnB1dCwgbmV4dCkpLnJlamVjdHMudG9UaHJvdyhcbiAgICAgICdOb24tc3RhbmRhcmQgZXJyb3IgdGhyb3duIHRvIEdyYXBoUUwgZXJyb3IgaGFuZGxlcicsXG4gICAgKTtcblxuICAgIGV4cGVjdChsb2dnZXIud2FybikudG9IYXZlQmVlbkNhbGxlZFdpdGgoXG4gICAgICAnTm9uLXN0YW5kYXJkIGVycm9yIHRocm93biwgd3JhcHBpbmcgd2l0aCB0aHJvd0Vycm9yJyxcbiAgICAgIHsgZXJyb3I6IG5vblN0YW5kYXJkRXJyb3IgfSxcbiAgICApO1xuXG4gICAgZXhwZWN0KGxvZ2dlci5lcnJvcikudG9IYXZlQmVlbkNhbGxlZFdpdGgoXG4gICAgICAnR3JhcGhRTCByZXNvbHZlciBlcnJvcjogTm9uLXN0YW5kYXJkIGVycm9yIHRocm93biB0byBHcmFwaFFMIGVycm9yIGhhbmRsZXInLFxuICAgICAgZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICBlcnJvclR5cGU6ICdvYmplY3QnLFxuICAgICAgICBvcmlnaW5hbEVycm9yOiBub25TdGFuZGFyZEVycm9yLFxuICAgICAgfSksXG4gICAgKTtcbiAgfSk7XG5cbiAgaXQoJ3Nob3VsZCBub3QgaW5jbHVkZSBzdGFjayB0cmFjZSBpbiBsb2dzIHdoZW4gZGlzYWJsZWQnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3JIYW5kbGVyID0gY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcih7XG4gICAgICBpbmNsdWRlU3RhY2tUcmFjZTogZmFsc2UsXG4gICAgfSk7XG4gICAgY29uc3QgbGlicmFyeUVycm9yID0gY3JlYXRlTGlicmFyeUVycm9yKCdUZXN0IGVycm9yJyk7XG4gICAgY29uc3QgbmV4dCA9IHZpLmZuKCkubW9ja1JlamVjdGVkVmFsdWUobGlicmFyeUVycm9yKTtcblxuICAgIGF3YWl0IGV4cGVjdChlcnJvckhhbmRsZXIobW9ja0dxbElucHV0LCBuZXh0KSkucmVqZWN0cy50b1Rocm93KCk7XG5cbiAgICBjb25zdCBlcnJvckxvZyA9IChsb2dnZXIuZXJyb3IgYXMgYW55KS5tb2NrLmNhbGxzWzBdWzFdO1xuICAgIGV4cGVjdChlcnJvckxvZykubm90LnRvSGF2ZVByb3BlcnR5KCdzdGFjaycpO1xuICB9KTtcblxuICBpdCgnc2hvdWxkIGluY2x1ZGUgZGVmYXVsdCBjb250ZXh0IGluIGxvZ3MnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3JIYW5kbGVyID0gY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcih7XG4gICAgICBkZWZhdWx0Q29udGV4dDogeyBzZXJ2aWNlOiAndXNlci1hcGknLCB2ZXJzaW9uOiAnMS4wLjEnIH0sXG4gICAgfSk7XG4gICAgY29uc3QgbGlicmFyeUVycm9yID0gY3JlYXRlTGlicmFyeUVycm9yKCdUZXN0IGVycm9yJyk7XG4gICAgY29uc3QgbmV4dCA9IHZpLmZuKCkubW9ja1JlamVjdGVkVmFsdWUobGlicmFyeUVycm9yKTtcblxuICAgIGF3YWl0IGV4cGVjdChlcnJvckhhbmRsZXIobW9ja0dxbElucHV0LCBuZXh0KSkucmVqZWN0cy50b1Rocm93KCk7XG5cbiAgICBleHBlY3QobG9nZ2VyLmVycm9yKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5hbnkoU3RyaW5nKSxcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgc2VydmljZTogJ3VzZXItYXBpJyxcbiAgICAgICAgdmVyc2lvbjogJzEuMC4xJyxcbiAgICAgIH0pLFxuICAgICk7XG4gIH0pO1xuXG4gIGl0KCdzaG91bGQgaGFuZGxlIGZhaWxlZCBtaWRkbGV3YXJlIGNvbnRleHQnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3JIYW5kbGVyID0gY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcigpO1xuICAgIGNvbnN0IGVycm9yV2l0aE1pZGRsZXdhcmVDb250ZXh0ID0gY3JlYXRlTGlicmFyeUVycm9yKCdBdXRoIGZhaWxlZCcsIHtcbiAgICAgIG1pZGRsZXdhcmVOYW1lOiAnYXV0aC1taWRkbGV3YXJlJyxcbiAgICAgIG1pZGRsZXdhcmVDaGFpbjogWydlcnJvci1oYW5kbGVyJywgJ2F1dGgtbWlkZGxld2FyZSddLFxuICAgIH0pO1xuICAgIGNvbnN0IG5leHQgPSB2aS5mbigpLm1vY2tSZWplY3RlZFZhbHVlKGVycm9yV2l0aE1pZGRsZXdhcmVDb250ZXh0KTtcblxuICAgIGF3YWl0IGV4cGVjdChlcnJvckhhbmRsZXIobW9ja0dxbElucHV0LCBuZXh0KSkucmVqZWN0cy50b1Rocm93KCk7XG5cbiAgICBleHBlY3QobG9nZ2VyLmVycm9yKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5hbnkoU3RyaW5nKSxcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgZmFpbGVkTWlkZGxld2FyZTogJ2F1dGgtbWlkZGxld2FyZScsXG4gICAgICAgIG1pZGRsZXdhcmVDaGFpbjogJ2Vycm9yLWhhbmRsZXIgLT4gYXV0aC1taWRkbGV3YXJlJyxcbiAgICAgIH0pLFxuICAgICk7XG4gIH0pO1xuXG4gIGl0KCdzaG91bGQgY29ycmVjdGx5IGV4dHJhY3QgQXBwU3luYyBldmVudCBpbmZvJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGVycm9ySGFuZGxlciA9IGNyZWF0ZUdyYXBoUUxFcnJvckhhbmRsZXIoKTtcbiAgICBjb25zdCBsaWJyYXJ5RXJyb3IgPSBjcmVhdGVMaWJyYXJ5RXJyb3IoJ0lucHV0IHZhbGlkYXRpb24gZmFpbGVkJyk7XG4gICAgY29uc3QgbmV4dCA9IHZpLmZuKCkubW9ja1JlamVjdGVkVmFsdWUobGlicmFyeUVycm9yKTtcblxuICAgIGF3YWl0IGV4cGVjdChlcnJvckhhbmRsZXIobW9ja0dxbElucHV0LCBuZXh0KSkucmVqZWN0cy50b1Rocm93KCk7XG5cbiAgICBleHBlY3QobG9nZ2VyLmVycm9yKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5zdHJpbmdDb250YWluaW5nKCdJbnB1dCB2YWxpZGF0aW9uIGZhaWxlZCcpLFxuICAgICAgZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICBncmFwaHFsOiB7XG4gICAgICAgICAgdHlwZU5hbWU6ICdRdWVyeScsXG4gICAgICAgICAgZmllbGROYW1lOiAnZ2V0VXNlcicsXG4gICAgICAgICAgYXJndW1lbnRzOiB7IGlkOiAnMTIzJyB9LFxuICAgICAgICB9LFxuICAgICAgICBpZGVudGl0eTogeyBzdWI6ICd1c2VyLXN1YicsIGlzc3VlcjogJ2NvZ25pdG8nIH0sXG4gICAgICAgIHJlcXVlc3RJZDogJ2dxbC1yZXF1ZXN0LWlkJyxcbiAgICAgIH0pLFxuICAgICk7XG4gIH0pO1xufSk7XG4iXX0=