Skip to content

Commit 4f1bc5c

Browse files
authored
Merge pull request #219 from abraham/copilot/fix-218
Override createApp redirect_uris to always be list of URIs
2 parents 37809d2 + 5dde4c0 commit 4f1bc5c

File tree

4 files changed

+88
-16
lines changed

4 files changed

+88
-16
lines changed

dist/schema.json

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5115,20 +5115,12 @@
51155115
"description": "A name for your application"
51165116
},
51175117
"redirect_uris": {
5118-
"description": "String or Array of Strings. Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter.",
5119-
"oneOf": [
5120-
{
5121-
"type": "string",
5122-
"format": "uri"
5123-
},
5124-
{
5125-
"type": "array",
5126-
"items": {
5127-
"type": "string",
5128-
"format": "uri"
5129-
}
5130-
}
5131-
]
5118+
"type": "array",
5119+
"items": {
5120+
"type": "string",
5121+
"format": "uri"
5122+
},
5123+
"description": "String or Array of Strings. Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
51325124
},
51335125
"scopes": {
51345126
"type": "string",

src/__tests__/integration/create-app-parameters.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('Create App Method Parameters', () => {
5959
expect(scopes!.schema!.enum).toContain('admin:read');
6060
});
6161

62-
test('should generate oneOf schema for redirect_uris parameter', () => {
62+
test('should generate oneOf schema for redirect_uris parameter in TypeParser', () => {
6363
// Import required types and utilities
6464
const { TypeParser } = require('../../generators/TypeParser');
6565
const { UtilityHelpers } = require('../../generators/UtilityHelpers');
@@ -95,11 +95,13 @@ describe('Create App Method Parameters', () => {
9595
);
9696

9797
// Convert parameter to schema using TypeParser
98+
// Note: This tests the TypeParser in isolation, not the final OpenAPI schema
99+
// which gets overridden in MethodConverter for the createApp operation
98100
const utilityHelpers = new UtilityHelpers();
99101
const typeParser = new TypeParser(utilityHelpers);
100102
const schema = typeParser.convertParameterToSchema(redirectUrisParam);
101103

102-
// Verify oneOf schema is generated
104+
// Verify oneOf schema is generated by TypeParser
103105
expect(schema.oneOf).toBeDefined();
104106
expect(schema.oneOf).toHaveLength(2);
105107

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { MethodParser } from '../../parsers/MethodParser';
2+
import { EntityParser } from '../../parsers/EntityParser';
3+
import { OpenAPIGenerator } from '../../generators/OpenAPIGenerator';
4+
5+
describe('CreateApp redirect_uris override', () => {
6+
test('should always output redirect_uris as array of URIs for createApp operation', () => {
7+
const methodParser = new MethodParser();
8+
const entityParser = new EntityParser();
9+
10+
const entities = entityParser.parseAllEntities();
11+
const methodFiles = methodParser.parseAllMethods();
12+
13+
const generator = new OpenAPIGenerator();
14+
const spec = generator.generateSchema(entities, methodFiles);
15+
16+
// Find the createApp operation (POST /api/v1/apps)
17+
const appsPath = spec.paths['/api/v1/apps'];
18+
expect(appsPath).toBeDefined();
19+
expect(appsPath.post).toBeDefined();
20+
21+
// Check the request body schema
22+
const requestBody = appsPath.post!.requestBody as any;
23+
expect(requestBody).toBeDefined();
24+
expect(requestBody.content).toBeDefined();
25+
expect(requestBody.content['application/json']).toBeDefined();
26+
27+
const schema = requestBody.content['application/json'].schema as any;
28+
expect(schema).toBeDefined();
29+
expect(schema.properties).toBeDefined();
30+
31+
// Verify redirect_uris is always an array of URIs, not oneOf
32+
const redirectUrisProperty = schema.properties.redirect_uris;
33+
expect(redirectUrisProperty).toBeDefined();
34+
expect(redirectUrisProperty.type).toBe('array');
35+
expect(redirectUrisProperty.items).toEqual({
36+
type: 'string',
37+
format: 'uri',
38+
});
39+
40+
// Ensure it's NOT using oneOf pattern
41+
expect(redirectUrisProperty.oneOf).toBeUndefined();
42+
43+
// Verify the description is preserved
44+
expect(redirectUrisProperty.description).toContain(
45+
'String or Array of Strings'
46+
);
47+
});
48+
});

src/generators/MethodConverter.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,36 @@ class MethodConverter {
282282
},
283283
},
284284
};
285+
} else if (
286+
method.httpMethod === 'POST' &&
287+
path === '/api/v1/apps' &&
288+
properties.redirect_uris
289+
) {
290+
// Special handling for POST /api/v1/apps endpoint
291+
// Override redirect_uris to always be array of URIs instead of oneOf
292+
properties.redirect_uris = {
293+
type: 'array',
294+
items: {
295+
type: 'string',
296+
format: 'uri',
297+
},
298+
description: properties.redirect_uris.description,
299+
};
300+
301+
// Default behavior for createApp endpoint
302+
operation.requestBody = {
303+
description: 'JSON request body parameters',
304+
required: required.length > 0,
305+
content: {
306+
'application/json': {
307+
schema: {
308+
type: 'object',
309+
properties,
310+
required: required.length > 0 ? required : undefined,
311+
} as OpenAPIProperty,
312+
},
313+
},
314+
};
285315
} else {
286316
// Default behavior for all other endpoints
287317
operation.requestBody = {

0 commit comments

Comments
 (0)