Skip to content

Commit 91df1ea

Browse files
authored
feat: do not flatten securities (#15)
BREAKING CHANGE: getSecurities returns Security[][] now instead of Security[] and translateToSecurities returns HttpSecurityScheme[][] instead of HttpSecurityScheme[]
1 parent cff0310 commit 91df1ea

File tree

12 files changed

+304
-152
lines changed

12 files changed

+304
-152
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
},
3636
"dependencies": {
3737
"@stoplight/json": "^3.0.1",
38-
"@stoplight/types": "^9.1.2",
38+
"@stoplight/types": "^11.0.0",
3939
"@types/swagger-schema-official": "~2.0.18",
4040
"@types/urijs": "~1.19.1",
4141
"lodash": "^4.17.11",

src/oas2/__tests__/accessors.test.ts

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,90 @@ const securityDefinitionsFixture: Dictionary<Security> = {
2020
};
2121

2222
describe('accessors', () => {
23-
const securityFixture = [
23+
const securityFixture: Array<Dictionary<string[], string>> = [
2424
{
2525
api_key: [],
2626
petstore_auth: ['write:pets', 'read:pets'],
2727
},
2828
];
2929

30+
describe('relation between schemes', () => {
31+
describe('when all of the given schemes are expected to be validated against', () => {
32+
const securityFixtureWithAndRelation = securityFixture;
33+
34+
it('returns an array containing multiple elements', () => {
35+
expect(
36+
getSecurities(
37+
{
38+
securityDefinitions: securityDefinitionsFixture,
39+
security: [],
40+
},
41+
securityFixtureWithAndRelation,
42+
),
43+
).toEqual([
44+
[
45+
{
46+
in: 'header',
47+
name: 'api_key',
48+
type: 'apiKey',
49+
},
50+
{
51+
authorizationUrl: 'http://swagger.io/api/oauth/dialog',
52+
flow: 'implicit',
53+
scopes: {
54+
'read:pets': 'read your pets',
55+
'write:pets': 'modify pets in your account',
56+
},
57+
type: 'oauth2',
58+
},
59+
],
60+
]);
61+
});
62+
});
63+
64+
describe('when one of the given schemes is expected to be validated against', () => {
65+
it('returns arrays containing one element each', () => {
66+
const securityFixtureWithOrRelation: Array<Dictionary<string[], string>> = [
67+
{
68+
petstore_auth: ['write:pets', 'read:pets'],
69+
},
70+
{
71+
api_key: [],
72+
},
73+
];
74+
75+
expect(
76+
getSecurities(
77+
{
78+
securityDefinitions: securityDefinitionsFixture,
79+
security: [],
80+
},
81+
securityFixtureWithOrRelation,
82+
),
83+
).toEqual([
84+
[
85+
{
86+
authorizationUrl: 'http://swagger.io/api/oauth/dialog',
87+
flow: 'implicit',
88+
scopes: {
89+
'read:pets': 'read your pets',
90+
'write:pets': 'modify pets in your account',
91+
},
92+
type: 'oauth2',
93+
},
94+
],
95+
[
96+
{
97+
in: 'header',
98+
name: 'api_key',
99+
type: 'apiKey',
100+
},
101+
],
102+
]);
103+
});
104+
});
105+
});
106+
30107
describe('getSecurities', () => {
31108
test('given no security definitions should return empty array', () => {
32109
expect(
@@ -87,7 +164,7 @@ describe('accessors', () => {
87164
},
88165
],
89166
),
90-
).toEqual([{ in: 'header', name: 'api_key', type: 'apiKey' }]);
167+
).toEqual([[{ in: 'header', name: 'api_key', type: 'apiKey' }]]);
91168
});
92169

93170
test('given security with custom scopes should override global definition', () => {
@@ -104,12 +181,14 @@ describe('accessors', () => {
104181
],
105182
),
106183
).toEqual([
107-
{
108-
authorizationUrl: 'http://swagger.io/api/oauth/dialog',
109-
flow: 'implicit',
110-
scopes: { 'read:pets': 'read your pets', 'write:pets': 'modify pets in your account' },
111-
type: 'oauth2',
112-
},
184+
[
185+
{
186+
authorizationUrl: 'http://swagger.io/api/oauth/dialog',
187+
flow: 'implicit',
188+
scopes: { 'read:pets': 'read your pets', 'write:pets': 'modify pets in your account' },
189+
type: 'oauth2',
190+
},
191+
],
113192
]);
114193
});
115194

@@ -123,13 +202,15 @@ describe('accessors', () => {
123202
securityFixture,
124203
),
125204
).toEqual([
126-
{ in: 'header', name: 'api_key', type: 'apiKey' },
127-
{
128-
authorizationUrl: 'http://swagger.io/api/oauth/dialog',
129-
flow: 'implicit',
130-
scopes: { 'write:pets': 'modify pets in your account', 'read:pets': 'read your pets' },
131-
type: 'oauth2',
132-
},
205+
[
206+
{ in: 'header', name: 'api_key', type: 'apiKey' },
207+
{
208+
authorizationUrl: 'http://swagger.io/api/oauth/dialog',
209+
flow: 'implicit',
210+
scopes: { 'write:pets': 'modify pets in your account', 'read:pets': 'read your pets' },
211+
type: 'oauth2',
212+
},
213+
],
133214
]);
134215
});
135216
});

src/oas2/accessors.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { Dictionary } from '@stoplight/types';
2-
import { compact, flatten, get, map, merge } from 'lodash';
2+
import { compact, get, isEmpty, map, merge } from 'lodash';
3+
import { negate } from 'lodash/fp';
34
import { Operation, Security, Spec } from 'swagger-schema-official';
45

56
export function getSecurities(
67
spec: Partial<Spec>,
78
operationSecurity: Array<Dictionary<string[], string>> | undefined,
8-
): Security[] {
9+
): Security[][] {
910
const globalSecurities = getSecurity(spec.security, spec.securityDefinitions || {});
1011
const operationSecurities = getSecurity(operationSecurity, spec.securityDefinitions || {});
11-
return !!operationSecurity ? operationSecurities : globalSecurities;
12+
13+
const securities = !!operationSecurity ? operationSecurities : globalSecurities;
14+
15+
return securities.filter(negate(isEmpty));
1216
}
1317

1418
export function getProduces(spec: Partial<Spec>, operation: Partial<Operation>) {
@@ -22,21 +26,19 @@ export function getConsumes(spec: Partial<Spec>, operation: Partial<Operation>)
2226
function getSecurity(
2327
security: Array<Dictionary<string[], string>> | undefined,
2428
definitions: Dictionary<Security, string>,
25-
): Security[] {
26-
return flatten(
27-
map(security, sec => {
28-
return compact(
29-
map(Object.keys(sec), (key: string) => {
30-
const def = definitions[key];
31-
if (def) {
32-
const defCopy = merge<Object, Security>({}, def);
33-
return defCopy;
34-
}
35-
return null;
36-
}),
37-
);
38-
}),
39-
);
29+
): Security[][] {
30+
return map(security, sec => {
31+
return compact(
32+
map(Object.keys(sec), (key: string) => {
33+
const def = definitions[key];
34+
if (def) {
35+
const defCopy = merge<Object, Security>({}, def);
36+
return defCopy;
37+
}
38+
return null;
39+
}),
40+
);
41+
});
4042
}
4143

4244
function getProducesOrConsumes(which: 'produces' | 'consumes', spec: Partial<Spec>, operation: Partial<Operation>) {

0 commit comments

Comments
 (0)