diff --git a/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.stories.ts b/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.stories.ts index 4cf7568ed..edea89fb7 100644 --- a/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.stories.ts +++ b/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.stories.ts @@ -10,8 +10,9 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const LicenseNameAndURL: Story = { - name: 'License Name with URL', +// Story when only the license URL is provided +export const LicenseWithOnlyURL: Story = { + name: 'License with only URL', args: { id: 'id', license: { @@ -21,8 +22,9 @@ export const LicenseNameAndURL: Story = { }, }; -export const LicenseNameAndIdentifier: Story = { - name: 'License Name and Identifier', +// Story when only the license identifier is provided +export const LicenseWithOnlyIdentifier: Story = { + name: 'License with only Identifier', args: { id: 'id', license: { @@ -32,8 +34,9 @@ export const LicenseNameAndIdentifier: Story = { }, }; -export const LicenseIdentifierAndNameAndUrl: Story = { - name: 'License Identifier, Name and URL', +// Story when both the license URL and identifier are provided (URL should take precedence) +export const LicenseWithURLAndIdentifier: Story = { + name: 'License with URL and Identifier (URL takes precedence)', args: { id: 'id', license: { diff --git a/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.tsx b/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.tsx index 882736dcf..22045de9e 100644 --- a/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.tsx +++ b/packages/elements-core/src/components/Docs/HttpService/AdditionalInfo.tsx @@ -24,8 +24,17 @@ export const AdditionalInfo: React.FC = ({ id, termsOfServi : ''; //use spdx to look up url for license identifier if available - const licenseUrl = - license?.url || license?.identifier ? `https://spdx.org/licenses/${license?.identifier}.html` : undefined; + // The licenseUrl is determined based on the mutual exclusivity of the `url` and `identifier` fields. + // If a `license.url` is provided, it takes precedence over the `license.identifier`. + // This is because the OpenAPI specification defines `url` and `identifier` as mutually exclusive fields, + // meaning you should use either one or the other, but not both. If both are provided, the `url` should be used. + // See: https://spec.openapis.org/oas/latest.html#license-object + const licenseUrl = license?.url + ? license?.url + : license?.identifier + ? `https://spdx.org/licenses/${license?.identifier}.html` + : undefined; + const licenseLink = license?.name && licenseUrl ? `[${license.name}](${licenseUrl})` diff --git a/packages/elements-core/src/components/Docs/HttpService/HttpService.spec.tsx b/packages/elements-core/src/components/Docs/HttpService/HttpService.spec.tsx index 747532853..ae6c8cd78 100644 --- a/packages/elements-core/src/components/Docs/HttpService/HttpService.spec.tsx +++ b/packages/elements-core/src/components/Docs/HttpService/HttpService.spec.tsx @@ -267,6 +267,39 @@ describe('HttpService', () => { expect(title).toBeInTheDocument(); }); + it('should render additional information with SPDX license identifier', () => { + const contact = { + name: 'Developer', + email: 'developer@stoplight.io', + url: 'https://stoplight.io/contact-us/', + }; + + const license = { + name: 'MIT License', + identifier: 'MIT', + }; + + render( + , + ); + + const licenseLink = screen.getByText('MIT License'); + expect(licenseLink).toHaveAttribute('href', 'https://spdx.org/licenses/MIT.html'); + }); + + it('should prefer license URL over SPDX identifier if both are provided', () => { + const license = { + name: 'MIT License', + url: 'https://opensource.org/licenses/MIT', + identifier: 'MIT', + }; + + render(); + + const licenseLink = screen.getByText('MIT License'); + expect(licenseLink).toHaveAttribute('href', 'https://opensource.org/licenses/MIT'); + }); + it('should not render if contact, license, and terms of service do not exist', () => { render();