Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bdb11d4
Add oas generator
vcua-mobify May 14, 2025
1176306
Add oas templates
vcua-mobify May 14, 2025
1fd3f58
sort package.json
vcua-mobify May 14, 2025
1568fc6
Add oas specific npm scripts
vcua-mobify May 20, 2025
dca3c90
Set OAS templates
vcua-mobify May 20, 2025
adf102f
Copy static dir and update imports
vcua-mobify May 20, 2025
1e296d6
Update test imports
vcua-mobify May 20, 2025
34ece5e
Add version file to static
vcua-mobify May 21, 2025
45aa643
New template for index file
vcua-mobify May 21, 2025
35894e1
Fix issues with the api template and types
vcua-mobify May 22, 2025
c0b4578
REVERT THIS: remove commit hook so I can actually push a feature branch
vcua-mobify May 22, 2025
3a9082b
Remove hasImports from oneOf
vcua-mobify May 22, 2025
6aeb85e
Re-add hooks
vcua-mobify May 22, 2025
1a3f6f8
Fix tests
vcua-mobify May 22, 2025
d6ac769
Revisit this: adjust thresholds
vcua-mobify May 22, 2025
1fa97bd
Fix tests and restore test thresholds
vcua-mobify May 26, 2025
ed22fe5
Revert app change
vcua-mobify May 28, 2025
709f8b4
Separate copyStatic
vcua-mobify May 28, 2025
5f9e3e5
Dynamic version
vcua-mobify May 28, 2025
7e8685c
Replace raml commands with oas in package.json
vcua-mobify May 28, 2025
75ec28f
Remove cross-env
vcua-mobify May 29, 2025
00b774c
Delete raml-specific templates and scripts
vcua-mobify May 29, 2025
ab5160e
Default isOAS to true when downloading from exchange
vcua-mobify May 29, 2025
a7db4ee
Use ExchangeConfig type from raml-toolkit
vcua-mobify Jun 2, 2025
bbbb255
Refer to contents inside lib instead of static dir
vcua-mobify Jun 2, 2025
2813adc
Use camel case instead of kebab case and fix tests
vcua-mobify Jun 2, 2025
726d8ff
Update test imports
vcua-mobify Jun 2, 2025
8aa5b6f
More test import updates
vcua-mobify Jun 2, 2025
de5f943
whitespace
vcua-mobify Jun 2, 2025
98adab1
Use new raml-toolkit release
vcua-mobify Jun 2, 2025
d4d34cd
Delete openapitools.json
vcua-mobify Jun 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions openapitools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this file here? Will it not use from raml-toolkit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I just tried generating the sdk again using the raml-toolkit command after deleting it and it didn't return.
This was probably an artifact from when I ran openapi-generator directly in the sdk. I'll delete it.

