Skip to content

Commit 9a4a214

Browse files
Merge pull request awslabs#285 from alex-chew/fetch-all-usage-plan-pages
Fetch all usage plan pages
2 parents 64e820c + 02c0274 commit 9a4a214

File tree

7 files changed

+232
-39
lines changed

7 files changed

+232
-39
lines changed

lambdas/backend/_common/customers-controller.js

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
'use strict'
55
const AWS = require('aws-sdk')
6+
const { getAllUsagePlans } = require('../shared/get-all-usage-plans')
67

78
const dynamoDb = new AWS.DynamoDB.DocumentClient()
89
const apigateway = new AWS.APIGateway()
@@ -195,10 +196,9 @@ function getUsagePlansForCustomer(cognitoIdentityId, error, callback) {
195196
keyId,
196197
limit: 1000
197198
}
198-
apigateway.getUsagePlans(params, (err, usagePlansData) => {
199-
if (err) error(err)
200-
else callback(usagePlansData)
201-
})
199+
getAllUsagePlans(apigateway, params)
200+
.then(usagePlansData => callback({ items: usagePlansData }))
201+
.catch(err => error(err))
202202
}
203203
})
204204
}
@@ -209,26 +209,22 @@ function getUsagePlanForProductCode(productCode, error, callback) {
209209
// do a linear scan of usage plans for name matching productCode
210210
var params = {
211211
limit: 1000
212-
};
213-
apigateway.getUsagePlans(params, function(err, data) {
214-
if (err) {
215-
error(err)
216-
} else {
217-
console.log(`Got usage plans ${JSON.stringify(data.items)}`)
212+
}
213+
getAllUsagePlans(apigateway, params).then(usagePlans => {
214+
console.log(`Got usage plans ${JSON.stringify(usagePlans)}`)
218215

219-
// note: ensure that only one usage plan maps to a given marketplace product code
220-
const usageplan = data.items.find(function (item) {
221-
return item.productCode !== undefined && item.productCode === productCode
222-
})
223-
if (usageplan !== undefined) {
224-
console.log(`Found usage plan matching ${productCode}`)
225-
callback(usageplan)
226-
} else {
227-
console.log(`Couldn't find usageplan matching product code ${productCode}`)
228-
error(`Couldn't find usageplan matching product code ${productCode}`)
229-
}
216+
// note: ensure that only one usage plan maps to a given marketplace product code
217+
const usageplan = usagePlans.find(function (item) {
218+
return item.productCode !== undefined && item.productCode === productCode
219+
})
220+
if (usageplan !== undefined) {
221+
console.log(`Found usage plan matching ${productCode}`)
222+
callback(usageplan)
223+
} else {
224+
console.log(`Couldn't find usageplan matching product code ${productCode}`)
225+
error(`Couldn't find usageplan matching product code ${productCode}`)
230226
}
231-
});
227+
}).catch(err => error(err))
232228
}
233229

234230
function updateCustomerMarketplaceId(cognitoIdentityId, marketplaceCustomerId, error, success) {
@@ -323,16 +319,6 @@ function updateCustomerApiKeyId(cognitoIdentityId, apiKeyId, error, success) {
323319
})
324320
}
325321

326-
// function getUsagePlans(error, callback) {
327-
// const params = {
328-
// limit: 1000
329-
// }
330-
// apigateway.getUsagePlans(params, (err, data) => {
331-
// if (err) error(err)
332-
// else callback(data)
333-
// })
334-
// }
335-
336322
module.exports = {
337323
ensureCustomerItem,
338324
subscribe,

lambdas/backend/express-route-handlers.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const feedbackController = require('./_common/feedback-controller.js')
33
const AWS = require('aws-sdk')
44
const catalog = require('./catalog/index')
55
const hash = require('object-hash')
6+
const { getAllUsagePlans } = require('./shared/get-all-usage-plans')
67

78
const Datauri = require('datauri')
89

@@ -421,7 +422,7 @@ async function getAdminCatalogVisibility(req, res) {
421422
})
422423
})
423424

424-
let usagePlans = await exports.apigateway.getUsagePlans().promise()
425+
let usagePlans = await getAllUsagePlans(exports.apigateway)
425426

