From 7fc6f7c64ca7548b5c8f64a4a85189df42c0f71c Mon Sep 17 00:00:00 2001 From: Federico Sozio Date: Mon, 13 Jan 2025 13:32:10 +0100 Subject: [PATCH 1/3] Fix resolveUrl for splited openapi definition relative path (issues #27280) Signed-off-by: Federico Sozio --- .changeset/orange-brooms-lick.md | 5 + .../package.json | 1 + .../src/lib/bundle.test.ts | 154 +++++++++++++++++- .../src/lib/bundle.ts | 2 +- yarn.lock | 1 + 5 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 .changeset/orange-brooms-lick.md diff --git a/.changeset/orange-brooms-lick.md b/.changeset/orange-brooms-lick.md new file mode 100644 index 0000000000..8336ef602a --- /dev/null +++ b/.changeset/orange-brooms-lick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-openapi': patch +--- + +Fix resolveUrl for splited openapi definition relative path diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index a6266a8bf3..9ae3692e80 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -49,6 +49,7 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^", "openapi-types": "^12.0.0" } } diff --git a/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts b/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts index ec77610733..4cf3864aeb 100644 --- a/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts +++ b/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { bundleFileWithRefs } from './bundle'; +import { bundleFileWithRefs, BundlerRead, BundlerResolveUrl } from './bundle'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; const specification = ` openapi: "3.0.0" @@ -178,3 +180,153 @@ channels: expect(result).toEqual(expectedSchema.trimStart()); }); }); + +describe('bundleFileWithRefs - Testing getRelativePath scenarios', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({})); + + const resolveUrl: BundlerResolveUrl = jest.fn( + (url: string, base: string): string => { + return scmIntegrations.resolveUrl({ url, base }); + }, + ); + + const read: BundlerRead = jest.fn(async (url: string) => { + return Buffer.from(url); + }); + + const baseUrl = + 'https://dev.azure.com/organization/project/_git/idp-configurations?path=%2Frepo%2Ftest-openapi.yaml&version=GBmaster'; + + it('should handle the relative path when refUrl has the same base as baseUrl', async () => { + const fileWithRefs = ` +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + $ref: "common.yaml" +`; + + const relativePath = 'common.yaml'; + const expectedUrl = + 'https://dev.azure.com/organization/project/_git/idp-configurations?path=%2Frepo%2Fcommon.yaml&version=GBmaster'; + + await bundleFileWithRefs(fileWithRefs, baseUrl, read, resolveUrl); + + expect(resolveUrl).toHaveBeenCalledWith(relativePath, baseUrl); + expect(read).toHaveBeenCalledWith(expectedUrl); + }); + + it('should handle the relative path, with subdir, when refUrl has the same base as baseUrl', async () => { + const fileWithRefs = ` +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + $ref: "commons/common.yaml" +`; + + const relativePath = 'commons/common.yaml'; + const expectedUrl = + 'https://dev.azure.com/organization/project/_git/idp-configurations?path=%2Frepo%2Fcommons%2Fcommon.yaml&version=GBmaster'; + + await bundleFileWithRefs(fileWithRefs, baseUrl, read, resolveUrl); + + expect(resolveUrl).toHaveBeenCalledWith(relativePath, baseUrl); + expect(read).toHaveBeenCalledWith(expectedUrl); + }); + + it('should handle the entire refUrl when there is no common base', async () => { + const fileWithRef = ` +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + $ref: "https://example.com/commons/common.yaml" +`; + + const refUrl = 'https://example.com/commons/common.yaml'; + + await bundleFileWithRefs(fileWithRef, baseUrl, read, resolveUrl); + + expect(resolveUrl).toHaveBeenCalledWith(refUrl, baseUrl); + expect(read).toHaveBeenCalledWith(refUrl); + }); + + it('should handle the relative path when refUrl has a different subdir', async () => { + const fileWithRefs = ` +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + $ref: "../commons/common.yaml" +`; + + const exampleBaseUrl = + 'https://example.com/path/to/definition/test-openapi.yaml.yaml'; + const relativePath = '../commons/common.yaml'; + const expectedUrl = 'https://example.com/path/to/commons/common.yaml'; + + await bundleFileWithRefs(fileWithRefs, exampleBaseUrl, read, resolveUrl); + + expect(resolveUrl).toHaveBeenCalledWith(relativePath, exampleBaseUrl); + expect(read).toHaveBeenCalledWith(expectedUrl); + }); + + it('should handle the relative path when refUrl has a different subdir (azure)', async () => { + const fileWithRefs = ` +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + $ref: "../commons/common.yaml" +`; + + const relativePath = '../commons/common.yaml'; + const expectedUrl = + 'https://dev.azure.com/organization/project/_git/idp-configurations?path=%2Fcommons%2Fcommon.yaml&version=GBmaster'; + + await bundleFileWithRefs(fileWithRefs, baseUrl, read, resolveUrl); + + expect(resolveUrl).toHaveBeenCalledWith(relativePath, baseUrl); + expect(read).toHaveBeenCalledWith(expectedUrl); + }); +}); diff --git a/plugins/catalog-backend-module-openapi/src/lib/bundle.ts b/plugins/catalog-backend-module-openapi/src/lib/bundle.ts index b2b186dff1..32dac30681 100644 --- a/plugins/catalog-backend-module-openapi/src/lib/bundle.ts +++ b/plugins/catalog-backend-module-openapi/src/lib/bundle.ts @@ -69,6 +69,6 @@ export async function bundleFileWithRefs( }, }; const fileObject = parse(fileWithRefs); - const bundledObject = await $RefParser.bundle(baseUrl, fileObject, options); + const bundledObject = await $RefParser.bundle(fileObject, options); return stringify(bundledObject); } diff --git a/yarn.lock b/yarn.lock index f40ec341af..141d30e5e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6150,6 +6150,7 @@ __metadata: "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" From fd3df87b477aaccc9c504eb3035641870014f04f Mon Sep 17 00:00:00 2001 From: federicosozio Date: Mon, 13 Jan 2025 15:02:48 +0100 Subject: [PATCH 2/3] Update .changeset/orange-brooms-lick.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: federicosozio --- .changeset/orange-brooms-lick.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/orange-brooms-lick.md b/.changeset/orange-brooms-lick.md index 8336ef602a..17a8ef7476 100644 --- a/.changeset/orange-brooms-lick.md +++ b/.changeset/orange-brooms-lick.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend-module-openapi': patch --- -Fix resolveUrl for splited openapi definition relative path +Fix `resolveUrl` for split openapi definition relative path From 58c3d521c3f1185ff08707281b2673bf6f2fbf2e Mon Sep 17 00:00:00 2001 From: Federico Sozio Date: Mon, 13 Jan 2025 17:24:14 +0100 Subject: [PATCH 3/3] fix plugin openapi tests Signed-off-by: Federico Sozio --- plugins/catalog-backend-module-openapi/package.json | 1 - plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts | 4 ++-- yarn.lock | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 9ae3692e80..a6266a8bf3 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -49,7 +49,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/config": "workspace:^", "openapi-types": "^12.0.0" } } diff --git a/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts b/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts index 4cf3864aeb..a34b1c9a5b 100644 --- a/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts +++ b/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts @@ -14,8 +14,8 @@ * limitations under the License. */ import { bundleFileWithRefs, BundlerRead, BundlerResolveUrl } from './bundle'; +import { mockServices } from '@backstage/backend-test-utils'; import { ScmIntegrations } from '@backstage/integration'; -import { ConfigReader } from '@backstage/config'; const specification = ` openapi: "3.0.0" @@ -186,7 +186,7 @@ describe('bundleFileWithRefs - Testing getRelativePath scenarios', () => { jest.clearAllMocks(); }); - const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({})); + const scmIntegrations = ScmIntegrations.fromConfig(mockServices.rootConfig()); const resolveUrl: BundlerResolveUrl = jest.fn( (url: string, base: string): string => { diff --git a/yarn.lock b/yarn.lock index 141d30e5e9..f40ec341af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6150,7 +6150,6 @@ __metadata: "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^"