"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.13.0"
}
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
"build:lib": "rollup -c",
"check:size": "npm-pack-all --output commerce-sdk-isomorphic-with-deps.tgz && bundlesize",
"check:types": "tsc --noEmit",
"ci": "rm -rf node_modules && yarn install",
"clean": "rm -rf build lib src/lib commerce-sdk-isomorphic-with-deps.tgz",
"depcheck": "depcheck",
"diffApis": "raml-toolkit diff --dir temp/oldApis apis -f console -o temp/diffApis.txt",
"diffApis": "raml-toolkit diff --dir ./temp/oldApis ./apis -f console -o temp/diffApis.txt -s oas",
"doc": "yarn run doc:generate",
"doc:generate": "typedoc --mode modules src/lib/** --external-modulemap \".*/src/lib/([\\w]+)\" --exclude \"src/lib/index.ts\"",
"eject": "react-scripts eject",
Expand All @@ -35,7 +36,7 @@
"lint": "eslint --ext js,jsx,ts,tsx .",
"lint:style": "stylelint ./src/",
"prepare": "snyk protect",
"renderTemplates": "ts-node --compiler-options '{\"module\": \"commonjs\", \"target\": \"ES6\" }' ./scripts/generate.ts",
"renderTemplates": "PACKAGE_VERSION=$(node -p \"require('./package.json').version\") ts-node --compiler-options '{\"module\": \"commonjs\", \"target\": \"ES6\" }' ./scripts/generate-oas.ts",
"start": "HTTPS=true react-scripts start",
"pretest": "yarn run lint && yarn run lint:style && depcheck && yarn run check:size",
"test": "yarn run check:types && yarn run test:unit && CI=true yarn run test:react",
Expand Down Expand Up @@ -108,14 +109,13 @@
"@babel/preset-env": "7.18.6",
"@babel/preset-react": "7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@commerce-apps/raml-toolkit": "0.5.12",
"@commerce-apps/raml-toolkit": "0.6.0",
"@rollup/plugin-babel": "5.3.1",
"@rollup/plugin-commonjs": "13.0.2",
"@rollup/plugin-node-resolve": "8.4.0",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "10.4.9",
"@types/fs-extra": "^9.0.13",
"@types/handlebars-helpers": "^0.5.3",
"@types/node-fetch": "^2.6.2",
"@types/react-dom": "^16.9.16",
"@types/seedrandom": "^3.0.8",
Expand All @@ -136,7 +136,7 @@
"eslint-plugin-react": "7.30.1",
"eslint-plugin-react-hooks": "4.6.0",
"fs-extra": "^9.1.0",
"handlebars-helpers": "^0.10.0",
"handlebars": "4.7.7",
"husky": "4.3.8",
"jest-environment-jsdom-sixteen": "1.0.3",
"lint-staged": "10.5.4",
Expand Down Expand Up @@ -177,11 +177,11 @@
"bundlesize": [
{
"path": "lib/**/*.js",
"maxSize": "50 kB"
"maxSize": "51 kB"
},
{
"path": "commerce-sdk-isomorphic-with-deps.tgz",
"maxSize": "565 kB"
"maxSize": "1096 kB"
}
],
"proxy": "https://SHORTCODE.api.commercecloud.salesforce.com"
Expand Down
25 changes: 12 additions & 13 deletions scripts/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ export const PRIMITIVE_DATA_TYPE_MAP = {
'http://www.w3.org/2001/XMLSchema#boolean': 'boolean',
};
export const API_LIST: string[] = [
'shopper-baskets',
'shopper-context',
'shopper-customers',
'shopper-discovery-search',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is shopper-discovery-search no longer needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, according to Kay, both shopper-discovery-search and slas-shopper-login-uap artifact are out of scope for the OAS work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we are not using these in PWA?

'shopper-experience',
'shopper-gift-certificates',
'slas-shopper-login-uap',
'shopper-orders',
'shopper-products',
'shopper-promotions',
'shopper-search',
'shopper-seo',
'shopper-stores',
'shopper-baskets-oas',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a comment in the other PR for just replacing the APIs, but do we want to add shopper baskets v1 as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at anypoint exchange, there is only one version of shopper-baskets-oas.

We should bring up getting an OAS version of baskets v1 with the api team.

'shopper-context-oas',
'shopper-customers-oas',
'shopper-experience-oas',
'shopper-gift-certificates-oas',
'shopper-login-oas',
'shopper-orders-oas',
'shopper-products-oas',
'shopper-promotions-oas',
'shopper-search-oas',
'shopper-seo-oas',
'shopper-stores-oas',
];
202 changes: 202 additions & 0 deletions scripts/generate-oas.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* Copyright (c) 2025, Salesforce, Inc.
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import fs from 'fs-extra';
import path from 'path';
import {generateFromOas} from '@commerce-apps/raml-toolkit';
import Handlebars from 'handlebars';
import {
resolveApiName,
getAPIDetailsFromExchange,
generateSDKs,
generateIndex,
main,
generateVersionFile,
} from './generate-oas';

// Mock dependencies
jest.mock('fs-extra');

jest.mock('@commerce-apps/raml-toolkit', () => ({
generateFromOas: {
generateFromOas: jest.fn(),
},
}));

describe('generate-oas', () => {
const mockApiDirectory = '/mock/api/directory';
let handlebarsSpy: jest.SpyInstance;

beforeEach(() => {
// Reset all mocks before each test
jest.clearAllMocks();

// Set up Handlebars spy
handlebarsSpy = jest.spyOn(Handlebars, 'compile');

// Mock fs-extra methods
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readJSONSync as jest.Mock).mockReturnValue({
main: 'api.yaml',
assetId: 'shopper-orders-oas',
name: 'Shopper orders OAS',
});
(fs.statSync as jest.Mock).mockReturnValue({isFile: () => true});
(fs.readdir as jest.Mock).mockImplementation((dir, callback) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
callback(null, ['shopper-orders']);
});
(fs.readFileSync as jest.Mock).mockReturnValue('template content');
// eslint-disable-next-line @typescript-eslint/no-empty-function
(fs.writeFileSync as jest.Mock).mockImplementation(() => {});
// eslint-disable-next-line @typescript-eslint/no-empty-function
(fs.copySync as jest.Mock).mockImplementation(() => {});
(fs.lstatSync as jest.Mock).mockReturnValue({isDirectory: () => true});
});

