1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-19 01:17:18 +02:00

feat: add support for cdnPrefix for static assets (#1191)

This commit is contained in:
Ivar Conradi Østhus 2022-01-06 10:31:00 +01:00 committed by GitHub
parent 2b59a4219a
commit 26b7da8b5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 147 additions and 121 deletions

View File

@ -63,6 +63,7 @@ Object {
"secureHeaders": false, "secureHeaders": false,
"server": Object { "server": Object {
"baseUriPath": "", "baseUriPath": "",
"cdnPrefix": undefined,
"enableRequestLogger": false, "enableRequestLogger": false,
"gracefulShutdownEnable": true, "gracefulShutdownEnable": true,
"gracefulShutdownTimeout": 1000, "gracefulShutdownTimeout": 1000,

View File

@ -11,32 +11,32 @@ jest.mock(
}, },
); );
const getApp = require('./app'); const getApp = require('./app').default;
test('should not throw when valid config', () => { test('should not throw when valid config', async () => {
const config = createTestConfig(); const config = createTestConfig();
const app = getApp(config, {}, {}); const app = await getApp(config, {}, {});
expect(typeof app.listen).toBe('function'); expect(typeof app.listen).toBe('function');
}); });
test('should call preHook', () => { test('should call preHook', async () => {
let called = 0; let called = 0;
const config = createTestConfig({ const config = createTestConfig({
preHook: () => { preHook: () => {
called++; called++;
}, },
}); });
getApp(config, {}, {}); await getApp(config, {}, {});
expect(called).toBe(1); expect(called).toBe(1);
}); });
test('should call preRouterHook', () => { test('should call preRouterHook', async () => {
let called = 0; let called = 0;
const config = createTestConfig({ const config = createTestConfig({
preRouterHook: () => { preRouterHook: () => {
called++; called++;
}, },
}); });
getApp(config, {}, {}); await getApp(config, {}, {});
expect(called).toBe(1); expect(called).toBe(1);
}); });

View File

@ -1,5 +1,4 @@
import { publicFolder } from 'unleash-frontend'; import { publicFolder } from 'unleash-frontend';
import fs from 'fs';
import express, { Application, RequestHandler } from 'express'; import express, { Application, RequestHandler } from 'express';
import cors from 'cors'; import cors from 'cors';
import compression from 'compression'; import compression from 'compression';
@ -22,23 +21,19 @@ import ossAuthentication from './middleware/oss-authentication';
import noAuthentication from './middleware/no-authentication'; import noAuthentication from './middleware/no-authentication';
import secureHeaders from './middleware/secure-headers'; import secureHeaders from './middleware/secure-headers';
import { rewriteHTML } from './util/rewriteHTML'; import { loadIndexHTML } from './util/load-index-html';
export default function getApp( export default async function getApp(
config: IUnleashConfig, config: IUnleashConfig,
stores: IUnleashStores, stores: IUnleashStores,
services: IUnleashServices, services: IUnleashServices,
unleashSession?: RequestHandler, unleashSession?: RequestHandler,
): Application { ): Promise<Application> {
const app = express(); const app = express();
const baseUriPath = config.server.baseUriPath || ''; const baseUriPath = config.server.baseUriPath || '';
let indexHTML = fs let indexHTML = await loadIndexHTML(config, publicFolder);
.readFileSync(path.join(publicFolder, 'index.html'))
.toString();
indexHTML = rewriteHTML(indexHTML, baseUriPath);
app.set('trust proxy', true); app.set('trust proxy', true);
app.disable('x-powered-by'); app.disable('x-powered-by');
@ -68,7 +63,7 @@ export default function getApp(
app.use(secureHeaders(config)); app.use(secureHeaders(config));
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use(favicon(path.join(publicFolder, 'favicon.ico'))); app.use(favicon(path.join(publicFolder, 'favicon.ico')));
app.use(baseUriPath, favicon(path.join(publicFolder, 'favicon.ico')));
app.use(baseUriPath, express.static(publicFolder, { index: false })); app.use(baseUriPath, express.static(publicFolder, { index: false }));
if (config.enableOAS) { if (config.enableOAS) {
@ -151,4 +146,3 @@ export default function getApp(
}); });
return app; return app;
} }
module.exports = getApp;

View File

@ -107,6 +107,7 @@ const defaultServerOption: IServerOption = {
host: process.env.HTTP_HOST, host: process.env.HTTP_HOST,
port: safeNumber(process.env.HTTP_PORT || process.env.PORT, 4242), port: safeNumber(process.env.HTTP_PORT || process.env.PORT, 4242),
baseUriPath: formatBaseUri(process.env.BASE_URI_PATH), baseUriPath: formatBaseUri(process.env.BASE_URI_PATH),
cdnPrefix: process.env.CDN_PREFIX,
unleashUrl: process.env.UNLEASH_URL || 'http://localhost:4242', unleashUrl: process.env.UNLEASH_URL || 'http://localhost:4242',
serverMetrics: true, serverMetrics: true,
keepAliveTimeout: minutesToMilliseconds(1), keepAliveTimeout: minutesToMilliseconds(1),

View File

@ -8,7 +8,7 @@ import getApp from '../app';
import User from '../types/user'; import User from '../types/user';
import sessionDb from './session-db'; import sessionDb from './session-db';
function getSetup(preRouterHook) { async function getSetup(preRouterHook) {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const config = createTestConfig({ const config = createTestConfig({
server: { baseUriPath: base }, server: { baseUriPath: base },
@ -23,7 +23,7 @@ function getSetup(preRouterHook) {
const stores = createStores(); const stores = createStores();
const services = createServices(stores, config); const services = createServices(stores, config);
const unleashSession = sessionDb(config, undefined); const unleashSession = sessionDb(config, undefined);
const app = getApp(config, stores, services, unleashSession); const app = await getApp(config, stores, services, unleashSession);
return { return {
base, base,
@ -31,17 +31,17 @@ function getSetup(preRouterHook) {
}; };
} }
test('should return 401 when missing user', () => { test('should return 401 when missing user', async () => {
expect.assertions(0); expect.assertions(0);
const { base, request } = getSetup(() => {}); const { base, request } = await getSetup(() => {});
return request.get(`${base}/api/protectedResource`).expect(401); return request.get(`${base}/api/protectedResource`).expect(401);
}); });
test('should return 200 when user exists', () => { test('should return 200 when user exists', async () => {
expect.assertions(0); expect.assertions(0);
const user = new User({ id: 1, email: 'some@mail.com' }); const user = new User({ id: 1, email: 'some@mail.com' });
const { base, request } = getSetup((app) => const { base, request } = await getSetup((app) =>
app.use((req, res, next) => { app.use((req, res, next) => {
req.user = user; req.user = user;
next(); next();

View File

@ -10,7 +10,7 @@ const uiConfig = {
slogan: 'hello', slogan: 'hello',
}; };
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const config = createTestConfig({ const config = createTestConfig({
server: { baseUriPath: base }, server: { baseUriPath: base },
@ -19,7 +19,7 @@ function getSetup() {
const stores = createStores(); const stores = createStores();
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
@ -36,8 +36,8 @@ let request;
let base; let base;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
request = setup.request; request = setup.request;
base = setup.base; base = setup.base;
destroy = setup.destroy; destroy = setup.destroy;

View File

@ -5,7 +5,7 @@ import { createServices } from '../../services';
import permissions from '../../../test/fixtures/permissions'; import permissions from '../../../test/fixtures/permissions';
import getApp from '../../app'; import getApp from '../../app';
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const perms = permissions(); const perms = permissions();
const config = createTestConfig({ const config = createTestConfig({
@ -15,7 +15,7 @@ function getSetup() {
const stores = createStores(); const stores = createStores();
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
@ -32,8 +32,8 @@ let base;
let request; let request;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
base = setup.base; base = setup.base;
request = setup.request; request = setup.request;
destroy = setup.destroy; destroy = setup.destroy;

View File

@ -5,7 +5,7 @@ import { createServices } from '../../services';
import permissions from '../../../test/fixtures/permissions'; import permissions from '../../../test/fixtures/permissions';
import getApp from '../../app'; import getApp from '../../app';
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const stores = createStores(); const stores = createStores();
const perms = permissions(); const perms = permissions();
@ -15,7 +15,7 @@ function getSetup() {
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
@ -23,9 +23,9 @@ function getSetup() {
}; };
} }
test('should render html preview of template', () => { test('should render html preview of template', async () => {
expect.assertions(0); expect.assertions(0);
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.get( .get(
`${base}/api/admin/email/preview/html/reset-password?name=Test%20Test`, `${base}/api/admin/email/preview/html/reset-password?name=Test%20Test`,
@ -35,9 +35,9 @@ test('should render html preview of template', () => {
.expect((res) => 'Test Test' in res.body); .expect((res) => 'Test Test' in res.body);
}); });
test('should render text preview of template', () => { test('should render text preview of template', async () => {
expect.assertions(0); expect.assertions(0);
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.get( .get(
`${base}/api/admin/email/preview/text/reset-password?name=Test%20Test`, `${base}/api/admin/email/preview/text/reset-password?name=Test%20Test`,
@ -47,9 +47,9 @@ test('should render text preview of template', () => {
.expect((res) => 'Test Test' in res.body); .expect((res) => 'Test Test' in res.body);
}); });
test('Requesting a non-existing template should yield 404', () => { test('Requesting a non-existing template should yield 404', async () => {
expect.assertions(0); expect.assertions(0);
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.get(`${base}/api/admin/email/preview/text/some-non-existing-template`) .get(`${base}/api/admin/email/preview/text/some-non-existing-template`)
.expect(404); .expect(404);

View File

@ -6,21 +6,21 @@ import createStores from '../../../test/fixtures/store';
import getApp from '../../app'; import getApp from '../../app';
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const stores = createStores(); const stores = createStores();
const config = createTestConfig({ const config = createTestConfig({
server: { baseUriPath: base }, server: { baseUriPath: base },
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { base, eventStore: stores.eventStore, request: supertest(app) }; return { base, eventStore: stores.eventStore, request: supertest(app) };
} }
test('should get empty events list via admin', () => { test('should get empty events list via admin', async () => {
expect.assertions(1); expect.assertions(1);
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.get(`${base}/api/admin/events`) .get(`${base}/api/admin/events`)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)

View File

@ -5,14 +5,14 @@ import getApp from '../../app';
import { createTestConfig } from '../../../test/config/test-config'; import { createTestConfig } from '../../../test/config/test-config';
import { createServices } from '../../services'; import { createServices } from '../../services';
function getSetup() { async function getSetup() {
const stores = createStores(); const stores = createStores();
const perms = permissions(); const perms = permissions();
const config = createTestConfig({ const config = createTestConfig({
preRouterHook: perms.hook, preRouterHook: perms.hook,
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
request: supertest(app), request: supertest(app),
@ -30,8 +30,8 @@ let stores;
let request; let request;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
stores = setup.stores; stores = setup.stores;
request = setup.request; request = setup.request;
destroy = setup.destroy; destroy = setup.destroy;

View File

@ -7,7 +7,7 @@ import { createServices } from '../../services';
let destroy; let destroy;
function getSetup() { async function getSetup() {
const randomBase = `/random${Math.round(Math.random() * 1000)}`; const randomBase = `/random${Math.round(Math.random() * 1000)}`;
const perms = permissions(); const perms = permissions();
const stores = createStores(); const stores = createStores();
@ -16,7 +16,7 @@ function getSetup() {
preRouterHook: perms.hook, preRouterHook: perms.hook,
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
destroy = () => { destroy = () => {
services.versionService.destroy(); services.versionService.destroy();
@ -36,8 +36,8 @@ afterEach(() => {
destroy(); destroy();
}); });
test('add version numbers for /strategies', () => { test('add version numbers for /strategies', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.get(`${base}/api/admin/strategies`) .get(`${base}/api/admin/strategies`)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
@ -47,8 +47,8 @@ test('add version numbers for /strategies', () => {
}); });
}); });
test('require a name when creating a new strategy', () => { test('require a name when creating a new strategy', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.post(`${base}/api/admin/strategies`) .post(`${base}/api/admin/strategies`)
.send({}) .send({})
@ -60,8 +60,8 @@ test('require a name when creating a new strategy', () => {
}); });
}); });
test('require parameters array when creating a new stratey', () => { test('require parameters array when creating a new strategy', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.post(`${base}/api/admin/strategies`) .post(`${base}/api/admin/strategies`)
.send({ name: 'TestStrat' }) .send({ name: 'TestStrat' })
@ -74,15 +74,15 @@ test('require parameters array when creating a new stratey', () => {
}); });
test('create a new strategy with empty parameters', async () => { test('create a new strategy with empty parameters', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.post(`${base}/api/admin/strategies`) .post(`${base}/api/admin/strategies`)
.send({ name: 'TestStrat', parameters: [] }) .send({ name: 'TestStrat', parameters: [] })
.expect(201); .expect(201);
}); });
test('not be possible to override name', () => { test('not be possible to override name', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
strategyStore.createStrategy({ name: 'Testing', parameters: [] }); strategyStore.createStrategy({ name: 'Testing', parameters: [] });
return request return request
@ -91,8 +91,8 @@ test('not be possible to override name', () => {
.expect(409); .expect(409);
}); });
test('update strategy', () => { test('update strategy', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
const name = 'AnotherStrat'; const name = 'AnotherStrat';
strategyStore.createStrategy({ name, parameters: [] }); strategyStore.createStrategy({ name, parameters: [] });
@ -102,8 +102,8 @@ test('update strategy', () => {
.expect(200); .expect(200);
}); });
test('not update unknown strategy', () => { test('not update unknown strategy', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
const name = 'UnknownStrat'; const name = 'UnknownStrat';
return request return request
.put(`${base}/api/admin/strategies/${name}`) .put(`${base}/api/admin/strategies/${name}`)
@ -111,8 +111,8 @@ test('not update unknown strategy', () => {
.expect(404); .expect(404);
}); });
test('validate format when updating strategy', () => { test('validate format when updating strategy', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
const name = 'AnotherStrat'; const name = 'AnotherStrat';
strategyStore.createStrategy({ name, parameters: [] }); strategyStore.createStrategy({ name, parameters: [] });
@ -122,16 +122,16 @@ test('validate format when updating strategy', () => {
.expect(400); .expect(400);
}); });
test('editable=false will stop delete request', () => { test('editable=false will stop delete request', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
const { request, base } = getSetup(); const { request, base } = await getSetup();
const name = 'default'; const name = 'default';
return request.delete(`${base}/api/admin/strategies/${name}`).expect(500); return request.delete(`${base}/api/admin/strategies/${name}`).expect(500);
}); });
test('editable=false will stop edit request', () => { test('editable=false will stop edit request', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
const { request, base } = getSetup(); const { request, base } = await getSetup();
const name = 'default'; const name = 'default';
return request return request
.put(`${base}/api/admin/strategies/${name}`) .put(`${base}/api/admin/strategies/${name}`)
@ -139,8 +139,8 @@ test('editable=false will stop edit request', () => {
.expect(500); .expect(500);
}); });
test('editable=true will allow delete request', () => { test('editable=true will allow delete request', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
const name = 'deleteStrat'; const name = 'deleteStrat';
strategyStore.createStrategy({ name, parameters: [] }); strategyStore.createStrategy({ name, parameters: [] });
@ -150,8 +150,8 @@ test('editable=true will allow delete request', () => {
.expect(200); .expect(200);
}); });
test('editable=true will allow edit request', () => { test('editable=true will allow edit request', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
const name = 'editStrat'; const name = 'editStrat';
strategyStore.createStrategy({ name, parameters: [] }); strategyStore.createStrategy({ name, parameters: [] });
@ -162,7 +162,7 @@ test('editable=true will allow edit request', () => {
}); });
test('deprecating a strategy works', async () => { test('deprecating a strategy works', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
const name = 'editStrat'; const name = 'editStrat';
strategyStore.createStrategy({ name, parameters: [] }); strategyStore.createStrategy({ name, parameters: [] });
@ -177,8 +177,8 @@ test('deprecating a strategy works', async () => {
.expect((res) => expect(res.body.deprecated).toBe(true)); .expect((res) => expect(res.body.deprecated).toBe(true));
}); });
test('deprecating a non-existent strategy yields 404', () => { test('deprecating a non-existent strategy yields 404', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.post(`${base}/api/admin/strategies/non-existent-strategy/deprecate`) .post(`${base}/api/admin/strategies/non-existent-strategy/deprecate`)
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
@ -186,7 +186,7 @@ test('deprecating a non-existent strategy yields 404', () => {
}); });
test('reactivating a strategy works', async () => { test('reactivating a strategy works', async () => {
const { request, base, strategyStore } = getSetup(); const { request, base, strategyStore } = await getSetup();
const name = 'editStrat'; const name = 'editStrat';
strategyStore.createStrategy({ name, parameters: [] }); strategyStore.createStrategy({ name, parameters: [] });
@ -201,16 +201,16 @@ test('reactivating a strategy works', async () => {
.expect((res) => expect(res.body.deprecated).toBe(false)); .expect((res) => expect(res.body.deprecated).toBe(false));
}); });
test('reactivating a non-existent strategy yields 404', () => { test('reactivating a non-existent strategy yields 404', async () => {
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.post(`${base}/api/admin/strategies/non-existent-strategy/reactivate`) .post(`${base}/api/admin/strategies/non-existent-strategy/reactivate`)
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(404); .expect(404);
}); });
test("deprecating 'default' strategy will yield 403", () => { test("deprecating 'default' strategy will yield 403", async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
const { request, base } = getSetup(); const { request, base } = await getSetup();
return request return request
.post(`${base}/api/admin/strategies/default/deprecate`) .post(`${base}/api/admin/strategies/default/deprecate`)
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')

View File

@ -5,7 +5,7 @@ import getApp from '../../app';
import { createTestConfig } from '../../../test/config/test-config'; import { createTestConfig } from '../../../test/config/test-config';
import { createServices } from '../../services'; import { createServices } from '../../services';
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const stores = createStores(); const stores = createStores();
const perms = permissions(); const perms = permissions();
@ -14,7 +14,7 @@ function getSetup() {
preRouterHook: perms.hook, preRouterHook: perms.hook,
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
@ -34,8 +34,8 @@ let tagStore;
let request; let request;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
base = setup.base; base = setup.base;
tagStore = setup.tagStore; tagStore = setup.tagStore;
request = setup.request; request = setup.request;

View File

@ -23,7 +23,7 @@ async function getSetup() {
server: { baseUriPath: base }, server: { baseUriPath: base },
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
userStore: stores.userStore, userStore: stores.userStore,

View File

@ -11,7 +11,7 @@ test('should enable prometheus', async () => {
const config = createTestConfig(); const config = createTestConfig();
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
const request = supertest(app); const request = supertest(app);

View File

@ -7,7 +7,7 @@ import FeatureController from './feature';
import { createTestConfig } from '../../../test/config/test-config'; import { createTestConfig } from '../../../test/config/test-config';
import { secondsToMilliseconds } from 'date-fns'; import { secondsToMilliseconds } from 'date-fns';
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const stores = createStores(); const stores = createStores();
const config = createTestConfig({ const config = createTestConfig({
@ -15,7 +15,7 @@ function getSetup() {
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
@ -35,8 +35,8 @@ let request;
let destroy; let destroy;
let featureToggleClientStore; let featureToggleClientStore;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
base = setup.base; base = setup.base;
request = setup.request; request = setup.request;
featureToggleClientStore = setup.featureToggleClientStore; featureToggleClientStore = setup.featureToggleClientStore;

View File

@ -7,12 +7,12 @@ import { createServices } from '../../services';
import { IUnleashStores } from '../../types'; import { IUnleashStores } from '../../types';
import { IUnleashOptions } from '../../server-impl'; import { IUnleashOptions } from '../../server-impl';
function getSetup(opts?: IUnleashOptions) { async function getSetup(opts?: IUnleashOptions) {
const stores = createStores(); const stores = createStores();
const config = createTestConfig(opts); const config = createTestConfig(opts);
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
request: supertest(app), request: supertest(app),
@ -29,8 +29,8 @@ let request;
let stores: IUnleashStores; let stores: IUnleashStores;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
request = setup.request; request = setup.request;
stores = setup.stores; stores = setup.stores;
destroy = setup.destroy; destroy = setup.destroy;
@ -83,7 +83,7 @@ test('should accept client metrics with yes/no', () => {
}); });
test('should accept client metrics with yes/no with metricsV2', async () => { test('should accept client metrics with yes/no with metricsV2', async () => {
const testRunner = getSetup({ const testRunner = await getSetup({
experimental: { metricsV2: { enabled: true } }, experimental: { metricsV2: { enabled: true } },
}); });
await testRunner.request await testRunner.request

View File

@ -5,11 +5,11 @@ import getLogger from '../../../test/fixtures/no-logger';
import getApp from '../../app'; import getApp from '../../app';
import { createServices } from '../../services'; import { createServices } from '../../services';
function getSetup() { async function getSetup() {
const stores = createStores(); const stores = createStores();
const config = createTestConfig(); const config = createTestConfig();
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
request: supertest(app), request: supertest(app),
@ -23,8 +23,8 @@ function getSetup() {
} }
let request; let request;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
request = setup.request; request = setup.request;
destroy = setup.destroy; destroy = setup.destroy;
}); });

View File

@ -7,11 +7,11 @@ import getLogger from '../../test/fixtures/no-logger';
import getApp from '../app'; import getApp from '../app';
import { IUnleashStores } from '../types'; import { IUnleashStores } from '../types';
function getSetup() { async function getSetup() {
const stores = createStores(); const stores = createStores();
const config = createTestConfig(); const config = createTestConfig();
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
request: supertest(app), request: supertest(app),
@ -26,8 +26,8 @@ function getSetup() {
let request; let request;
let destroy; let destroy;
let stores; let stores;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
request = setup.request; request = setup.request;
destroy = setup.destroy; destroy = setup.destroy;
stores = setup.stores; stores = setup.stores;
@ -38,7 +38,7 @@ afterEach(() => {
getLogger.setMuteError(false); getLogger.setMuteError(false);
}); });
test('should give 500 when db is failing', () => { test('should give 500 when db is failing', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
const config = createTestConfig(); const config = createTestConfig();
const failingStores: Partial<IUnleashStores> = { const failingStores: Partial<IUnleashStores> = {
@ -54,7 +54,7 @@ test('should give 500 when db is failing', () => {
// @ts-ignore // @ts-ignore
const services = createServices(failingStores, config); const services = createServices(failingStores, config);
// @ts-ignore // @ts-ignore
const app = getApp(createTestConfig(), failingStores, services); const app = await getApp(createTestConfig(), failingStores, services);
request = supertest(app); request = supertest(app);
getLogger.setMuteError(true); getLogger.setMuteError(true);
expect.assertions(2); expect.assertions(2);

View File

@ -4,14 +4,14 @@ import createStores from '../../test/fixtures/store';
import getApp from '../app'; import getApp from '../app';
import { createServices } from '../services'; import { createServices } from '../services';
function getSetup() { async function getSetup() {
const base = `/random${Math.round(Math.random() * 1000)}`; const base = `/random${Math.round(Math.random() * 1000)}`;
const stores = createStores(); const stores = createStores();
const config = createTestConfig({ const config = createTestConfig({
server: { baseUriPath: base }, server: { baseUriPath: base },
}); });
const services = createServices(stores, config); const services = createServices(stores, config);
const app = getApp(config, stores, services); const app = await getApp(config, stores, services);
return { return {
base, base,
@ -27,8 +27,8 @@ function getSetup() {
let base; let base;
let request; let request;
let destroy; let destroy;
beforeEach(() => { beforeEach(async () => {
const setup = getSetup(); const setup = await getSetup();
base = setup.base; base = setup.base;
request = setup.request; request = setup.request;
destroy = setup.destroy; destroy = setup.destroy;

View File

@ -57,7 +57,7 @@ async function createApp(
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
config.server.secret = secret; config.server.secret = secret;
} }
const app = getApp(config, stores, services, unleashSession); const app = await getApp(config, stores, services, unleashSession);
if (typeof config.eventHook === 'function') { if (typeof config.eventHook === 'function') {
addEventHook(config.eventHook, stores.eventStore); addEventHook(config.eventHook, stores.eventStore);

View File

@ -70,6 +70,7 @@ export interface IServerOption {
keepAliveTimeout: number; keepAliveTimeout: number;
headersTimeout: number; headersTimeout: number;
baseUriPath: string; baseUriPath: string;
cdnPrefix?: string;
unleashUrl: string; unleashUrl: string;
serverMetrics: boolean; serverMetrics: boolean;
enableRequestLogger: boolean; enableRequestLogger: boolean;

View File

@ -0,0 +1,24 @@
import fs from 'fs';
import { IUnleashConfig } from '../server-impl';
import { rewriteHTML } from './rewriteHTML';
import path from 'path';
import fetch from 'node-fetch';
export async function loadIndexHTML(
config: IUnleashConfig,
publicFolder: string,
): Promise<string> {
const { cdnPrefix, baseUriPath = '' } = config.server;
let indexHTML: string;
if (cdnPrefix) {
const res = await fetch(`${cdnPrefix}/index.html`);
indexHTML = await res.text();
} else {
indexHTML = fs
.readFileSync(path.join(publicFolder, 'index.html'))
.toString();
}
return rewriteHTML(indexHTML, baseUriPath, cdnPrefix);
}

View File

@ -1,7 +1,15 @@
export const rewriteHTML = (input: string, rewriteValue: string): string => { export const rewriteHTML = (
input: string,
rewriteValue: string,
cdnPrefix?: string,
): string => {
let result = input; let result = input;
result = result.replace(/::baseUriPath::/gi, rewriteValue); result = result.replace(/::baseUriPath::/gi, rewriteValue);
result = result.replace(/\/static/gi, `${rewriteValue}/static`); result = result.replace(/::cdnPrefix::/gi, cdnPrefix || '');
result = result.replace(
/\/static/gi,
`${cdnPrefix || rewriteValue}/static`,
);
return result; return result;
}; };

View File

@ -20,6 +20,7 @@ process.nextTick(async () => {
baseUriPath: '', baseUriPath: '',
// keepAliveTimeout: 1, // keepAliveTimeout: 1,
gracefulShutdownEnable: true, gracefulShutdownEnable: true,
// cdnPrefix: 'https://cdn.getunleash.io/unleash/v4.4.1',
}, },
logLevel: LogLevel.debug, logLevel: LogLevel.debug,
enableOAS: true, enableOAS: true,

View File

@ -25,7 +25,3 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig {
const options = mergeAll<IUnleashOptions>([testConfig, config]); const options = mergeAll<IUnleashOptions>([testConfig, config]);
return createConfig(options); return createConfig(options);
} }
module.exports = {
createTestConfig,
};

View File

@ -18,12 +18,12 @@ export interface IUnleashTest {
services: IUnleashServices; services: IUnleashServices;
} }
function createApp( async function createApp(
stores, stores,
adminAuthentication = IAuthType.NONE, adminAuthentication = IAuthType.NONE,
preHook?: Function, preHook?: Function,
customOptions?: any, customOptions?: any,
): IUnleashTest { ): Promise<IUnleashTest> {
const config = createTestConfig({ const config = createTestConfig({
authentication: { authentication: {
type: adminAuthentication, type: adminAuthentication,
@ -38,7 +38,7 @@ function createApp(
const unleashSession = sessionDb(config, undefined); const unleashSession = sessionDb(config, undefined);
const emitter = new EventEmitter(); const emitter = new EventEmitter();
emitter.setMaxListeners(0); emitter.setMaxListeners(0);
const app = getApp(config, stores, services, unleashSession); const app = await getApp(config, stores, services, unleashSession);
const request = supertest.agent(app); const request = supertest.agent(app);
const destroy = async () => { const destroy = async () => {

View File

@ -7246,7 +7246,7 @@ universalify@^0.1.0, universalify@^0.1.2:
resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
unleash-frontend@4.4.1: unleash-frontend@v4.4.1:
version "4.4.1" version "4.4.1"
resolved "https://registry.yarnpkg.com/unleash-frontend/-/unleash-frontend-4.4.1.tgz#753008e8a1a25b204edf23595261635f030b8590" resolved "https://registry.yarnpkg.com/unleash-frontend/-/unleash-frontend-4.4.1.tgz#753008e8a1a25b204edf23595261635f030b8590"
integrity sha512-hyVd56nbWkFdyEeCeHMVZjKlQyWu82QqzGT6IDKzYJruYpIk00/9dm7zycziZIwzu7GXfKkI4J6fnm6Ge7mB5g== integrity sha512-hyVd56nbWkFdyEeCeHMVZjKlQyWu82QqzGT6IDKzYJruYpIk00/9dm7zycziZIwzu7GXfKkI4J6fnm6Ge7mB5g==