mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-05 17:53:12 +02:00
Merge branch 'main' into archive_table
This commit is contained in:
commit
2490399cd0
10
package.json
10
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "unleash-server",
|
"name": "unleash-server",
|
||||||
"description": "Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.",
|
"description": "Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.",
|
||||||
"version": "4.13.0-beta.0",
|
"version": "4.13.0-beta.2",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"unleash",
|
"unleash",
|
||||||
"feature toggle",
|
"feature toggle",
|
||||||
@ -145,7 +145,7 @@
|
|||||||
"copyfiles": "2.4.1",
|
"copyfiles": "2.4.1",
|
||||||
"coveralls": "3.1.1",
|
"coveralls": "3.1.1",
|
||||||
"del-cli": "4.0.1",
|
"del-cli": "4.0.1",
|
||||||
"eslint": "8.16.0",
|
"eslint": "8.17.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "17.0.0",
|
"eslint-config-airbnb-typescript": "17.0.0",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
@ -156,16 +156,16 @@
|
|||||||
"husky": "8.0.1",
|
"husky": "8.0.1",
|
||||||
"jest": "27.5.1",
|
"jest": "27.5.1",
|
||||||
"lint-staged": "12.5.0",
|
"lint-staged": "12.5.0",
|
||||||
"nock": "^13.2.4",
|
"nock": "13.2.6",
|
||||||
"prettier": "2.6.2",
|
"prettier": "2.6.2",
|
||||||
"proxyquire": "2.1.3",
|
"proxyquire": "2.1.3",
|
||||||
"source-map-support": "0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
"superagent": "7.1.6",
|
"superagent": "7.1.6",
|
||||||
"supertest": "6.2.3",
|
"supertest": "6.2.3",
|
||||||
"ts-jest": "27.1.5",
|
"ts-jest": "27.1.5",
|
||||||
"ts-node": "10.8.0",
|
"ts-node": "10.8.1",
|
||||||
"tsc-watch": "5.0.3",
|
"tsc-watch": "5.0.3",
|
||||||
"typescript": "4.7.2"
|
"typescript": "4.7.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"async": "^3.2.3",
|
"async": "^3.2.3",
|
||||||
|
@ -89,6 +89,8 @@ Object {
|
|||||||
"unleashUrl": "http://localhost:4242",
|
"unleashUrl": "http://localhost:4242",
|
||||||
},
|
},
|
||||||
"session": Object {
|
"session": Object {
|
||||||
|
"clearSiteDataOnLogout": true,
|
||||||
|
"cookieName": "unleash-session",
|
||||||
"db": true,
|
"db": true,
|
||||||
"ttlHours": 48,
|
"ttlHours": 48,
|
||||||
},
|
},
|
||||||
|
@ -91,6 +91,11 @@ const defaultDbOptions: IDBOption = {
|
|||||||
|
|
||||||
const defaultSessionOption: ISessionOption = {
|
const defaultSessionOption: ISessionOption = {
|
||||||
ttlHours: parseEnvVarNumber(process.env.SESSION_TTL_HOURS, 48),
|
ttlHours: parseEnvVarNumber(process.env.SESSION_TTL_HOURS, 48),
|
||||||
|
clearSiteDataOnLogout: parseEnvVarBoolean(
|
||||||
|
process.env.SESSION_CLEAR_SITE_DATA_ON_LOGOUT,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
cookieName: 'unleash-session',
|
||||||
db: true,
|
db: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ function sessionDb(
|
|||||||
knex: Knex,
|
knex: Knex,
|
||||||
): RequestHandler {
|
): RequestHandler {
|
||||||
let store;
|
let store;
|
||||||
const { db } = config.session;
|
const { db, cookieName } = config.session;
|
||||||
const age =
|
const age =
|
||||||
hoursToMilliseconds(config.session.ttlHours) || hoursToMilliseconds(48);
|
hoursToMilliseconds(config.session.ttlHours) || hoursToMilliseconds(48);
|
||||||
const KnexSessionStore = knexSessionStore(session);
|
const KnexSessionStore = knexSessionStore(session);
|
||||||
@ -25,7 +25,7 @@ function sessionDb(
|
|||||||
store = new session.MemoryStore();
|
store = new session.MemoryStore();
|
||||||
}
|
}
|
||||||
return session({
|
return session({
|
||||||
name: 'unleash-session',
|
name: cookieName,
|
||||||
rolling: false,
|
rolling: false,
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
|
12
src/lib/openapi/spec/feature-variants-response.ts
Normal file
12
src/lib/openapi/spec/feature-variants-response.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
|
|
||||||
|
export const featureVariantsResponse: OpenAPIV3.ResponseObject = {
|
||||||
|
description: 'featureVariantResponse',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
$ref: '#/components/schemas/featureVariantsSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
26
src/lib/openapi/spec/feature-variants-schema.ts
Normal file
26
src/lib/openapi/spec/feature-variants-schema.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { createSchemaObject, CreateSchemaType } from '../types';
|
||||||
|
import { variantSchema } from './variant-schema';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['version', 'variants'],
|
||||||
|
properties: {
|
||||||
|
version: {
|
||||||
|
type: 'integer',
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
$ref: '#/components/schemas/variantSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'components/schemas': {
|
||||||
|
variantSchema,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FeatureVariantsSchema = CreateSchemaType<typeof schema>;
|
||||||
|
|
||||||
|
export const featureVariantsSchema = createSchemaObject(schema);
|
15
src/lib/openapi/spec/update-feature-variants-request.ts
Normal file
15
src/lib/openapi/spec/update-feature-variants-request.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
|
|
||||||
|
export const updateFeatureVariantsRequest: OpenAPIV3.RequestBodyObject = {
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
$ref: '#/components/schemas/variantSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -5,10 +5,14 @@ import { IUnleashConfig } from '../../../types/option';
|
|||||||
import { IUnleashServices } from '../../../types';
|
import { IUnleashServices } from '../../../types';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
import { UPDATE_FEATURE_VARIANTS } from '../../../types/permissions';
|
import { NONE, UPDATE_FEATURE_VARIANTS } from '../../../types/permissions';
|
||||||
import { IVariant } from '../../../types/model';
|
import { IVariant } from '../../../types/model';
|
||||||
import { extractUsername } from '../../../util/extract-user';
|
import { extractUsername } from '../../../util/extract-user';
|
||||||
import { IAuthRequest } from '../../unleash-types';
|
import { IAuthRequest } from '../../unleash-types';
|
||||||
|
import { featureVariantsResponse } from '../../../openapi/spec/feature-variants-response';
|
||||||
|
import { patchRequest } from '../../../openapi/spec/patch-request';
|
||||||
|
import { updateFeatureVariantsRequest } from '../../../openapi/spec/update-feature-variants-request';
|
||||||
|
import { FeatureVariantsSchema } from '../../../openapi/spec/feature-variants-schema';
|
||||||
|
|
||||||
const PREFIX = '/:projectId/features/:featureName/variants';
|
const PREFIX = '/:projectId/features/:featureName/variants';
|
||||||
|
|
||||||
@ -19,7 +23,6 @@ interface FeatureParams extends ProjectParam {
|
|||||||
interface ProjectParam {
|
interface ProjectParam {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VariantsController extends Controller {
|
export default class VariantsController extends Controller {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
@ -29,19 +32,59 @@ export default class VariantsController extends Controller {
|
|||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
{
|
{
|
||||||
featureToggleService,
|
featureToggleService,
|
||||||
}: Pick<IUnleashServices, 'featureToggleService'>,
|
openApiService,
|
||||||
|
}: Pick<IUnleashServices, 'featureToggleService' | 'openApiService'>,
|
||||||
) {
|
) {
|
||||||
super(config);
|
super(config);
|
||||||
this.logger = config.getLogger('admin-api/project/variants.ts');
|
this.logger = config.getLogger('admin-api/project/variants.ts');
|
||||||
this.featureService = featureToggleService;
|
this.featureService = featureToggleService;
|
||||||
this.get(PREFIX, this.getVariants);
|
this.route({
|
||||||
this.patch(PREFIX, this.patchVariants, UPDATE_FEATURE_VARIANTS);
|
method: 'get',
|
||||||
this.put(PREFIX, this.overwriteVariants, UPDATE_FEATURE_VARIANTS);
|
path: PREFIX,
|
||||||
|
permission: NONE,
|
||||||
|
acceptAnyContentType: true,
|
||||||
|
handler: this.getVariants,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'getFeatureVariants',
|
||||||
|
responses: { 200: featureVariantsResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
this.route({
|
||||||
|
method: 'patch',
|
||||||
|
path: PREFIX,
|
||||||
|
permission: UPDATE_FEATURE_VARIANTS,
|
||||||
|
handler: this.patchVariants,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'patchFeatureVariants',
|
||||||
|
requestBody: patchRequest,
|
||||||
|
responses: { 200: featureVariantsResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
this.route({
|
||||||
|
method: 'put',
|
||||||
|
path: PREFIX,
|
||||||
|
permission: UPDATE_FEATURE_VARIANTS,
|
||||||
|
handler: this.overwriteVariants,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'overwriteFeatureVariants',
|
||||||
|
requestBody: updateFeatureVariantsRequest,
|
||||||
|
responses: { 200: featureVariantsResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getVariants(
|
async getVariants(
|
||||||
req: Request<FeatureParams, any, any, any>,
|
req: Request<FeatureParams, any, any, any>,
|
||||||
res: Response,
|
res: Response<FeatureVariantsSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { featureName } = req.params;
|
const { featureName } = req.params;
|
||||||
const variants = await this.featureService.getVariants(featureName);
|
const variants = await this.featureService.getVariants(featureName);
|
||||||
@ -50,7 +93,7 @@ export default class VariantsController extends Controller {
|
|||||||
|
|
||||||
async patchVariants(
|
async patchVariants(
|
||||||
req: IAuthRequest<FeatureParams, any, Operation[]>,
|
req: IAuthRequest<FeatureParams, any, Operation[]>,
|
||||||
res: Response,
|
res: Response<FeatureVariantsSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { projectId, featureName } = req.params;
|
const { projectId, featureName } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
@ -69,7 +112,7 @@ export default class VariantsController extends Controller {
|
|||||||
|
|
||||||
async overwriteVariants(
|
async overwriteVariants(
|
||||||
req: IAuthRequest<FeatureParams, any, IVariant[], any>,
|
req: IAuthRequest<FeatureParams, any, IVariant[], any>,
|
||||||
res: Response,
|
res: Response<FeatureVariantsSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { projectId, featureName } = req.params;
|
const { projectId, featureName } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
|
@ -5,13 +5,12 @@ import SimplePasswordProvider from './auth/simple-password-provider';
|
|||||||
import { IUnleashConfig } from '../types/option';
|
import { IUnleashConfig } from '../types/option';
|
||||||
import { IUnleashServices } from '../types/services';
|
import { IUnleashServices } from '../types/services';
|
||||||
import { api } from './api-def';
|
import { api } from './api-def';
|
||||||
|
import LogoutController from './logout';
|
||||||
|
|
||||||
const AdminApi = require('./admin-api');
|
const AdminApi = require('./admin-api');
|
||||||
const ClientApi = require('./client-api');
|
const ClientApi = require('./client-api');
|
||||||
const Controller = require('./controller');
|
const Controller = require('./controller');
|
||||||
const HealthCheckController = require('./health-check');
|
const HealthCheckController = require('./health-check');
|
||||||
const LogoutController = require('./logout');
|
|
||||||
|
|
||||||
class IndexRouter extends Controller {
|
class IndexRouter extends Controller {
|
||||||
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
||||||
super(config);
|
super(config);
|
||||||
|
@ -44,6 +44,59 @@ test('should set "Clear-Site-Data" header', async () => {
|
|||||||
.expect('Clear-Site-Data', '"cookies", "storage"');
|
.expect('Clear-Site-Data', '"cookies", "storage"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should not set "Clear-Site-Data" header', async () => {
|
||||||
|
const baseUriPath = '';
|
||||||
|
const app = express();
|
||||||
|
const config = createTestConfig({
|
||||||
|
server: { baseUriPath },
|
||||||
|
session: { clearSiteDataOnLogout: false },
|
||||||
|
});
|
||||||
|
app.use('/logout', new LogoutController(config).router);
|
||||||
|
const request = supertest(app);
|
||||||
|
expect.assertions(1);
|
||||||
|
await request
|
||||||
|
.get(`${baseUriPath}/logout`)
|
||||||
|
.expect(302)
|
||||||
|
.expect((res) =>
|
||||||
|
expect(res.headers['Clear-Site-Data']).toBeUndefined(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should clear "unleash-session" cookies', async () => {
|
||||||
|
const baseUriPath = '';
|
||||||
|
const app = express();
|
||||||
|
const config = createTestConfig({ server: { baseUriPath } });
|
||||||
|
app.use('/logout', new LogoutController(config).router);
|
||||||
|
const request = supertest(app);
|
||||||
|
expect.assertions(0);
|
||||||
|
await request
|
||||||
|
.get(`${baseUriPath}/logout`)
|
||||||
|
.expect(302)
|
||||||
|
.expect(
|
||||||
|
'Set-Cookie',
|
||||||
|
'unleash-session=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should clear "unleash-session" cookie even when disabled clear site data', async () => {
|
||||||
|
const baseUriPath = '';
|
||||||
|
const app = express();
|
||||||
|
const config = createTestConfig({
|
||||||
|
server: { baseUriPath },
|
||||||
|
session: { clearSiteDataOnLogout: false },
|
||||||
|
});
|
||||||
|
app.use('/logout', new LogoutController(config).router);
|
||||||
|
const request = supertest(app);
|
||||||
|
expect.assertions(0);
|
||||||
|
await request
|
||||||
|
.get(`${baseUriPath}/logout`)
|
||||||
|
.expect(302)
|
||||||
|
.expect(
|
||||||
|
'Set-Cookie',
|
||||||
|
'unleash-session=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('should call destroy on session', async () => {
|
test('should call destroy on session', async () => {
|
||||||
const baseUriPath = '';
|
const baseUriPath = '';
|
||||||
const fakeSession = {
|
const fakeSession = {
|
||||||
|
@ -4,11 +4,17 @@ import Controller from './controller';
|
|||||||
import { IAuthRequest } from './unleash-types';
|
import { IAuthRequest } from './unleash-types';
|
||||||
|
|
||||||
class LogoutController extends Controller {
|
class LogoutController extends Controller {
|
||||||
|
private clearSiteDataOnLogout: boolean;
|
||||||
|
|
||||||
|
private cookieName: string;
|
||||||
|
|
||||||
private baseUri: string;
|
private baseUri: string;
|
||||||
|
|
||||||
constructor(config: IUnleashConfig) {
|
constructor(config: IUnleashConfig) {
|
||||||
super(config);
|
super(config);
|
||||||
this.baseUri = config.server.baseUriPath;
|
this.baseUri = config.server.baseUriPath;
|
||||||
|
this.clearSiteDataOnLogout = config.session.clearSiteDataOnLogout;
|
||||||
|
this.cookieName = config.session.cookieName;
|
||||||
this.get('/', this.logout);
|
this.get('/', this.logout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,10 +33,14 @@ class LogoutController extends Controller {
|
|||||||
req.logout();
|
req.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
res.set('Clear-Site-Data', '"cookies", "storage"');
|
res.clearCookie(this.cookieName);
|
||||||
|
|
||||||
|
if (this.clearSiteDataOnLogout) {
|
||||||
|
res.set('Clear-Site-Data', '"cookies", "storage"');
|
||||||
|
}
|
||||||
|
|
||||||
res.redirect(`${this.baseUri}/`);
|
res.redirect(`${this.baseUri}/`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = LogoutController;
|
|
||||||
export default LogoutController;
|
export default LogoutController;
|
||||||
|
@ -37,6 +37,8 @@ export interface IDBOption {
|
|||||||
export interface ISessionOption {
|
export interface ISessionOption {
|
||||||
ttlHours: number;
|
ttlHours: number;
|
||||||
db: boolean;
|
db: boolean;
|
||||||
|
clearSiteDataOnLogout: boolean;
|
||||||
|
cookieName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVersionOption {
|
export interface IVersionOption {
|
||||||
|
@ -266,6 +266,7 @@ test('PUTing an invalid variant throws 400 exception', async () => {
|
|||||||
name: 'variant',
|
name: 'variant',
|
||||||
weight: 500,
|
weight: 500,
|
||||||
weightType: 'party',
|
weightType: 'party',
|
||||||
|
stickiness: 'userId',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
await app.request
|
await app.request
|
||||||
|
@ -1520,6 +1520,142 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"/api/admin/projects/{projectId}/features/{featureName}/variants": Object {
|
||||||
|
"get": Object {
|
||||||
|
"operationId": "getFeatureVariants",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "projectId",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "featureName",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/featureVariantsSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "featureVariantResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"patch": Object {
|
||||||
|
"operationId": "patchFeatureVariants",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "projectId",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "featureName",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"requestBody": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"items": Object {
|
||||||
|
"$ref": "#/components/schemas/patchOperationSchema",
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
},
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/featureVariantsSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "featureVariantResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"put": Object {
|
||||||
|
"operationId": "overwriteFeatureVariants",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "projectId",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "featureName",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"requestBody": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"items": Object {
|
||||||
|
"$ref": "#/components/schemas/variantSchema",
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
},
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/featureVariantsSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "featureVariantResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"security": Array [
|
"security": Array [
|
||||||
Object {
|
Object {
|
||||||
|
@ -92,7 +92,7 @@ unleash.start(unleashOptions);
|
|||||||
- `sender` - Which email should be set as sender of mails being sent from Unleash?
|
- `sender` - Which email should be set as sender of mails being sent from Unleash?
|
||||||
- `smtpuser` - Username for your SMTP server
|
- `smtpuser` - Username for your SMTP server
|
||||||
- `smtppass` - Password for your SMTP server
|
- `smtppass` - Password for your SMTP server
|
||||||
- **eventHook** (`function(event, data)`) - If provided, this function will be invoked whenever a feature is mutated. The possible values for `event` are `'feature-created'`, `'feature-updated'`, `'feature-archived'`, `'feature-revived'`. The `data` argument contains information about the mutation. Its fields are `type` (string) - the event type (same as `event`); `createdBy` (string) - the user who performed the mutation; `data` - the contents of the change. The contents in `data` differs based on the event type; For `'feature-archived'` and `'feature-revived'`, the only field will be `name` - the name of the feature. For `'feature-created'` and `'feature-updated'` the data follows a schema defined in the code [here](https://github.com/Unleash/unleash/blob/master/src/lib/services/feature-schema.js#L65). See an [api here](/api/admin/events).
|
- **eventHook** (`function(event, data)`) - (_deprecated in Unleash 4.3_ in favor of the [Webhook addon](../addons/webhook.md)) If provided, this function will be invoked whenever a feature is mutated. The possible values for `event` are `'feature-created'`, `'feature-archived'` and `'feature-revived'`. The `data` argument contains information about the mutation. Its fields are `type` (string) - the event type (same as `event`); `createdBy` (string) - the user who performed the mutation; `data` - the contents of the change. The contents in `data` differs based on the event type; For `'feature-archived'` and `'feature-revived'`, the only field will be `name` - the name of the feature. For `'feature-created'` the data follows a schema defined in the code [here](https://github.com/Unleash/unleash/blob/7b7f0b84e8cddd5880dcf29c231672113224b9a7/src/lib/schema/feature-schema.ts#L77). See an [api here](/api/admin/events).
|
||||||
- **getLogger** (function) - Used to register a [custom log provider](#how-do-i-configure-the-log-output).
|
- **getLogger** (function) - Used to register a [custom log provider](#how-do-i-configure-the-log-output).
|
||||||
- **logLevel** (`debug` | `info` | `warn` | `error` | `fatal`) - The lowest level to log at, also configurable using environment variable `LOG_LEVEL`.
|
- **logLevel** (`debug` | `info` | `warn` | `error` | `fatal`) - The lowest level to log at, also configurable using environment variable `LOG_LEVEL`.
|
||||||
- **preHook** (function) - this is a hook if you need to provide any middlewares to express before `unleash` adds any. Express app instance is injected as first argument.
|
- **preHook** (function) - this is a hook if you need to provide any middlewares to express before `unleash` adds any. Express app instance is injected as first argument.
|
||||||
@ -322,3 +322,24 @@ Unleash builds directly on the [node-postgres library](https://node-postgres.com
|
|||||||
- Check the default values of connection pool about idle session handling.
|
- Check the default values of connection pool about idle session handling.
|
||||||
|
|
||||||
- If you have a network component which closes idle sessions on the TCP layer, make sure that the connection pool's `idleTimeoutMillis` setting is lower than the `timespan` setting. If it isn't, then the network component will close the connection.
|
- If you have a network component which closes idle sessions on the TCP layer, make sure that the connection pool's `idleTimeoutMillis` setting is lower than the `timespan` setting. If it isn't, then the network component will close the connection.
|
||||||
|
|
||||||
|
### Proxying requests from Unleash
|
||||||
|
|
||||||
|
You can configure proxy services that intercept all outgoing requests from Unleash. This lets you use the Microsoft Teams or the Webhook addon for external services, even if the internet can only be reached via a proxy on your machine or container (for example if restricted by a firewall or similiar).
|
||||||
|
|
||||||
|
As an example, here's how you could do it using the [node-global-proxy](https://www.npmjs.com/package/node-global-proxy) package:
|
||||||
|
```
|
||||||
|
const proxy = require("node-global-proxy").default;
|
||||||
|
|
||||||
|
proxy.setConfig({
|
||||||
|
http: "http://user:password@url:8080", //proxy adress, replace values as needed
|
||||||
|
//https: "https://user:password@url:1080", //if a https proxy is needed
|
||||||
|
});
|
||||||
|
|
||||||
|
proxy.start(); //this starts the proxy, after this call all requests will be proxied
|
||||||
|
```
|
||||||
|
|
||||||
|
Using above code-snippet, every outgoing request from unleash or its addons will be subsequently routed through set proxy.
|
||||||
|
If the proxy routing needs to be bypassed or stopped, its possible to stop it by using
|
||||||
|
|
||||||
|
`proxy.stop();`
|
||||||
|
@ -65,6 +65,6 @@
|
|||||||
"enhanced-resolve": "5.9.3",
|
"enhanced-resolve": "5.9.3",
|
||||||
"react-router": "6.3.0",
|
"react-router": "6.3.0",
|
||||||
"storybook-addon-root-attribute": "1.0.2",
|
"storybook-addon-root-attribute": "1.0.2",
|
||||||
"typescript": "4.7.2"
|
"typescript": "4.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
yarn.lock
39
yarn.lock
@ -2832,10 +2832,10 @@ eslint-visitor-keys@^3.3.0:
|
|||||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz"
|
||||||
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||||
|
|
||||||
eslint@8.16.0:
|
eslint@8.17.0:
|
||||||
version "8.16.0"
|
version "8.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.16.0.tgz#6d936e2d524599f2a86c708483b4c372c5d3bbae"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.17.0.tgz#1cfc4b6b6912f77d24b874ca1506b0fe09328c21"
|
||||||
integrity sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==
|
integrity sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc" "^1.3.0"
|
"@eslint/eslintrc" "^1.3.0"
|
||||||
"@humanwhocodes/config-array" "^0.9.2"
|
"@humanwhocodes/config-array" "^0.9.2"
|
||||||
@ -5078,11 +5078,6 @@ lodash.merge@^4.6.2:
|
|||||||
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
||||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||||
|
|
||||||
lodash.set@^4.3.2:
|
|
||||||
version "4.3.2"
|
|
||||||
resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz"
|
|
||||||
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=
|
|
||||||
|
|
||||||
lodash.sortby@^4.7.0:
|
lodash.sortby@^4.7.0:
|
||||||
version "4.7.0"
|
version "4.7.0"
|
||||||
resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz"
|
resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz"
|
||||||
@ -5575,14 +5570,14 @@ next-tick@~1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz"
|
||||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||||
|
|
||||||
nock@^13.2.4:
|
nock@13.2.6:
|
||||||
version "13.2.4"
|
version "13.2.6"
|
||||||
resolved "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz"
|
resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.6.tgz#35e419cd9d385ffa67e59523d9699e41b29e1a03"
|
||||||
integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug==
|
integrity sha512-GbyeSwSEP0FYouzETZ0l/XNm5tNcDNcXJKw3LCAb+mx8bZSwg1wEEvdL0FAyg5TkBJYiWSCtw6ag4XfmBy60FA==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
json-stringify-safe "^5.0.1"
|
json-stringify-safe "^5.0.1"
|
||||||
lodash.set "^4.3.2"
|
lodash "^4.17.21"
|
||||||
propagate "^2.0.0"
|
propagate "^2.0.0"
|
||||||
|
|
||||||
node-cleanup@^2.1.2:
|
node-cleanup@^2.1.2:
|
||||||
@ -7429,10 +7424,10 @@ ts-jest@27.1.5:
|
|||||||
semver "7.x"
|
semver "7.x"
|
||||||
yargs-parser "20.x"
|
yargs-parser "20.x"
|
||||||
|
|
||||||
ts-node@10.8.0:
|
ts-node@10.8.1:
|
||||||
version "10.8.0"
|
version "10.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.0.tgz#3ceb5ac3e67ae8025c1950626aafbdecb55d82ce"
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066"
|
||||||
integrity sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA==
|
integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cspotcode/source-map-support" "^0.8.0"
|
"@cspotcode/source-map-support" "^0.8.0"
|
||||||
"@tsconfig/node10" "^1.0.7"
|
"@tsconfig/node10" "^1.0.7"
|
||||||
@ -7586,10 +7581,10 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
|
resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
typescript@4.7.2:
|
typescript@4.7.3:
|
||||||
version "4.7.2"
|
version "4.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
|
||||||
integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==
|
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
|
||||||
|
|
||||||
uid-safe@~2.1.5:
|
uid-safe@~2.1.5:
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
|
Loading…
Reference in New Issue
Block a user