mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-12 13:48:35 +02:00
chore!: remove deprecated default env from new installs (#10080)
**BREAKING CHANGE**: DEFAULT_ENV changed from `default` (should not be
used anymore) to `development`
## About the changes
- Only delete default env if the install is fresh new.
- Consider development the new default. The main consequence of this
change is that the default is no longer considered `type=production`
environment but also for frontend tokens due to this assumption:
724c4b78a2/src/lib/schema/api-token-schema.test.ts (L54-L59)
(I believe this is mostly due to the [support for admin
tokens](https://github.com/Unleash/unleash/pull/10080#discussion_r2126871567))
- `feature_toggle_update_total` metric reports `n/a` in environment and
environment type as it's not environment specific
This commit is contained in:
parent
d6f76a098e
commit
bdb763c9d5
@ -63,12 +63,12 @@ exports[`should match snapshot from /api/client/features 1`] = `
|
||||
},
|
||||
],
|
||||
"meta": {
|
||||
"etag": ""61824cd0:19"",
|
||||
"queryHash": "61824cd0",
|
||||
"etag": ""76d8bb0e:19"",
|
||||
"queryHash": "76d8bb0e",
|
||||
"revisionId": 19,
|
||||
},
|
||||
"query": {
|
||||
"environment": "default",
|
||||
"environment": "development",
|
||||
"inlineSegmentConstraints": true,
|
||||
},
|
||||
"version": 2,
|
||||
|
@ -21,9 +21,7 @@ let db: ITestDb;
|
||||
let eventStore: IEventStore;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('dependent_features', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('dependent_features', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -53,9 +53,9 @@ test('querying environments in OSS only returns environments that are included i
|
||||
.get('/api/admin/environments')
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.environments).toHaveLength(3);
|
||||
expect(res.body.environments).toHaveLength(2);
|
||||
const names = res.body.environments.map((env) => env.name);
|
||||
expect(names).toEqual(['default', 'development', 'production']);
|
||||
expect(names).toEqual(['development', 'production']);
|
||||
});
|
||||
});
|
||||
|
||||
@ -64,8 +64,8 @@ test('querying project environments in OSS only returns environments that are in
|
||||
.get('/api/admin/environments/project/default')
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.environments).toHaveLength(3);
|
||||
expect(res.body.environments).toHaveLength(2);
|
||||
const names = res.body.environments.map((env) => env.name);
|
||||
expect(names).toEqual(['default', 'development', 'production']);
|
||||
expect(names).toEqual(['development', 'production']);
|
||||
});
|
||||
});
|
||||
|
@ -12,9 +12,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('environment_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('environment_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
@ -34,24 +32,33 @@ afterAll(async () => {
|
||||
});
|
||||
|
||||
test('Can list all existing environments', async () => {
|
||||
await app.request
|
||||
const { body } = await app.request
|
||||
.get('/api/admin/environments')
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect((res) => {
|
||||
expect(res.body.version).toBe(1);
|
||||
expect(res.body.environments[0]).toStrictEqual({
|
||||
name: DEFAULT_ENV,
|
||||
enabled: true,
|
||||
sortOrder: 1,
|
||||
type: 'production',
|
||||
protected: true,
|
||||
requiredApprovals: null,
|
||||
projectCount: 1,
|
||||
apiTokenCount: 0,
|
||||
enabledToggleCount: 0,
|
||||
});
|
||||
});
|
||||
.expect('Content-Type', /json/);
|
||||
expect(body.version).toBe(1);
|
||||
expect(body.environments[0]).toStrictEqual({
|
||||
name: DEFAULT_ENV,
|
||||
enabled: true,
|
||||
sortOrder: 2,
|
||||
type: 'development',
|
||||
protected: false,
|
||||
requiredApprovals: null,
|
||||
projectCount: 1,
|
||||
apiTokenCount: 0,
|
||||
enabledToggleCount: 0,
|
||||
});
|
||||
expect(body.environments[1]).toStrictEqual({
|
||||
name: 'production',
|
||||
enabled: true,
|
||||
sortOrder: 3,
|
||||
type: 'production',
|
||||
protected: false,
|
||||
requiredApprovals: null,
|
||||
projectCount: 1,
|
||||
apiTokenCount: 0,
|
||||
enabledToggleCount: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('Can update sort order', async () => {
|
||||
|
@ -23,7 +23,6 @@ import { FeatureLifecycleReadModel } from './feature-lifecycle-read-model.js';
|
||||
import type { IFeatureLifecycleReadModel } from './feature-lifecycle-read-model-type.js';
|
||||
import { STAGE_ENTERED } from '../../metric-events.js';
|
||||
import type ClientInstanceService from '../metrics/instance/instance-service.js';
|
||||
import { DEFAULT_ENV } from '../../server-impl.js';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
@ -34,9 +33,7 @@ let featureLifecycleReadModel: IFeatureLifecycleReadModel;
|
||||
let clientInstanceService: ClientInstanceService;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_lifecycle', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('feature_lifecycle', getLogger);
|
||||
app = await setupAppWithAuth(
|
||||
db.stores,
|
||||
{
|
||||
@ -127,26 +124,27 @@ const getFeaturesLifecycleCount = async () => {
|
||||
};
|
||||
|
||||
test('should return lifecycle stages', async () => {
|
||||
const environment = 'production'; // prod environment moves lifecycle to live stage
|
||||
await app.createFeature('my_feature_a');
|
||||
await app.enableFeature('my_feature_a', DEFAULT_ENV);
|
||||
await app.enableFeature('my_feature_a', environment);
|
||||
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' });
|
||||
await reachedStage('my_feature_a', 'initial');
|
||||
await expectFeatureStage('my_feature_a', 'initial');
|
||||
eventBus.emit(CLIENT_METRICS_ADDED, [
|
||||
{
|
||||
featureName: 'my_feature_a',
|
||||
environment: DEFAULT_ENV,
|
||||
environment: environment,
|
||||
},
|
||||
{
|
||||
featureName: 'non_existent_feature',
|
||||
environment: DEFAULT_ENV,
|
||||
environment: environment,
|
||||
},
|
||||
]);
|
||||
|
||||
// missing feature
|
||||
eventBus.emit(CLIENT_METRICS_ADDED, [
|
||||
{
|
||||
environment: DEFAULT_ENV,
|
||||
environment: environment,
|
||||
yes: 0,
|
||||
no: 0,
|
||||
},
|
||||
@ -241,13 +239,14 @@ test('should backfill archived feature', async () => {
|
||||
});
|
||||
|
||||
test('should not backfill for existing lifecycle', async () => {
|
||||
const environment = 'production'; // prod environment moves lifecycle to live stage
|
||||
await app.createFeature('my_feature_e');
|
||||
await app.enableFeature('my_feature_e', DEFAULT_ENV);
|
||||
await app.enableFeature('my_feature_e', environment);
|
||||
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_e' });
|
||||
eventBus.emit(CLIENT_METRICS_ADDED, [
|
||||
{
|
||||
featureName: 'my_feature_e',
|
||||
environment: DEFAULT_ENV,
|
||||
environment: environment,
|
||||
},
|
||||
]);
|
||||
await reachedStage('my_feature_e', 'live');
|
||||
|
@ -18,9 +18,7 @@ let eventStore: IEventStore;
|
||||
let featureLinkReadModel: IFeatureLinksReadModel;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_link', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('feature_link', getLogger);
|
||||
app = await setupAppWithAuth(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -9,11 +9,9 @@ import {
|
||||
import getLogger from '../../../test/fixtures/no-logger.js';
|
||||
import type { FeatureSearchQueryParameters } from '../../openapi/spec/feature-search-query-parameters.js';
|
||||
import {
|
||||
CREATE_FEATURE_STRATEGY,
|
||||
DEFAULT_PROJECT,
|
||||
type IUnleashStores,
|
||||
TEST_AUDIT_USER,
|
||||
UPDATE_FEATURE_ENVIRONMENT,
|
||||
} from '../../types/index.js';
|
||||
import { DEFAULT_ENV } from '../../util/index.js';
|
||||
|
||||
@ -22,9 +20,7 @@ let db: ITestDb;
|
||||
let stores: IUnleashStores;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_search', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('feature_search', getLogger);
|
||||
app = await setupAppWithAuth(
|
||||
db.stores,
|
||||
{
|
||||
@ -46,38 +42,6 @@ beforeAll(async () => {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await stores.environmentStore.create({
|
||||
name: 'development',
|
||||
type: 'development',
|
||||
});
|
||||
|
||||
await app.linkProjectToEnvironment('default', 'development');
|
||||
|
||||
await stores.accessStore.addPermissionsToRole(
|
||||
body.rootRole,
|
||||
[
|
||||
{ name: UPDATE_FEATURE_ENVIRONMENT },
|
||||
{ name: CREATE_FEATURE_STRATEGY },
|
||||
],
|
||||
'development',
|
||||
);
|
||||
|
||||
await stores.environmentStore.create({
|
||||
name: 'production',
|
||||
type: 'production',
|
||||
});
|
||||
|
||||
await app.linkProjectToEnvironment('default', 'production');
|
||||
|
||||
await stores.accessStore.addPermissionsToRole(
|
||||
body.rootRole,
|
||||
[
|
||||
{ name: UPDATE_FEATURE_ENVIRONMENT },
|
||||
{ name: CREATE_FEATURE_STRATEGY },
|
||||
],
|
||||
'production',
|
||||
);
|
||||
|
||||
await app.services.userService.createUser(
|
||||
{
|
||||
username: 'admin@test.com',
|
||||
@ -865,11 +829,6 @@ test('should return segments in payload with no duplicates/nulls', async () => {
|
||||
name: 'my_feature_a',
|
||||
segments: [mySegment.name],
|
||||
environments: [
|
||||
{
|
||||
name: 'default',
|
||||
hasStrategies: true,
|
||||
hasEnabledStrategies: true,
|
||||
},
|
||||
{
|
||||
name: 'development',
|
||||
hasStrategies: true,
|
||||
@ -1183,11 +1142,6 @@ test('should return environment usage metrics and lifecycle', async () => {
|
||||
name: 'my_feature_b',
|
||||
lifecycle: { stage: 'completed', status: 'discarded' },
|
||||
environments: [
|
||||
{
|
||||
name: 'default',
|
||||
yes: 0,
|
||||
no: 0,
|
||||
},
|
||||
{
|
||||
name: 'development',
|
||||
yes: 10,
|
||||
@ -1448,7 +1402,6 @@ test('should return change request ids per environment', async () => {
|
||||
{
|
||||
name: 'my_feature_a',
|
||||
environments: [
|
||||
{ name: 'default', changeRequestIds: [] },
|
||||
{ name: 'development', changeRequestIds: [5, 6, 7] },
|
||||
{ name: 'production', changeRequestIds: [1] },
|
||||
],
|
||||
@ -1456,7 +1409,6 @@ test('should return change request ids per environment', async () => {
|
||||
{
|
||||
name: 'my_feature_b',
|
||||
environments: [
|
||||
{ name: 'default', changeRequestIds: [] },
|
||||
{ name: 'development', changeRequestIds: [8] },
|
||||
{ name: 'production', changeRequestIds: [] },
|
||||
],
|
||||
@ -1557,7 +1509,6 @@ test('should return release plan milestones', async () => {
|
||||
{
|
||||
name: 'my_feature_a',
|
||||
environments: [
|
||||
{ name: 'default' },
|
||||
{
|
||||
name: 'development',
|
||||
totalMilestones: 3,
|
||||
@ -1569,6 +1520,5 @@ test('should return release plan milestones', async () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(body.features[0].environments[0].milestoneName).toBeUndefined();
|
||||
expect(body.features[0].environments[2].milestoneName).toBeUndefined();
|
||||
expect(body.features[0].environments[1].milestoneName).toBeUndefined();
|
||||
});
|
||||
|
@ -11,9 +11,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('archive_test_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('archive_test_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
@ -125,26 +123,6 @@ test('Should disable all environments when reviving a toggle', async () => {
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'development',
|
||||
enabled: true,
|
||||
type: 'development',
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'production',
|
||||
enabled: true,
|
||||
type: 'production',
|
||||
sortOrder: 2,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
'feat-proj-1',
|
||||
'default',
|
||||
true,
|
||||
);
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
'feat-proj-1',
|
||||
'production',
|
||||
|
@ -64,9 +64,6 @@ beforeAll(async () => {
|
||||
db = await dbInit(
|
||||
'feature_toggle_service_v2_service_serial',
|
||||
config.getLogger,
|
||||
{
|
||||
dbInitMethod: 'legacy' as const,
|
||||
},
|
||||
);
|
||||
unleashConfig = config;
|
||||
stores = db.stores;
|
||||
|
@ -17,9 +17,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_strategy_auth_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('feature_strategy_auth_api_serial', getLogger);
|
||||
app = await setupAppWithAuth(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -87,9 +87,7 @@ const updateStrategy = async (
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_strategy_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('feature_strategy_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -27,9 +27,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
let frontendApiService: FrontendApiService;
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('frontend_api', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('frontend_api', getLogger);
|
||||
app = await setupAppWithAuth(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -417,7 +417,7 @@ describe('bulk metrics', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('filters out metrics for environments we do not have access for. No auth setup so we can only access default env', async () => {
|
||||
test('without access to production environment due to no auth setup, we can only access the default env', async () => {
|
||||
const now = new Date();
|
||||
|
||||
await request
|
||||
@ -437,7 +437,7 @@ describe('bulk metrics', () => {
|
||||
{
|
||||
featureName: 'test_feature_two',
|
||||
appName: 'test_application',
|
||||
environment: 'development',
|
||||
environment: 'production',
|
||||
timestamp: startOfHour(now),
|
||||
yes: 1000,
|
||||
no: 800,
|
||||
|
@ -13,9 +13,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('advanced_playground', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('advanced_playground', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ import dbInit, {
|
||||
} from '../../../test/e2e/helpers/database-init.js';
|
||||
import NotFoundError from '../../error/notfound-error.js';
|
||||
import {
|
||||
type IEnvironment,
|
||||
type IUnleashStores,
|
||||
SYSTEM_USER,
|
||||
SYSTEM_USER_AUDIT,
|
||||
@ -13,54 +14,48 @@ import NameExistsError from '../../error/name-exists-error.js';
|
||||
import type { EventService } from '../../services/index.js';
|
||||
import { createEventsService } from '../events/createEventsService.js';
|
||||
import { test, beforeAll, afterAll, expect } from 'vitest';
|
||||
import { DEFAULT_ENV } from '../../server-impl.js';
|
||||
let stores: IUnleashStores;
|
||||
let db: ITestDb;
|
||||
let service: EnvironmentService;
|
||||
let eventService: EventService;
|
||||
let createdEnvironment: IEnvironment;
|
||||
let withApprovals: IEnvironment;
|
||||
|
||||
beforeAll(async () => {
|
||||
const config = createTestConfig();
|
||||
db = await dbInit('environment_service_serial', config.getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('environment_service_serial', config.getLogger);
|
||||
stores = db.stores;
|
||||
eventService = createEventsService(db.rawDatabase, config);
|
||||
service = new EnvironmentService(stores, config, eventService);
|
||||
|
||||
createdEnvironment = await db.stores.environmentStore.create({
|
||||
name: 'testenv',
|
||||
type: 'production',
|
||||
});
|
||||
|
||||
withApprovals = await db.stores.environmentStore.create({
|
||||
name: 'approval_env',
|
||||
type: 'production',
|
||||
requiredApprovals: 1,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
test('Can get environment', async () => {
|
||||
const created = await db.stores.environmentStore.create({
|
||||
name: 'testenv',
|
||||
type: 'production',
|
||||
});
|
||||
|
||||
const retrieved = await service.get('testenv');
|
||||
expect(retrieved).toEqual(created);
|
||||
const retrieved = await service.get(createdEnvironment.name);
|
||||
expect(retrieved).toEqual(createdEnvironment);
|
||||
});
|
||||
|
||||
test('Can get all', async () => {
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'testenv2',
|
||||
type: 'production',
|
||||
});
|
||||
|
||||
const environments = await service.getAll();
|
||||
expect(environments).toHaveLength(3); // the one we created plus 'default'
|
||||
expect(environments).toHaveLength(4); // the 2 created plus 'development' and 'production''
|
||||
});
|
||||
|
||||
test('Can manage required approvals', async () => {
|
||||
const created = await db.stores.environmentStore.create({
|
||||
name: 'approval_env',
|
||||
type: 'production',
|
||||
requiredApprovals: 1,
|
||||
});
|
||||
|
||||
const retrieved = await service.get('approval_env');
|
||||
|
||||
const retrieved = await service.get(withApprovals.name);
|
||||
await db.stores.environmentStore.update(
|
||||
{
|
||||
type: 'production',
|
||||
@ -77,13 +72,16 @@ test('Can manage required approvals', async () => {
|
||||
const changeRequestEnvs =
|
||||
await db.stores.environmentStore.getChangeRequestEnvironments([
|
||||
'approval_env',
|
||||
DEFAULT_ENV,
|
||||
'development',
|
||||
'other',
|
||||
]);
|
||||
|
||||
expect(retrieved).toEqual(created);
|
||||
expect(updated).toEqual({ ...created, requiredApprovals: 2 });
|
||||
expect(groupRetrieved).toMatchObject({ ...created, requiredApprovals: 2 });
|
||||
expect(retrieved).toEqual(withApprovals);
|
||||
expect(updated).toEqual({ ...withApprovals, requiredApprovals: 2 });
|
||||
expect(groupRetrieved).toMatchObject({
|
||||
...withApprovals,
|
||||
requiredApprovals: 2,
|
||||
});
|
||||
expect(changeRequestEnvs).toEqual([
|
||||
{ name: 'approval_env', requiredApprovals: 2 },
|
||||
]);
|
||||
@ -345,32 +343,22 @@ test('When given overrides should remap projects to override environments', asyn
|
||||
expect(projects).not.toContain('default');
|
||||
});
|
||||
|
||||
test('Override works correctly when enabling default and disabling prod and dev', async () => {
|
||||
const defaultEnvironment = DEFAULT_ENV;
|
||||
const prodEnvironment = 'production';
|
||||
const devEnvironment = 'development';
|
||||
|
||||
test('Override works correctly when enabling a custom environment and disabling prod and dev', async () => {
|
||||
const newEnvironment = 'custom';
|
||||
await db.stores.environmentStore.create({
|
||||
name: prodEnvironment,
|
||||
name: newEnvironment,
|
||||
type: 'production',
|
||||
});
|
||||
|
||||
await db.stores.environmentStore.create({
|
||||
name: devEnvironment,
|
||||
type: 'development',
|
||||
});
|
||||
await service.toggleEnvironment(prodEnvironment, true);
|
||||
await service.toggleEnvironment(devEnvironment, true);
|
||||
|
||||
await service.overrideEnabledProjects([defaultEnvironment]);
|
||||
await service.toggleEnvironment(newEnvironment, true);
|
||||
await service.overrideEnabledProjects([newEnvironment]);
|
||||
|
||||
const environments = await service.getAll();
|
||||
const targetedEnvironment = environments.find(
|
||||
(env) => env.name === defaultEnvironment,
|
||||
(env) => env.name === newEnvironment,
|
||||
);
|
||||
|
||||
const allOtherEnvironments = environments
|
||||
.filter((x) => x.name !== defaultEnvironment)
|
||||
.filter((x) => x.name !== newEnvironment)
|
||||
.map((env) => env.enabled);
|
||||
const envNames = environments.map((x) => x.name);
|
||||
|
||||
|
@ -12,9 +12,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('project_environments_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('project_environments_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
@ -65,7 +63,7 @@ test('Should add environment to project', async () => {
|
||||
const environment = envs.find((env) => env.environment === 'test');
|
||||
|
||||
expect(environment).toBeDefined();
|
||||
expect(envs).toHaveLength(2); // test + default
|
||||
expect(envs).toHaveLength(3); // test + development + production
|
||||
});
|
||||
|
||||
test('Should validate environment', async () => {
|
||||
|
@ -16,9 +16,7 @@ let projectStore: IProjectStore;
|
||||
const testDate = '2023-10-01T12:34:56.000Z';
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('projects_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('projects_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -121,7 +121,6 @@ beforeAll(async () => {
|
||||
anonymiseEventLog: true,
|
||||
},
|
||||
},
|
||||
dbInitMethod: 'legacy' as const,
|
||||
};
|
||||
|
||||
db = await dbInit('segments_api_serial', getLogger, customOptions);
|
||||
|
@ -197,9 +197,7 @@ const createTestSegments = async () => {
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('segments', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('segments', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -128,7 +128,7 @@ test('should collect metrics for updated toggles', async () => {
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(
|
||||
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="default",environmentType="production",action="updated"\} 1/,
|
||||
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="n\/a",environmentType="n\/a",action="updated"\} 1/,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -902,8 +902,8 @@ export function registerPrometheusMetrics(
|
||||
featureFlagUpdateTotal.increment({
|
||||
toggle: featureName,
|
||||
project,
|
||||
environment: 'default',
|
||||
environmentType: 'production',
|
||||
environment: 'n/a',
|
||||
environmentType: 'n/a',
|
||||
action: 'updated',
|
||||
});
|
||||
});
|
||||
|
@ -27,9 +27,9 @@ export const apiTokenSchema = {
|
||||
},
|
||||
environment: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The environment the token has access to. `*` if it has access to all environments.',
|
||||
description: 'The environment the token has access to.',
|
||||
example: 'development',
|
||||
default: 'development',
|
||||
},
|
||||
project: {
|
||||
type: 'string',
|
||||
|
@ -1,7 +1,6 @@
|
||||
import joi from 'joi';
|
||||
import { ALL } from '../types/models/api-token.js';
|
||||
import { ApiTokenType } from '../types/model.js';
|
||||
import { DEFAULT_ENV } from '../util/constants.js';
|
||||
|
||||
export const createApiToken = joi
|
||||
.object()
|
||||
@ -14,10 +13,6 @@ export const createApiToken = joi
|
||||
.valid(ApiTokenType.CLIENT, ApiTokenType.FRONTEND),
|
||||
expiresAt: joi.date().optional(),
|
||||
projects: joi.array().min(1).optional().default([ALL]),
|
||||
environment: joi.when('type', {
|
||||
is: joi.string().valid(ApiTokenType.CLIENT, ApiTokenType.FRONTEND),
|
||||
then: joi.string().optional().default(DEFAULT_ENV),
|
||||
otherwise: joi.string().optional().default(ALL),
|
||||
}),
|
||||
environment: joi.string().optional().default('development'),
|
||||
})
|
||||
.options({ stripUnknown: true, allowUnknown: false, abortEarly: false });
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const DEFAULT_ENV = 'default';
|
||||
export const DEFAULT_ENV = 'development';
|
||||
|
||||
export const ALL_PROJECTS = '*';
|
||||
export const ALL_ENVS = '*';
|
||||
|
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
exports.up = function(db, cb) {
|
||||
db.runSql(
|
||||
`SELECT count(*) as count FROM events;`,
|
||||
(err, results) => {
|
||||
if (Number(results.rows[0].count) === 4) {
|
||||
// If there are exactly 4 events, it means this is a new install
|
||||
db.runSql(
|
||||
`DELETE FROM environments WHERE name = 'default';`,
|
||||
|
||||
cb);
|
||||
} else {
|
||||
// If there are not exactly 4 events, do nothing
|
||||
cb();
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
exports.down = function(db, cb) {
|
||||
cb();
|
||||
};
|
@ -11,9 +11,7 @@ let db: ITestDb;
|
||||
let app: IUnleashTest;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('context_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('context_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
@ -34,13 +32,15 @@ afterAll(async () => {
|
||||
|
||||
test('gets all context fields', async () => {
|
||||
expect.assertions(1);
|
||||
return app.request
|
||||
const { body } = await app.request
|
||||
.get('/api/admin/context')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.length).toBe(3);
|
||||
});
|
||||
.expect(200);
|
||||
|
||||
// because tests share the database, we might have more fields than expected
|
||||
expect(body.map((field) => field.name)).toEqual(
|
||||
expect.arrayContaining(['environment', 'userId', 'sessionId']),
|
||||
);
|
||||
});
|
||||
|
||||
test('get the context field', async () => {
|
||||
|
@ -15,9 +15,7 @@ let stores: IUnleashStores;
|
||||
let refreshDbMetrics: () => Promise<void>;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('instance_admin_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('instance_admin_api_serial', getLogger);
|
||||
stores = db.stores;
|
||||
await stores.settingStore.insert('instanceInfo', { id: 'test-static' });
|
||||
app = await setupAppWithCustomConfig(
|
||||
@ -126,7 +124,7 @@ test('should return signed instance statistics', async () => {
|
||||
.expect((res) => {
|
||||
expect(res.body.instanceId).toBe('test-static');
|
||||
expect(res.body.sum).toBe(
|
||||
'5ba2cb7c3e29f4e5b3382c560b92b837f3603dc7db73a501ec331c7f0ed17bd0',
|
||||
'd9bac94bba7afa20d98f0a9d54a84b79a6668f8103b8f89db85d05d38e84f519',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -11,9 +11,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('metrics_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('metrics_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
@ -179,9 +177,9 @@ test('should save multiple projects from token', async () => {
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
applications: [
|
||||
{
|
||||
expect(body.applications).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
appName: 'multi-project-app',
|
||||
usage: [
|
||||
{
|
||||
@ -193,8 +191,7 @@ test('should save multiple projects from token', async () => {
|
||||
project: 'mainProject',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
});
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
@ -12,9 +12,7 @@ let db: ITestDb;
|
||||
let user: IUser;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('project_health_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('project_health_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
|
@ -9,9 +9,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('strategy_api_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('strategy_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(db.stores, {
|
||||
experimental: {
|
||||
flags: {
|
||||
@ -19,6 +17,16 @@ beforeAll(async () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
await db.stores.strategyStore.createStrategy({
|
||||
name: 'toBeDeleted',
|
||||
description: 'toBeDeleted strategy',
|
||||
parameters: [],
|
||||
});
|
||||
await db.stores.strategyStore.createStrategy({
|
||||
name: 'toBeUpdated',
|
||||
description: 'toBeUpdated strategy',
|
||||
parameters: [],
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -29,13 +37,11 @@ afterAll(async () => {
|
||||
test('gets all strategies', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
return app.request
|
||||
const { body } = await app.request
|
||||
.get('/api/admin/strategies')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.strategies).toHaveLength(3);
|
||||
});
|
||||
.expect(200);
|
||||
expect(body.strategies).toHaveLength(6);
|
||||
});
|
||||
|
||||
test('gets a strategy by name', async () => {
|
||||
@ -93,9 +99,7 @@ test('refuses to create a strategy with an existing name', async () => {
|
||||
test('deletes a new strategy', async () => {
|
||||
expect.assertions(0);
|
||||
|
||||
return app.request
|
||||
.delete('/api/admin/strategies/usersWithEmail')
|
||||
.expect(200);
|
||||
return app.request.delete('/api/admin/strategies/toBeDeleted').expect(200);
|
||||
});
|
||||
|
||||
test("can't delete a strategy that dose not exist", async () => {
|
||||
@ -108,10 +112,10 @@ test('updates a exiting strategy', async () => {
|
||||
expect.assertions(0);
|
||||
|
||||
return app.request
|
||||
.put('/api/admin/strategies/default')
|
||||
.put('/api/admin/strategies/toBeUpdated')
|
||||
.send({
|
||||
name: 'default',
|
||||
description: 'Default is the best!',
|
||||
name: 'toBeUpdated',
|
||||
description: 'toBeUpdated is the best!',
|
||||
parameters: [],
|
||||
})
|
||||
.set('Content-Type', 'application/json')
|
||||
|
@ -15,9 +15,7 @@ const userId = -9999;
|
||||
const projectId = 'default';
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_env_api_client', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('feature_env_api_client', getLogger);
|
||||
app = await setupAppWithCustomConfig(db.stores, {}, db.rawDatabase);
|
||||
|
||||
await app.services.featureToggleService.createFeatureToggle(
|
||||
|
@ -152,26 +152,26 @@ describe.each([
|
||||
.expect(200);
|
||||
|
||||
if (etagVariant.feature_enabled) {
|
||||
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
||||
expect(res.headers.etag).toBe(`"76d8bb0e:16:${etagVariant.name}"`);
|
||||
expect(res.body.meta.etag).toBe(
|
||||
`"61824cd0:16:${etagVariant.name}"`,
|
||||
`"76d8bb0e:16:${etagVariant.name}"`,
|
||||
);
|
||||
} else {
|
||||
expect(res.headers.etag).toBe('"61824cd0:16"');
|
||||
expect(res.body.meta.etag).toBe('"61824cd0:16"');
|
||||
expect(res.headers.etag).toBe('"76d8bb0e:16"');
|
||||
expect(res.body.meta.etag).toBe('"76d8bb0e:16"');
|
||||
}
|
||||
});
|
||||
|
||||
test(`returns ${etagVariant.feature_enabled ? 200 : 304} for pre-calculated hash${etagVariant.feature_enabled ? ' because hash changed' : ''}`, async () => {
|
||||
const res = await app.request
|
||||
.get('/api/client/features')
|
||||
.set('if-none-match', '"61824cd0:16"')
|
||||
.set('if-none-match', '"76d8bb0e:16"')
|
||||
.expect(etagVariant.feature_enabled ? 200 : 304);
|
||||
|
||||
if (etagVariant.feature_enabled) {
|
||||
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
||||
expect(res.headers.etag).toBe(`"76d8bb0e:16:${etagVariant.name}"`);
|
||||
expect(res.body.meta.etag).toBe(
|
||||
`"61824cd0:16:${etagVariant.name}"`,
|
||||
`"76d8bb0e:16:${etagVariant.name}"`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -193,13 +193,13 @@ describe.each([
|
||||
.expect(200);
|
||||
|
||||
if (etagVariant.feature_enabled) {
|
||||
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
||||
expect(res.headers.etag).toBe(`"76d8bb0e:16:${etagVariant.name}"`);
|
||||
expect(res.body.meta.etag).toBe(
|
||||
`"61824cd0:16:${etagVariant.name}"`,
|
||||
`"76d8bb0e:16:${etagVariant.name}"`,
|
||||
);
|
||||
} else {
|
||||
expect(res.headers.etag).toBe('"61824cd0:16"');
|
||||
expect(res.body.meta.etag).toBe('"61824cd0:16"');
|
||||
expect(res.headers.etag).toBe('"76d8bb0e:16"');
|
||||
expect(res.body.meta.etag).toBe('"76d8bb0e:16"');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -16,9 +16,7 @@ let db: ITestDb;
|
||||
|
||||
let defaultToken: IApiToken;
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('metrics_two_api_client', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('metrics_two_api_client', getLogger);
|
||||
app = await setupAppWithAuth(db.stores, {}, db.rawDatabase);
|
||||
defaultToken =
|
||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||
|
@ -15,9 +15,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('register_client', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('register_client', getLogger);
|
||||
app = await setupApp(db.stores);
|
||||
});
|
||||
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { log } from 'db-migrate-shared';
|
||||
import { migrateDb } from '../../../migrator.js';
|
||||
import { createStores } from '../../../lib/db/index.js';
|
||||
import { createDb } from '../../../lib/db/db-pool.js';
|
||||
import { getDbConfig } from './database-config.js';
|
||||
import { createTestConfig } from '../../config/test-config.js';
|
||||
import dbState from './database.json' with { type: 'json' };
|
||||
import type { LogProvider } from '../../../lib/logger.js';
|
||||
import noLoggerProvider from '../../fixtures/no-logger.js';
|
||||
import type EnvironmentStore from '../../../lib/features/project-environments/environment-store.js';
|
||||
import type { IUnleashStores } from '../../../lib/types/index.js';
|
||||
import type { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store.js';
|
||||
import { DEFAULT_ENV } from '../../../lib/util/constants.js';
|
||||
import type {
|
||||
IUnleashConfig,
|
||||
IUnleashOptions,
|
||||
@ -27,72 +22,6 @@ process.setMaxListeners(0);
|
||||
|
||||
export const testDbPrefix = 'unleashtestdb_';
|
||||
|
||||
async function getDefaultEnvRolePermissions(knex) {
|
||||
return knex.table('role_permission').whereIn('environment', ['default']);
|
||||
}
|
||||
|
||||
async function restoreRolePermissions(knex, rolePermissions) {
|
||||
await knex.table('role_permission').insert(rolePermissions);
|
||||
}
|
||||
|
||||
async function resetDatabase(knex) {
|
||||
return Promise.all([
|
||||
knex
|
||||
.table('environments')
|
||||
.del(), // deletes role permissions transitively
|
||||
knex.table('strategies').del(),
|
||||
knex.table('features').del(),
|
||||
knex.table('client_applications').del(),
|
||||
knex.table('client_instances').del(),
|
||||
knex.table('context_fields').del(),
|
||||
knex.table('users').del(),
|
||||
knex.table('projects').del(),
|
||||
knex.table('tags').del(),
|
||||
knex.table('tag_types').del(),
|
||||
knex.table('addons').del(),
|
||||
knex.table('users').del(),
|
||||
knex.table('api_tokens').del(),
|
||||
knex.table('api_token_project').del(),
|
||||
knex
|
||||
.table('reset_tokens')
|
||||
.del(),
|
||||
// knex.table('settings').del(),
|
||||
]);
|
||||
}
|
||||
|
||||
function createStrategies(store) {
|
||||
return dbState.strategies.map((s) => store.createStrategy(s));
|
||||
}
|
||||
|
||||
function createContextFields(store) {
|
||||
return dbState.contextFields.map((c) => store.create(c));
|
||||
}
|
||||
|
||||
function createProjects(store) {
|
||||
return dbState.projects.map((i) => store.create(i));
|
||||
}
|
||||
|
||||
function createTagTypes(store) {
|
||||
return dbState.tag_types.map((t) => store.createTagType(t));
|
||||
}
|
||||
|
||||
async function connectProject(store: IFeatureEnvironmentStore): Promise<void> {
|
||||
await store.connectProject(DEFAULT_ENV, 'default');
|
||||
}
|
||||
|
||||
async function createEnvironments(store: EnvironmentStore): Promise<void> {
|
||||
await Promise.all(dbState.environments.map(async (e) => store.create(e)));
|
||||
}
|
||||
|
||||
async function setupDatabase(stores) {
|
||||
await createEnvironments(stores.environmentStore);
|
||||
await Promise.all(createStrategies(stores.strategyStore));
|
||||
await Promise.all(createContextFields(stores.contextFieldStore));
|
||||
await Promise.all(createProjects(stores.projectStore));
|
||||
await Promise.all(createTagTypes(stores.tagTypeStore));
|
||||
await connectProject(stores.featureEnvironmentStore);
|
||||
}
|
||||
|
||||
export interface ITestDb {
|
||||
config: IUnleashConfig;
|
||||
stores: IUnleashStores;
|
||||
@ -102,7 +31,6 @@ export interface ITestDb {
|
||||
}
|
||||
|
||||
type DBTestOptions = {
|
||||
dbInitMethod?: 'legacy' | 'template';
|
||||
stopMigrationAt?: string; // filename where migration should stop
|
||||
};
|
||||
|
||||
@ -112,16 +40,12 @@ export default async function init(
|
||||
configOverride: Partial<IUnleashOptions & DBTestOptions> = {},
|
||||
): Promise<ITestDb> {
|
||||
const testDbName = `${testDbPrefix}${uuidv4().replace(/-/g, '')}`;
|
||||
const useDbTemplate =
|
||||
(configOverride.dbInitMethod ?? 'template') === 'template';
|
||||
const testDBTemplateName = process.env.TEST_DB_TEMPLATE_NAME;
|
||||
const config = createTestConfig({
|
||||
db: {
|
||||
...getDbConfig(),
|
||||
pool: { min: 1, max: 4 },
|
||||
...(useDbTemplate
|
||||
? { database: testDbName }
|
||||
: { schema: databaseSchema }),
|
||||
database: testDbName,
|
||||
ssl: false,
|
||||
},
|
||||
...configOverride,
|
||||
@ -130,55 +54,30 @@ export default async function init(
|
||||
|
||||
log.setLogLevel('error');
|
||||
|
||||
if (useDbTemplate) {
|
||||
if (!testDBTemplateName) {
|
||||
throw new Error(
|
||||
'TEST_DB_TEMPLATE_NAME environment variable is not set',
|
||||
);
|
||||
}
|
||||
const client = new Client(getDbConfig());
|
||||
await client.connect();
|
||||
|
||||
await client.query(
|
||||
`CREATE DATABASE ${testDbName} TEMPLATE ${testDBTemplateName}`,
|
||||
if (!testDBTemplateName) {
|
||||
throw new Error(
|
||||
'TEST_DB_TEMPLATE_NAME environment variable is not set',
|
||||
);
|
||||
await client.query(`ALTER DATABASE ${testDbName} SET TIMEZONE TO UTC`);
|
||||
|
||||
await client.end();
|
||||
} else {
|
||||
const db = createDb(config);
|
||||
|
||||
await db.raw(`DROP SCHEMA IF EXISTS ${config.db.schema} CASCADE`);
|
||||
await db.raw(`CREATE SCHEMA IF NOT EXISTS ${config.db.schema}`);
|
||||
await migrateDb(config, configOverride.stopMigrationAt);
|
||||
await db.destroy();
|
||||
}
|
||||
const client = new Client(getDbConfig());
|
||||
await client.connect();
|
||||
|
||||
await client.query(
|
||||
`CREATE DATABASE ${testDbName} TEMPLATE ${testDBTemplateName}`,
|
||||
);
|
||||
await client.query(`ALTER DATABASE ${testDbName} SET TIMEZONE TO UTC`);
|
||||
|
||||
await client.end();
|
||||
|
||||
const testDb = createDb(config);
|
||||
const stores = createStores(config, testDb);
|
||||
stores.eventStore.setMaxListeners(0);
|
||||
|
||||
if (!useDbTemplate) {
|
||||
const defaultRolePermissions =
|
||||
await getDefaultEnvRolePermissions(testDb);
|
||||
await resetDatabase(testDb);
|
||||
await setupDatabase(stores);
|
||||
await restoreRolePermissions(testDb, defaultRolePermissions);
|
||||
}
|
||||
|
||||
return {
|
||||
config,
|
||||
rawDatabase: testDb,
|
||||
stores,
|
||||
reset: async () => {
|
||||
if (!useDbTemplate) {
|
||||
const defaultRolePermissions =
|
||||
await getDefaultEnvRolePermissions(testDb);
|
||||
await resetDatabase(testDb);
|
||||
await setupDatabase(stores);
|
||||
await restoreRolePermissions(testDb, defaultRolePermissions);
|
||||
}
|
||||
},
|
||||
reset: async () => {},
|
||||
destroy: async () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
testDb.destroy((error) => (error ? reject(error) : resolve()));
|
||||
|
@ -8,9 +8,7 @@ let stores: IUnleashStores;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('api_token_store_serial', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
db = await dbInit('api_token_store_serial', getLogger);
|
||||
stores = db.stores;
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user