Skip to content

Commit 96933d5

Browse files
authored
Closes #542 - added Switcher Relay to GraphQL API schema (#543)
1 parent b58ddac commit 96933d5

File tree

9 files changed

+243
-140
lines changed

9 files changed

+243
-140
lines changed

npm-shrinkwrap.json

Lines changed: 95 additions & 128 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"jsonwebtoken": "^9.0.2",
5151
"moment": "^2.30.1",
5252
"mongodb": "^6.10.0",
53-
"mongoose": "^8.7.3",
53+
"mongoose": "^8.8.0",
5454
"pino": "^9.5.0",
5555
"pino-pretty": "^11.3.0",
5656
"swagger-ui-express": "^5.0.1",
@@ -59,7 +59,7 @@
5959
},
6060
"devDependencies": {
6161
"env-cmd": "^10.1.0",
62-
"eslint": "^9.13.0",
62+
"eslint": "^9.14.0",
6363
"jest": "^29.7.0",
6464
"jest-sonar-reporter": "^2.0.0",
6565
"node-notifier": "^10.0.1",

requests/Switcher API.postman_collection.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5918,8 +5918,8 @@
59185918
"body": {
59195919
"mode": "graphql",
59205920
"graphql": {
5921-
"query": "query Domain(\r\n $_id: String, \r\n $name: String, \r\n $activated: Boolean, \r\n $environment: String, \r\n $_component: String) {\r\n domain(\r\n _id: $_id, \r\n name: $name, \r\n activated: $activated, \r\n environment: $environment,\r\n _component: $_component\r\n ) {\r\n _id\r\n name\r\n version\r\n description\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n activated\r\n group {\r\n _id\r\n name\r\n description\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n config {\r\n _id\r\n key\r\n description\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n strategies {\r\n _id\r\n strategy\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n operation\r\n values\r\n }\r\n components\r\n }\r\n }\r\n }\r\n}",
5922-
"variables": "{\r\n \"_id\": \"\",\r\n \"activated\": true,\r\n \"environment\": \"\",\r\n \"_component\": \"\"\r\n}"
5921+
"query": "query Domain(\r\n $_id: String, \r\n $name: String, \r\n $activated: Boolean, \r\n $environment: String, \r\n $_component: String) {\r\n domain(\r\n _id: $_id, \r\n name: $name, \r\n activated: $activated, \r\n environment: $environment,\r\n _component: $_component\r\n ) {\r\n _id\r\n name\r\n version\r\n description\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n activated\r\n group {\r\n _id\r\n name\r\n description\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n config {\r\n _id\r\n key\r\n description\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n strategies {\r\n _id\r\n strategy\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n operation\r\n values\r\n }\r\n relay {\r\n relay_type\r\n relay_method\r\n relay_endpoint\r\n description\r\n activated\r\n statusByEnv {\r\n env\r\n value\r\n }\r\n }\r\n components\r\n }\r\n }\r\n }\r\n}",
5922+
"variables": "{\r\n \"_id\": \"5e44eb76916dd10048d72542\",\r\n \"activated\": true,\r\n \"environment\": \"\",\r\n \"_component\": \"\"\r\n}"
59235923
}
59245924
},
59255925
"url": {

src/client/configuration-type.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GraphQLObjectType, GraphQLString, GraphQLList, GraphQLBoolean, GraphQLFloat } from 'graphql';
2-
import { resolveConfigStrategy, resolveConfig, resolveGroupConfig, resolveEnvStatus } from './resolvers.js';
2+
import { resolveConfigStrategy, resolveConfig, resolveGroupConfig, resolveEnvStatus, resolveRelay } from './resolvers.js';
33
import { EnvType } from '../models/environment.js';
44
import {
55
resolveFlatDomain,
@@ -53,6 +53,43 @@ export const strategyType = new GraphQLObjectType({
5353
}
5454
});
5555

56+
export const relayType = new GraphQLObjectType({
57+
name: 'Relay',
58+
fields: {
59+
relay_type: {
60+
type: GraphQLString,
61+
resolve: (source) => source.type
62+
},
63+
relay_method: {
64+
type: GraphQLString,
65+
resolve: (source) => source.method
66+
},
67+
relay_endpoint: {
68+
type: GraphQLString,
69+
resolve: (source, _args, { environment }) => {
70+
return source.endpoint[`${environment}`] === undefined ?
71+
source.endpoint[`${EnvType.DEFAULT}`] : source.endpoint[`${environment}`];
72+
}
73+
},
74+
description: {
75+
type: GraphQLString
76+
},
77+
activated: {
78+
type: GraphQLBoolean,
79+
resolve: (source, _args, { environment }) => {
80+
return source.activated[`${environment}`] === undefined ?
81+
source.activated[`${EnvType.DEFAULT}`] : source.activated[`${environment}`];
82+
}
83+
},
84+
statusByEnv: {
85+
type: new GraphQLList(envStatus),
86+
resolve: (source) => {
87+
return resolveEnvStatus(source);
88+
}
89+
},
90+
}
91+
});
92+
5693
export const configType = new GraphQLObjectType({
5794
name: 'Config',
5895
fields: {
@@ -103,6 +140,12 @@ export const configType = new GraphQLObjectType({
103140
resolve: async (source) => {
104141
return resolveComponents(source);
105142
}
143+
},
144+
relay: {
145+
type: relayType,
146+
resolve: (source, _args, context) => {
147+
return resolveRelay(source, context);
148+
}
106149
}
107150
}
108151
});

src/client/resolvers.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ export async function resolveConfigStrategy(source, _id, strategy, operation, ac
5252
return strategies;
5353
}
5454

55+
export async function resolveRelay(source, context) {
56+
const relay = source.relay;
57+
const environment = context.environment ? context.environment : EnvType.DEFAULT;
58+
59+
if (!relay.type || !relay.activated[environment] || !relay.endpoint[environment]) {
60+
return null;
61+
}
62+
63+
return relay;
64+
}
65+
5566
export async function resolveConfig(source, _id, key, activated, context) {
5667
const args = {};
5768

@@ -62,7 +73,7 @@ export async function resolveConfig(source, _id, key, activated, context) {
6273
let configs = await Config.find({ group: source._id, ...args }).lean().exec();
6374

6475
if (activated !== undefined) {
65-
configs = configs.filter(config => config.activated[context.environment || EnvType.DEFAULT] === activated);
76+
configs = configs.filter(config => isElementActive(config, context.environment, activated));
6677
}
6778

6879
try {
@@ -87,7 +98,7 @@ export async function resolveGroupConfig(source, _id, name, activated, context)
8798
let groups = await GroupConfig.find({ domain: source._id, ...args }).lean().exec();
8899

89100
if (activated !== undefined) {
90-
groups = groups.filter(group => group.activated[context.environment || EnvType.DEFAULT] === activated);
101+
groups = groups.filter(group => isElementActive(group, context.environment, activated));
91102
}
92103

93104
try {
@@ -119,7 +130,7 @@ export async function resolveDomain(_id, name, activated, context) {
119130
}
120131

121132
let domain = await Domain.findOne({ ...args }).lean().exec();
122-
if (activated !== undefined && domain?.activated[context.environment || EnvType.DEFAULT] !== activated) {
133+
if (activated !== undefined && !isElementActive(domain, context.environment, activated)) {
123134
return null;
124135
}
125136

@@ -135,6 +146,18 @@ export async function resolveDomain(_id, name, activated, context) {
135146
return domain;
136147
}
137148

149+
/**
150+
* Elemeents (Domain, Group, Config) can be activated in different environments.
151+
* When the environment is not defined, the default environment activated flag is used.
152+
*/
153+
function isElementActive(element, environment, activated) {
154+
if (element?.activated[environment || EnvType.DEFAULT] === undefined) {
155+
return element?.activated[EnvType.DEFAULT] === activated;
156+
}
157+
158+
return element?.activated[environment || EnvType.DEFAULT] === activated;
159+
}
160+
138161
/**
139162
* Resolve components first is used by SDKs to filter only configurations in which the component
140163
* exists resulting in a snapshot size reduction.

src/services/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ export function isRelayValid(relay) {
331331
}
332332

333333
const foundNotHttps = Object.values(relay.endpoint)
334-
.filter(endpoint => !endpoint.toLowerCase().startsWith('https'));
334+
.filter(endpoint => !String(endpoint).toLowerCase().startsWith('https'));
335335

336336
if (foundNotHttps.length) {
337337
throw new BadRequestError('HTTPS required');

tests/client-api.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ describe('Testing domain', () => {
5757
expect(JSON.parse(req.text)).toMatchObject(JSON.parse(graphqlUtils.expected102));
5858
});
5959

60+
test('CLIENT_SUITE - Should return the Domain structure for Environment', async () => {
61+
const req = await request(app)
62+
.post('/adm-graphql')
63+
.set('Authorization', `Bearer ${adminMasterAccountToken}`)
64+
.send(graphqlUtils.domainQuery([['_id', domainId], ['environment', 'QA']], true, true, true));
65+
66+
expect(req.statusCode).toBe(200);
67+
expect(JSON.parse(req.text)).toMatchObject(JSON.parse(graphqlUtils.expectedQA102));
68+
});
69+
6070
test('CLIENT_SUITE - Should return 2 switchers when NOT filtered by Component', async () => {
6171
const req = await request(app)
6272
.post('/adm-graphql')

tests/fixtures/db_client.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { randomUUID } from 'crypto';
55
import Admin from '../../src/models/admin';
66
import Domain from '../../src/models/domain';
77
import GroupConfig from '../../src/models/group-config';
8-
import { Config } from '../../src/models/config';
8+
import { Config, RelayMethods, RelayTypes } from '../../src/models/config';
99
import Component from '../../src/models/component';
1010
import History from '../../src/models/history';
1111
import { Metric } from '../../src/models/metric';
@@ -100,7 +100,14 @@ export const configDocument = {
100100
owner: adminMasterAccountId,
101101
components: [],
102102
group: groupConfigId,
103-
domain: domainId
103+
domain: domainId,
104+
relay: {
105+
type: RelayTypes.NOTIFICATION,
106+
method: RelayMethods.POST,
107+
endpoint: new Map().set(EnvType.DEFAULT, 'http://localhost:3000'),
108+
description: 'Test Relay',
109+
activated: new Map().set(EnvType.DEFAULT, true)
110+
}
104111
};
105112

106113
export const configStrategyUSERId = new mongoose.Types.ObjectId();

tests/graphql-utils/index.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ export const domainQuery = (where, group, config, strategy) => {
4242
strategies${elementQuery(strategy)} {
4343
strategy activated operation values statusByEnv { env value }
4444
}
45+
relay {
46+
relay_type
47+
relay_method
48+
relay_endpoint
49+
description
50+
activated
51+
statusByEnv {
52+
env
53+
value
54+
}
55+
}
4556
components
4657
}
4758
}
@@ -172,7 +183,49 @@ export const expected102 = `
172183
"values":[
173184
"10.0.0.0/24"
174185
]
175-
}]
186+
}],
187+
"relay": {
188+
"relay_type": "NOTIFICATION",
189+
"relay_method": "POST",
190+
"relay_endpoint": "http://localhost:3000",
191+
"description": "Test Relay",
192+
"activated": true
193+
}
194+
}]
195+
}]
196+
}
197+
}
198+
}`;
199+
200+
export const expectedQA102 = `
201+
{
202+
"data": {
203+
"domain": {
204+
"name": "Domain",
205+
"version": 5,
206+
"description": "Test Domain",
207+
"activated": true,
208+
"group": [{
209+
"name": "Group Test",
210+
"description": "Test Group",
211+
"activated": true,
212+
"config": [{
213+
"key": "TEST_CONFIG_KEY",
214+
"description": "Test config 1",
215+
"activated": true,
216+
"strategies": [],
217+
"relay": null,
218+
"components": [
219+
"TestApp"
220+
]
221+
},
222+
{
223+
"key": "TEST_CONFIG_KEY_PRD_QA",
224+
"description": "Test config 2 - Off in PRD and ON in QA",
225+
"activated": true,
226+
"strategies": [],
227+
"relay": null,
228+
"components": []
176229
}]
177230
}]
178231
}

0 commit comments

Comments
 (0)