Skip to content

Commit 7cec49c

Browse files
authored
feature: operationId generator v2 compatibility mode (#1283)
* add idFromPathMethod `v2CompatibilityMode` * add top-level `v2CompatibilityMode` option * add `v2CompatibilityMode` note to README * `v2CompatibilityMode` -> `v2OperationIdCompatibilityMode` MOAR clarity
1 parent 14063b5 commit 7cec49c

File tree

5 files changed

+99
-21
lines changed

5 files changed

+99
-21
lines changed

README.md

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ We'll be consolidating that soon. Just giving you the heads up. You may see refe
1717
### Usage
1818

1919
##### Prerequisites
20-
- Runtime:
21-
- browser: es5 compatible. IE11+
20+
- Runtime:
21+
- browser: es5 compatible. IE11+
2222
- node v4.x.x
2323
- Building
24-
- node v6.x.x
24+
- node v6.x.x
2525

2626
##### Download via npm
2727

@@ -34,7 +34,7 @@ npm install swagger-client
3434
```javascript
3535
import Swagger from 'swagger-client'
3636
// Or commonjs
37-
const Swagger = require('swagger-client')
37+
const Swagger = require('swagger-client')
3838
```
3939

4040
##### Import in browser
@@ -134,7 +134,7 @@ const params = {
134134

135135
parameters, // _named_ parameters in an object, eg: { petId: 'abc' }
136136
securities, // _named_ securities, will only be added to the request, if the spec indicates it. eg: {apiKey: 'abc'}
137-
requestContentType,
137+
requestContentType,
138138
responseContentType,
139139

140140
(http), // You can also override the HTTP client completely
@@ -166,11 +166,11 @@ Swagger('http://petstore.swagger.io/v2/swagger.json')
166166
client.originalSpec // In case you need it
167167
client.errors // Any resolver errors
168168

169-
// Tags interface
169+
// Tags interface
170170
client.apis.pet.addPet({id: 1, name: "bobby"}).then(...)
171171

172172
// TryItOut Executor, with the `spec` already provided
173-
client.execute({operationId: 'addPet', parameters: {id: 1, name: "bobby") }).then(...)
173+
client.execute({operationId: 'addPet', parameters: {id: 1, name: "bobby") }).then(...)
174174
})
175175