426427
// In the case of apiGateway APIs, the client doesn't know if there are usage plan associated or not
427428
// so we need to provide that information. This can't be merged with the above loop:
@@ -431,7 +432,7 @@ async function getAdminCatalogVisibility(req, res) {
431432
visibility.apiGateway.map((apiEntry) => {
432433
apiEntry.subscribable = false
433434

434-
usagePlans.items.forEach((usagePlan) => {
435+
usagePlans.forEach((usagePlan) => {
435436
usagePlan.apiStages.forEach((apiStage) => {
436437
if(apiEntry.id === apiStage.apiId && apiEntry.stage === apiStage.stage) {
437438
apiEntry.subscribable = true
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Fetches all usage plans, combining all pages into a single array.
3+
*
4+
* @param apiGateway
5+
* an instance of `AWS.APIGateway` to use for API calls
6+
*
7+
* @param paramOverrides
8+
* a parameter object passed in calls to `APIGateway.getUsagePlans`
9+
*
10+
* @returns
11+
* a Promise resolving with an array of items returned from
12+
* `APIGateway.getUsagePlans` calls
13+
*/
14+
async function getAllUsagePlans(apiGateway, paramOverrides = {}) {
15+
// The maximum allowed value of `limit` is 500 according to
16+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/APIGateway.html#getUsagePlans-property
17+
const defaultParams = {limit: 500, ...paramOverrides}
18+
19+
console.log('Fetching first page of usage plans')
20+
let response = await apiGateway.getUsagePlans(defaultParams).promise()
21+
const usagePlans = response.items
22+
23+
while (response.position) {
24+
console.log(`Fetching next page of usage plans, at position=[${response.position}]`)
25+
const nextParams = {...defaultParams, position: response.position}
26+
response = await apiGateway.getUsagePlans(nextParams).promise()
27+
usagePlans.push(...response.items)
28+
}
29+
30+
return usagePlans
31+
}
32+
33+
module.exports = { getAllUsagePlans }

lambdas/catalog-updater/index.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ let AWS = require('aws-sdk'),
1212
bucketName = '',
1313
hash = require('object-hash')
1414

15+
const { getAllUsagePlans } = require('./shared/get-all-usage-plans')
16+
1517
/**
1618
* Takes in an s3 listObjectsV2 object and returns whether it's a "swagger file" (one ending in .JSON, .YAML, or .YML),
1719
* and whether it's in the catalog folder (S3 Key starts with "catalog/").
@@ -185,10 +187,9 @@ function buildCatalog(swaggerFiles, sdkGeneration) {
185187
generic: []
186188
}
187189

188-
return exports.gateway.getUsagePlans({}).promise()
189-
.then((result) => {
190-
console.log(`usagePlans: ${JSON.stringify(result.items, null, 4)}`)
191-
let usagePlans = result.items
190+
return getAllUsagePlans(exports.gateway)
191+
.then(usagePlans => {
192+
console.log(`usagePlans: ${JSON.stringify(usagePlans, null, 4)}`)
192193
for (let i = 0; i < usagePlans.length; i++) {
193194
catalog.apiGateway[i] = usagePlanToCatalogObject(usagePlans[i], swaggerFiles, sdkGeneration)
194195
}
@@ -252,4 +253,4 @@ exports = module.exports = {
252253
gateway: new AWS.APIGateway(),
253254
handler,
254255
hash
255-
}
256+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Fetches all usage plans, combining all pages into a single array.
3+
*
4+
* @param apiGateway
5+
* an instance of `AWS.APIGateway` to use for API calls
6+
*
7+
* @param paramOverrides
8+
* a parameter object passed in calls to `APIGateway.getUsagePlans`
9+
*
10+
* @returns
11+
* a Promise resolving with an array of items returned from
12+
* `APIGateway.getUsagePlans` calls
13+
*/
14+
async function getAllUsagePlans(apiGateway, paramOverrides = {}) {
15+
// The maximum allowed value of `limit` is 500 according to
16+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/APIGateway.html#getUsagePlans-property
17+
const defaultParams = {limit: 500, ...paramOverrides}
18+
19+
console.log('Fetching first page of usage plans')
20+
let response = await apiGateway.getUsagePlans(defaultParams).promise()
21+
const usagePlans = response.items
22+
23+
while (response.position) {
24+
console.log(`Fetching next page of usage plans, at position=[${response.position}]`)
25+
const nextParams = {...defaultParams, position: response.position}
26+
response = await apiGateway.getUsagePlans(nextParams).promise()
27+
usagePlans.push(...response.items)
28+
}
29+
30+
return usagePlans
31+
}
32+
33+
module.exports = { getAllUsagePlans }
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
const { getAllUsagePlans } = require('../get-all-usage-plans')
2+
3+
const promiser = require('../../setup-jest').promiser
4+
5+
const mockUsagePlanItem = () => ({
6+
id: '1a2b3c',
7+
name: '1a2b3c',
8+
apiStages: [
9+
{
10+
apiId: 'anmlcrckrs',
11+
stage: 'prod',
12+
},
13+
{
14+
apiId: 'jlpnochips',
15+
stage: 'beta',
16+
},
17+
],
18+
throttle: {
19+
burstLimit: 10,
20+
rateLimit: 10,
21+
},
22+
quota: {
23+
limit: 10000,
24+
offset: 0,
25+
period: 'DAY',
26+
}
27+
})
28+
29+
describe('getAllUsagePlans', () => {
30+
test('returns all usage plans, when none exist', async () => {
31+
const mockApiGateway = {
32+
getUsagePlans: jest.fn().mockReturnValueOnce(promiser({
33+
items: []
34+
}))
35+
}
36+
37+
const result = await getAllUsagePlans(mockApiGateway)
38+
const mocked = mockApiGateway.getUsagePlans.mock
39+
expect(mocked.calls.length).toBe(1)
40+
expect(mocked.calls[0][0]).not.toHaveProperty('position')
41+
expect(result).toHaveLength(0)
42+
})
43+
44+
test('returns all usage plans, when only one page of usage plans exists', async () => {
45+
const mockApiGateway = {
46+
getUsagePlans: jest.fn().mockReturnValueOnce(promiser({
47+
items: [
48+
mockUsagePlanItem(),
49+
mockUsagePlanItem(),
50+
mockUsagePlanItem(),
51+
mockUsagePlanItem(),
52+
]
53+
}))
54+
}
55+
56+
const result = await getAllUsagePlans(mockApiGateway)
57+
const mocked = mockApiGateway.getUsagePlans.mock
58+
expect(mocked.calls.length).toBe(1)
59+
expect(mocked.calls[0][0]).not.toHaveProperty('position')
60+
expect(result).toHaveLength(4)
61+
})
62+
63+
test('returns all usage plans, when multiple pages of usage plans exist', async () => {
64+
const mockApiGateway = {
65+
getUsagePlans: jest.fn().mockReturnValueOnce(promiser({
66+
items: [
67+
mockUsagePlanItem(),
68+
mockUsagePlanItem(),
69+
mockUsagePlanItem(),
70+
mockUsagePlanItem(),
71+
],
72+
position: 'qwertyuiopasdf%3D%3D',
73+
})).mockReturnValueOnce(promiser({
74+
items: [
75+
mockUsagePlanItem(),
76+
mockUsagePlanItem(),
77+
mockUsagePlanItem(),
78+
mockUsagePlanItem(),
79+
],
80+
position: 'zxcvbnm1234567%3D%3D',
81+
})).mockReturnValueOnce(promiser({
82+
items: [
83+
mockUsagePlanItem(),
84+
mockUsagePlanItem(),
85+
],
86+
}))
87+
}
88+
89+
const result = await getAllUsagePlans(mockApiGateway)
90+
const mocked = mockApiGateway.getUsagePlans.mock
91+
expect(mocked.calls.length).toBe(3)
92+
expect(mocked.calls[0][0]).not.toHaveProperty('position')
93+
expect(mocked.calls[1][0]).toHaveProperty('position', 'qwertyuiopasdf%3D%3D')
94+
expect(mocked.calls[2][0]).toHaveProperty('position', 'zxcvbnm1234567%3D%3D')
95+
expect(result).toHaveLength(10)
96+
})
97+
98+
test('passes through an API Gateway request error', async () => {
99+
const expectedError = {}
100+
const mockApiGateway = {
101+
getUsagePlans: jest.fn().mockReturnValueOnce(promiser(null, expectedError))
102+
}
103+
104+
await expect(getAllUsagePlans(mockApiGateway)).rejects.toStrictEqual(expectedError)
105+
})
106+
})

lambdas/shared/get-all-usage-plans.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Fetches all usage plans, combining all pages into a single array.
3+
*
4+
* @param apiGateway
5+
* an instance of `AWS.APIGateway` to use for API calls
6+
*
7+
* @param paramOverrides
8+
* a parameter object passed in calls to `APIGateway.getUsagePlans`
9+
*
10+
* @returns
11+
* a Promise resolving with an array of items returned from
12+
* `APIGateway.getUsagePlans` calls
13+
*/
14+
async function getAllUsagePlans(apiGateway, paramOverrides = {}) {
15+
// The maximum allowed value of `limit` is 500 according to
16+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/APIGateway.html#getUsagePlans-property
17+
const defaultParams = {limit: 500, ...paramOverrides}
18+
19+
console.log('Fetching first page of usage plans')
20+
let response = await apiGateway.getUsagePlans(defaultParams).promise()
21+
const usagePlans = response.items
22+
23+
while (response.position) {
24+
console.log(`Fetching next page of usage plans, at position=[${response.position}]`)
25+
const nextParams = {...defaultParams, position: response.position}
26+
response = await apiGateway.getUsagePlans(nextParams).promise()
27+
usagePlans.push(...response.items)
28+
}
29+
30+
return usagePlans
31+
}
32+
33+
module.exports = { getAllUsagePlans }

0 commit comments

Comments
 (0)