Merge remote-tracking branch 'upstream/master'
Signed-off-by: Matteo Silvestri <matteosilv@gmail.com>
This commit is contained in:
+2
-2
@@ -65,7 +65,7 @@ describe('CacheManager', () => {
|
||||
const config = new ConfigReader({ backend: {} });
|
||||
expect(() => {
|
||||
CacheManager.fromConfig(config);
|
||||
}).not.toThrowError();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('throws on unknown cache store', () => {
|
||||
@@ -74,7 +74,7 @@ describe('CacheManager', () => {
|
||||
});
|
||||
expect(() => {
|
||||
CacheManager.fromConfig(config);
|
||||
}).toThrowError();
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(Math.abs(+child.deadline! - deadline)).toBeLessThan(50);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
jest.advanceTimersByTime(timeout + 1);
|
||||
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('results in minimum deadline when parent triggers sooner', async () => {
|
||||
@@ -66,15 +66,15 @@ describe('AbortContext', () => {
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(Math.abs(+parent.deadline! - parentDeadline)).toBeLessThan(50);
|
||||
expect(Math.abs(+child.deadline! - childDeadline)).toBeLessThan(50);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
jest.advanceTimersByTime(parentTimeout + 1);
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(1);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(1);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('results in minimum deadline when child triggers sooner', async () => {
|
||||
@@ -98,22 +98,22 @@ describe('AbortContext', () => {
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(Math.abs(+parent.deadline! - parentDeadline)).toBeLessThan(50);
|
||||
expect(Math.abs(+child.deadline! - childDeadline)).toBeLessThan(50);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
jest.advanceTimersByTime(childTimeout + 1);
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
|
||||
jest.advanceTimersByTime(parentTimeout - childTimeout + 1);
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(1);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(1);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('child carries over parent signal state if parent was already aborted and had no deadline', async () => {
|
||||
@@ -133,13 +133,13 @@ describe('AbortContext', () => {
|
||||
child.abortSignal.addEventListener('abort', childListener);
|
||||
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
expect(Math.abs(+child.deadline! - childDeadline)).toBeLessThan(50);
|
||||
|
||||
jest.advanceTimersByTime(childTimeout + 1);
|
||||
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0); // still
|
||||
expect(childListener).toHaveBeenCalledTimes(0); // still
|
||||
});
|
||||
|
||||
it('child carries over parent signal state if parent was already aborted and had a deadline', async () => {
|
||||
@@ -175,15 +175,15 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
parentController.abort();
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(1);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(1);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not signal parent when child is aborted', async () => {
|
||||
@@ -201,15 +201,15 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
childController.abort();
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('child carries over parent signal state if parent was already aborted', async () => {
|
||||
@@ -227,13 +227,13 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
childController.abort();
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('child carries over given signal state if it was already aborted', async () => {
|
||||
@@ -247,7 +247,7 @@ describe('AbortContext', () => {
|
||||
child.abortSignal.addEventListener('abort', childListener);
|
||||
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -267,15 +267,15 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
parentController.abort();
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(1);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(1);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not signal parent when child is aborted', async () => {
|
||||
@@ -293,15 +293,15 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(false);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
childController.abort();
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(false);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(parentListener).toBeCalledTimes(0);
|
||||
expect(childListener).toBeCalledTimes(1);
|
||||
expect(parentListener).toHaveBeenCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('child carries over parent signal state if parent was already aborted', async () => {
|
||||
@@ -319,13 +319,13 @@ describe('AbortContext', () => {
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
|
||||
childController.abort();
|
||||
|
||||
expect(parent.abortSignal.aborted).toBe(true);
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('child carries over given signal state if it was already aborted', async () => {
|
||||
@@ -339,7 +339,7 @@ describe('AbortContext', () => {
|
||||
child.abortSignal.addEventListener('abort', childListener);
|
||||
|
||||
expect(child.abortSignal.aborted).toBe(true);
|
||||
expect(childListener).toBeCalledTimes(0);
|
||||
expect(childListener).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,8 @@ import {
|
||||
} from './connection';
|
||||
import { PluginDatabaseManager } from './types';
|
||||
import path from 'path';
|
||||
import { Logger } from 'winston';
|
||||
import { stringifyError } from '@backstage/errors';
|
||||
|
||||
/**
|
||||
* Provides a config lookup path for a plugin's config block.
|
||||
@@ -44,6 +46,7 @@ function pluginPath(pluginId: string): string {
|
||||
*/
|
||||
export type DatabaseManagerOptions = {
|
||||
migrations?: PluginDatabaseManager['migrations'];
|
||||
logger?: Logger;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -339,6 +342,31 @@ export class DatabaseManager {
|
||||
schemaOverrides,
|
||||
);
|
||||
|
||||
return createDatabaseClient(pluginConfig, databaseClientOverrides);
|
||||
const client = createDatabaseClient(pluginConfig, databaseClientOverrides);
|
||||
this.startKeepaliveLoop(pluginId, client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private startKeepaliveLoop(pluginId: string, client: Knex): void {
|
||||
let lastKeepaliveFailed = false;
|
||||
|
||||
setInterval(() => {
|
||||
client.raw('select 1').then(
|
||||
() => {
|
||||
lastKeepaliveFailed = false;
|
||||
},
|
||||
(error: unknown) => {
|
||||
if (!lastKeepaliveFailed) {
|
||||
lastKeepaliveFailed = true;
|
||||
this.options?.logger?.warn(
|
||||
`Database keepalive failed for plugin ${pluginId}, ${stringifyError(
|
||||
error,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}, 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('database connection', () => {
|
||||
connection: '',
|
||||
}),
|
||||
),
|
||||
).toThrowError();
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('throws an error without a connection', () => {
|
||||
@@ -120,7 +120,7 @@ describe('database connection', () => {
|
||||
client: 'pg',
|
||||
}),
|
||||
),
|
||||
).toThrowError();
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -147,7 +147,7 @@ describe('database connection', () => {
|
||||
});
|
||||
|
||||
it('throws an error for unknown connection', () => {
|
||||
expect(() => createNameOverride('unknown', 'testname')).toThrowError();
|
||||
expect(() => createNameOverride('unknown', 'testname')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ describe('postgres', () => {
|
||||
'postgresql://postgres:pass@localhost:5432/dbname?sslrootcert=/path/to/file',
|
||||
),
|
||||
),
|
||||
).toThrowError(/no such file or directory/);
|
||||
).toThrow(/no such file or directory/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export function requestLoggingHandler(logger?: Logger): RequestHandler {
|
||||
return morgan('combined', {
|
||||
stream: {
|
||||
write(message: String) {
|
||||
actualLogger.info(message.trimRight());
|
||||
actualLogger.info(message.trimEnd());
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -551,7 +551,7 @@ describe('GithubUrlReader', () => {
|
||||
credentialsProvider: mockCredentialsProvider,
|
||||
},
|
||||
);
|
||||
}).toThrowError('must configure an explicit apiBaseUrl');
|
||||
}).toThrow('must configure an explicit apiBaseUrl');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ describe('GcsUrlReader', () => {
|
||||
|
||||
it('throws if search url looks truly glob-y', async () => {
|
||||
const glob = 'https://storage.cloud.google.com/bucket/**/path*';
|
||||
await expect(() => reader.search(glob)).rejects.toThrowError(
|
||||
await expect(() => reader.search(glob)).rejects.toThrow(
|
||||
'GcsUrlReader only supports prefix-based searches',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('ReadUrlResponseFactory', () => {
|
||||
it('buffer cannot be called after stream is called', async () => {
|
||||
const response = await ReadUrlResponseFactory.fromReadable(readable);
|
||||
response.stream!();
|
||||
expect(() => response.buffer()).toThrowError(ConflictError);
|
||||
expect(() => response.buffer()).toThrow(ConflictError);
|
||||
});
|
||||
|
||||
it('stream returns expected data', async () => {
|
||||
@@ -68,7 +68,7 @@ describe('ReadUrlResponseFactory', () => {
|
||||
it('stream cannot be called after buffer is called', async () => {
|
||||
const response = await ReadUrlResponseFactory.fromReadable(readable);
|
||||
response.buffer();
|
||||
expect(() => response.stream!()).toThrowError(ConflictError);
|
||||
expect(() => response.stream!()).toThrow(ConflictError);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('UrlReaderPredicateMux', () => {
|
||||
it('throws an error if no predicate matches', async () => {
|
||||
const mux = new UrlReaderPredicateMux(getVoidLogger());
|
||||
|
||||
await expect(mux.readUrl('http://foo/1')).rejects.toThrowError(
|
||||
await expect(mux.readUrl('http://foo/1')).rejects.toThrow(
|
||||
/^Reading from 'http:\/\/foo\/1' is not allowed. You may/,
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('UrlReaderPredicateMux', () => {
|
||||
|
||||
await expect(mux.readUrl('http://foo/1')).resolves.toBeUndefined();
|
||||
|
||||
await expect(mux.readUrl('http://bar/1')).rejects.toThrowError(
|
||||
await expect(mux.readUrl('http://bar/1')).rejects.toThrow(
|
||||
/^Reading from 'http:\/\/bar\/1' is not allowed. You may/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -66,9 +66,9 @@ describe('ServerTokenManager', () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(configWithSecret, {
|
||||
logger,
|
||||
});
|
||||
await expect(
|
||||
tokenManager.authenticate('random-string'),
|
||||
).rejects.toThrowError(/invalid server token/i);
|
||||
await expect(tokenManager.authenticate('random-string')).rejects.toThrow(
|
||||
/invalid server token/i,
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate server tokens created by a different instance using the same secret', async () => {
|
||||
@@ -129,7 +129,7 @@ describe('ServerTokenManager', () => {
|
||||
|
||||
const { token } = await tokenManager1.getToken();
|
||||
|
||||
await expect(tokenManager2.authenticate(token)).rejects.toThrowError(
|
||||
await expect(tokenManager2.authenticate(token)).rejects.toThrow(
|
||||
/invalid server token/i,
|
||||
);
|
||||
});
|
||||
@@ -145,7 +145,7 @@ describe('ServerTokenManager', () => {
|
||||
|
||||
const { token } = await noopTokenManager.getToken();
|
||||
|
||||
await expect(tokenManager.authenticate(token)).rejects.toThrowError(
|
||||
await expect(tokenManager.authenticate(token)).rejects.toThrow(
|
||||
/invalid server token/i,
|
||||
);
|
||||
});
|
||||
@@ -164,7 +164,7 @@ describe('ServerTokenManager', () => {
|
||||
|
||||
const { token } = await tokenManager2.getToken();
|
||||
|
||||
await expect(tokenManager1.authenticate(token)).rejects.toThrowError(
|
||||
await expect(tokenManager1.authenticate(token)).rejects.toThrow(
|
||||
/invalid server token/i,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user