176176
```
@@ -190,13 +190,24 @@ Swagger({ url: "http://petstore.swagger.io/v2/swagger.json" }).then((client) =>
190190
.apis
191191
.pet // tag name == `pet`
192192
.addPet({ // operationId == `addPet`
193-
id: 1,
193+
id: 1,
194194
body: {
195195
name: "bobby",
196196
status: "available"
197197
}
198198
})
199-
.then(...)
199+
.then(...)
200+
})
201+
```
202+
203+
If you'd like to use the operationId formatting logic from Swagger-Client 2.x, set the `v2OperationIdCompatibilityMode` option:
204+
205+
```js
206+
Swagger({
207+
url: "http://petstore.swagger.io/v2/swagger.json",
208+
v2OperationIdCompatibilityMode: true
209+
}).then((client) => {
210+
// do things as usual
200211
})
201212
```
202213
@@ -221,14 +232,14 @@ Swagger({...}).then((client) => {
221232
apiPrefix: "v2"
222233
}
223234
})
224-
.then(...)
235+
.then(...)
225236
})
226237
```
227238
228-
In Browser
239+
In Browser
229240
----------
230241
231-
Prepare swagger-client.js by `npm run build-bundle`
242+
Prepare swagger-client.js by `npm run build-bundle`
232243
Note, browser version exports class `SwaggerClient` to global namespace
233244
If you need activate CORS requests, just enable it by `withCredentials` property at `http`
234245
@@ -248,7 +259,7 @@ var swaggerClient = new SwaggerClient(specUrl)
248259
console.error("failed to load the spec" + reason);
249260
})
250261
.then(function(addPetResult) {
251-
console.log(addPetResult.obj);
262+
console.log(addPetResult.obj);
252263
// you may return more promises, if necessary
253264
}, function (reason) {
254265
console.error("failed on API call " + reason);
@@ -316,7 +327,7 @@ npm run build-bundle # build browser version available at .../browser
316327

317328
# Migration from 2.x
318329

319-
There has been a complete overhaul of the codebase.
330+
There has been a complete overhaul of the codebase.
320331
For notes about how to migrate coming from 2.x,
321332
please see [Migration from 2.x](docs/MIGRATION_2_X.md)
322333

src/helpers.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,31 @@ export function isSwagger2(spec) {
2626
}
2727

2828
// Strategy for determining operationId
29-
export function opId(operation, pathName, method = '') {
29+
export function opId(operation, pathName, method = '', {v2OperationIdCompatibilityMode} = {}) {
3030
if (!operation || typeof operation !== 'object') {
3131
return null
3232
}
3333
const idWithoutWhitespace = (operation.operationId || '').replace(/\s/g, '')
3434
if (idWithoutWhitespace.length) {
3535
return escapeString(operation.operationId)
3636
}
37-
return idFromPathMethod(pathName, method)
37+
return idFromPathMethod(pathName, method, {v2OperationIdCompatibilityMode})
3838
}
3939

4040

4141
// Create a generated operationId from pathName + method
42-
export function idFromPathMethod(pathName, method) {
42+
export function idFromPathMethod(pathName, method, {v2OperationIdCompatibilityMode} = {}) {
43+
if (v2OperationIdCompatibilityMode) {
44+
let res = `${method.toLowerCase()}_${pathName}`
45+
.replace(/[\s!@#$%^&*()_+=[{\]};:<>|./?,\\'""-]/g, '_')
46+
47+
res = res || `${pathName.substring(1)}_${method}`
48+
49+
return res
50+
.replace(/((_){2,})/g, '_')
51+
.replace(/^(_)*/g, '')
52+
.replace(/([_])*$/g, '')
53+
}
4354
return `${toLower(method)}${escapeString(pathName)}`
4455
}
4556

src/interfaces.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ export function makeExecute(swaggerJs = {}) {
3535
export function makeApisTagOperationsOperationExecute(swaggerJs = {}) {
3636
// { apis: tag: operations: execute }
3737
const cb = self.makeExecute(swaggerJs)
38-
const tagOperations = self.mapTagOperations({spec: swaggerJs.spec, cb})
38+
const tagOperations = self.mapTagOperations({
39+
v2OperationIdCompatibilityMode: swaggerJs.v2OperationIdCompatibilityMode,
40+
spec: swaggerJs.spec,
41+
cb,
42+
})
3943

4044
const apis = {}
4145
for (const tag in tagOperations) {
@@ -54,7 +58,11 @@ export function makeApisTagOperationsOperationExecute(swaggerJs = {}) {
5458
export function makeApisTagOperation(swaggerJs = {}) {
5559
const cb = self.makeExecute(swaggerJs)
5660
return {
57-
apis: self.mapTagOperations({spec: swaggerJs.spec, cb})
61+
apis: self.mapTagOperations({
62+
v2OperationIdCompatibilityMode: swaggerJs.v2OperationIdCompatibilityMode,
63+
spec: swaggerJs.spec,
64+
cb
65+
})
5866
}
5967
}
6068

@@ -67,7 +75,7 @@ export function makeApisTagOperation(swaggerJs = {}) {
6775
* `defaultTag` will house all non-tagged operations
6876
*
6977
*/
70-
export function mapTagOperations({spec, cb = nullFn, defaultTag = 'default'}) {
78+
export function mapTagOperations({spec, cb = nullFn, defaultTag = 'default', v2OperationIdCompatibilityMode}) {
7179
const operationIdCounter = {}
7280
const tagOperations = {} // Will house all tags + operations
7381
eachOperation(spec, ({pathName, method, operation}) => {
@@ -78,7 +86,7 @@ export function mapTagOperations({spec, cb = nullFn, defaultTag = 'default'}) {
7886
return
7987
}
8088
const tagObj = tagOperations[tag] = tagOperations[tag] || {}
81-
const id = opId(operation, pathName, method)
89+
const id = opId(operation, pathName, method, {v2OperationIdCompatibilityMode})
8290
const cbResult = cb({spec, pathName, method, operation, operationId: id})
8391

8492
if (operationIdCounter[id]) {

test/helpers.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ describe('helpers', function () {
1212
// Then
1313
expect(id).toEqual('get_one')
1414
})
15+
it('should return get_one as an operationId', function () {
16+
// When
17+
const id = idFromPathMethod('/one', 'get')
18+
19+
// Then
20+
expect(id).toEqual('get_one')
21+
})
22+
it('should handle strange paths/methods correctly when in v2 mode', function () {
23+
const fn = (path, method) => {
24+
return idFromPathMethod(path, method, {
25+
v2OperationIdCompatibilityMode: true
26+
})
27+
}
28+
// https://github.com/swagger-api/swagger-js/issues/1269#issue-309070070
29+
expect(fn('/foo/{bar}/baz', 'get')).toEqual('get_foo_bar_baz')
30+
31+
expect(fn('/one/{foo}/{bar}', 'get')).toEqual('get_one_foo_bar')
32+
expect(fn('/one/{bar}/-----{baz}', 'get')).toEqual('get_one_bar_baz')
33+
})
1534
})
1635

1736
describe('getOperationRaw', function () {

test/index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,35 @@ describe('constructor', () => {
124124
})
125125
})
126126

127+
it('should honor `v2OperationIdCompatibilityMode` when building `apis`', function () {
128+
// Given
129+
const spec = {
130+
swagger: '2.0',
131+
paths: {
132+
'/foo/{bar}/baz': {
133+
get: {
134+
description: '',
135+
tags: ['myTag']
136+
}
137+
}
138+
}
139+
}
140+
141+
// When
142+
return Swagger({
143+
spec,
144+
v2OperationIdCompatibilityMode: true
145+
}).then((swag) => {
146+
const {apis} = swag
147+
148+
// Then
149+
expect(apis).toBeAn('object')
150+
expect(apis.myTag).toBeAn('object')
151+
console.log(apis)
152+
expect(apis.myTag.get_foo_bar_baz).toBeA(Function)
153+
})
154+
})
155+
127156
it('should handle circular $refs when a baseDoc is provided', () => {
128157
// Given
129158
const spec = {

0 commit comments

Comments
 (0)