describe('Main execution', () => {
it('should process all API directories and generate SDKs', () => {
process.env.COMMERCE_SDK_INPUT_DIR = mockApiDirectory;
process.env.PACKAGE_VERSION = '1.0.0';

main();

expect(fs.copySync).toHaveBeenCalledWith(
path.join(__dirname, '../src/static'),
path.join(__dirname, '../src/lib'),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
{filter: expect.any(Function)}
);
expect(fs.readdir).toHaveBeenCalledWith(
mockApiDirectory,
expect.any(Function)
);
});
});

describe('resolveApiName', () => {
it('should handle special case for Shopper orders OAS', () => {
const result = resolveApiName('Shopper orders OAS');
expect(result).toBe('ShopperOrders');
});

it('should handle special case for Shopper Seo OAS', () => {
const result = resolveApiName('Shopper Seo OAS');
expect(result).toBe('ShopperSEO');
});

it('should handle regular API names', () => {
const result = resolveApiName('Shopper Baskets OAS');
expect(result).toBe('ShopperBaskets');
});
});

describe('getAPIDetailsFromExchange', () => {
it('should return correct API details from exchange.json', () => {
const result = getAPIDetailsFromExchange(
path.join(mockApiDirectory, 'shopperOrders')
);
expect(result).toEqual({
filepath: path.join(mockApiDirectory, 'shopperOrders', 'api.yaml'),
filename: 'api.yaml',
directoryName: 'shopperOrders',
name: 'Shopper orders OAS',
apiName: 'ShopperOrders',
});
});

it('should throw error when exchange.json does not exist', () => {
(fs.existsSync as jest.Mock).mockReturnValueOnce(false);
expect(() => getAPIDetailsFromExchange('nonexistent')).toThrow(
'Exchange file does not exist'
);
});
});

describe('generateSDKs', () => {
it('should generate SDK for shopper API', () => {
const apiSpecDetail = {
filepath: '/path/to/shopper/api.yaml',
filename: 'api.yaml',
name: 'Test API',
directoryName: 'test-api',
apiName: 'TestAPI',
};

generateSDKs(apiSpecDetail);

expect(generateFromOas.generateFromOas).toHaveBeenCalledWith({
inputSpec: '/path/to/shopper/api.yaml',
outputDir: path.join(__dirname, '../src/lib/test-api'),
templateDir: path.join(__dirname, '../templatesOas'),
skipValidateSpec: true,
});
});

it('should not generate SDK for non-api files', () => {
const apiSpecDetail = {
filepath: '/path/to/shopper/not-api.yaml',
filename: '/not-api.yaml',
name: 'Not API',
directoryName: 'none',
apiName: 'NotAPI',
};

(fs.statSync as jest.Mock).mockReturnValueOnce({isFile: () => false});
generateSDKs(apiSpecDetail);

expect(generateFromOas.generateFromOas).not.toHaveBeenCalled();
});

it('should not generate SDK for non-shopper API', () => {
const apiSpecDetail = {
filepath: '/path/to/non/api.yaml',
filename: 'api.yaml',
name: 'Non Shopper API',
directoryName: 'none',
apiName: 'NonShopper',
};

(fs.statSync as jest.Mock).mockReturnValueOnce({isFile: () => true});
generateSDKs(apiSpecDetail);

expect(generateFromOas.generateFromOas).not.toHaveBeenCalled();
});
});

describe('generateIndex', () => {
it('should generate index file with correct context', () => {
const context = {
children: [
{
name: 'Test API',
apiName: 'TestAPI',
},
],
};

generateIndex(context);

expect(fs.writeFileSync).toHaveBeenCalledWith(
path.join(__dirname, '../src/lib/index.ts'),
expect.any(String)
);
});
});

describe('generateVersionFile', () => {
it('should generate version file', () => {
process.env.PACKAGE_VERSION = '1.0.0';

generateVersionFile();

expect(fs.writeFileSync).toHaveBeenCalledWith(
path.join(__dirname, '../src/lib/version.ts'),
expect.any(String)
);
});
});
});
Loading
Loading