From 8698b73491a700e2cf8126226b8d99970f6ee329 Mon Sep 17 00:00:00 2001 From: Josh W Lewis Date: Thu, 30 Jan 2025 15:08:25 -0600 Subject: [PATCH 1/2] Add pagination support --- package.json | 1 + src/buildpack-registry-api.ts | 16 ++++----- src/buildpack-registry.ts | 65 ++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index ab34e5e..ce6c5b0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "devDependencies": { "@types/chai": "^4.3", "@types/mocha": "^5.2.5", + "@types/nock": "^9.3.0", "@types/node": "^20.14.8", "@types/node-fetch": "^2.1.2", "chai": "^4.3", diff --git a/src/buildpack-registry-api.ts b/src/buildpack-registry-api.ts index cbb10ce..b3c2ae7 100644 --- a/src/buildpack-registry-api.ts +++ b/src/buildpack-registry-api.ts @@ -1,6 +1,6 @@ -import fetch, {Headers, RequestInit, Response} from 'node-fetch' +import fetch, { Headers, RequestInit, Response } from 'node-fetch' -export {Response} from 'node-fetch' +export { Response } from 'node-fetch' export type IBody = { [property: string]: string @@ -85,7 +85,7 @@ export class BuildpackRegistryApi { } headers(options: HeaderOptions = {}): Headers { - let defaultHeaders: {[property: string]: string} = { + let defaultHeaders: { [property: string]: string } = { Accept: 'application/vnd.heroku+json; version=3.buildpack-registry', 'Content-Type': 'application/json' } @@ -99,7 +99,7 @@ export class BuildpackRegistryApi { if (process.env.HEROKU_HEADERS) { let herokuHeaders = JSON.parse(process.env.HEROKU_HEADERS) - return new Headers({...defaultHeaders, herokuHeaders}) + return new Headers({ ...defaultHeaders, herokuHeaders }) } else { return new Headers(defaultHeaders) } @@ -117,9 +117,9 @@ export class BuildpackRegistryApi { return fetch(`${BuildpackRegistryApi.url()}${path}`, options) } - async get(path: string): Promise { - return fetch(`${BuildpackRegistryApi.url()}${path}`, { - headers: this.headers() - }) + async get(path: string, headers?: Headers): Promise { + return await fetch(`${BuildpackRegistryApi.url()}${path}`, { + headers: headers ? headers : this.headers(), + }); } } diff --git a/src/buildpack-registry.ts b/src/buildpack-registry.ts index c8d0df4..5dded87 100644 --- a/src/buildpack-registry.ts +++ b/src/buildpack-registry.ts @@ -1,8 +1,10 @@ -import {Result} from 'true-myth' +import { Result } from 'true-myth' -import {BuildpackBody, BuildpackRegistryApi as Api, Category, HeaderOptions, ReadmeBody, Response, RevisionBody, RevisionStatus} from './buildpack-registry-api' +import { BuildpackBody, BuildpackRegistryApi as Api, Category, HeaderOptions, ReadmeBody, Response, RevisionBody, RevisionStatus } from './buildpack-registry-api' -export {BuildpackBody, Category, ReadmeBody, RevisionBody, RevisionStatus} +import { Headers } from 'node-fetch' + +export { BuildpackBody, Category, ReadmeBody, RevisionBody, RevisionStatus } const BUILDPACK_FORMATTING_MESSAGE = "To specify a buildpack, please format it like the following: namespace/name (e.g. heroku/ruby). Also names can only contain letters, numbers, '_', and '-'." @@ -58,15 +60,41 @@ export class BuildpackRegistry { } } + async list(path: string): Promise, ResponseError>> { + let page = await this.api.get(path); + let items = []; + if (page.status === 200 || page.status === 206) { + items = await page.json(); + } + while (page.status === 206) { + let nextRange = page.headers.get('Next-Range') + if (typeof nextRange !== "string") { + break; + } + page = await this.api.get(path, new Headers({ 'Accept-Range': nextRange})); + if (page.status === 200 || page.status === 206) { + items = [...items, ...await page.json()]; + } + } + if (page.status !== 200) { + return Result.err({ + status: page.status, + path, + description: await page.text() + }); + } + return Result.ok(items); + } + async publish(buildpack: string, ref: string, token: string, secondFactor?: string): Promise> { - let options: HeaderOptions = {token} + let options: HeaderOptions = { token } if (secondFactor !== undefined) { options.secondFactor = secondFactor } let path = `/buildpacks/${encodeURIComponent(buildpack)}/revisions` let response = await this.api.post( path, - {ref}, + { ref }, this.api.headers(options)) if (response.status === 200) { @@ -81,7 +109,7 @@ export class BuildpackRegistry { } async rollback(buildpack: string, token: string, secondFactor?: string): Promise> { - let options: HeaderOptions = {token} + let options: HeaderOptions = { token } if (secondFactor !== undefined) { options.secondFactor = secondFactor } @@ -123,16 +151,7 @@ export class BuildpackRegistry { } let path = `/buildpacks${queryString}` - let response = await this.api.get(path) - if (response.status === 200) { - return Result.ok(await response.json()) - } else { - return Result.err({ - status: response.status, - path, - description: await response.text(), - }) - } + return this.list(path); } async info(buildpack: string): Promise> { @@ -196,7 +215,7 @@ export class BuildpackRegistry { } async archive(buildpack: string, token: string, secondFactor?: string): Promise> { - let options: HeaderOptions = {token} + let options: HeaderOptions = { token } if (secondFactor !== undefined) { options.secondFactor = secondFactor } @@ -233,17 +252,7 @@ export class BuildpackRegistry { async listVersions(buildpack: string): Promise> { let path = `/buildpacks/${encodeURIComponent(buildpack)}/revisions` - let response = await this.api.get(path) - - if (response.status === 200) { - return Result.ok(await response.json()) - } else { - return Result.err({ - status: response.status, - path, - description: await response.text() - }) - } + return await this.list(path) } async delay(ms: number) { From 161d9b40c0961ec1d69c175e955cdc22f34b1463 Mon Sep 17 00:00:00 2001 From: Josh W Lewis Date: Fri, 31 Jan 2025 09:32:52 -0600 Subject: [PATCH 2/2] Update chai and nock --- yarn.lock | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index dc8c201..47c266b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11,6 +11,13 @@ version "5.2.5" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073" +"@types/nock@^9.3.0": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@types/nock/-/nock-9.3.1.tgz#7d761a43a10aebc7ec6bae29d89afc6cbffa5d30" + integrity sha512-eOVHXS5RnWOjTVhu3deCM/ruy9E6JCgeix2g7wpFiekQh3AaEAK1cz43tZDukKmtSmQnwvSySq7ubijCA32I7Q== + dependencies: + "@types/node" "*" + "@types/node-fetch@^2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.1.2.tgz#8c5da14d70321e4c4ecd5db668e3f93cf6c7399f" @@ -1650,11 +1657,7 @@ tsutils@^2.27.2: dependencies: tslib "^1.8.1" -type-detect@^4.0.0: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - -type-detect@^4.1.0: +type-detect@^4.0.0, type-detect@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==