Skip to content

Commit

Permalink
[XM Cloud][Next.js] Editing Configuration Endpoint (#1724)
Browse files Browse the repository at this point in the history
* add config api route in xmcloud addon

* add generate metadata script to xmcloud addon

* add editing config middleware; add generate-metadata in nextjs scripts; modify component builder codegen to export components

* update response when unauthorized

* move editing-config-middleware from middleware to editing

* add unit testsfor editing config middleware; remove base middleware inheritance

* remove unused file

* update changelog

* fix lint error

* fix linting errors

* minor changelog update

* add appropriate descriptions to editing config middleware

* reafactoring - move the package metadata logic in the devtools package

* export metadata, remove console logs

* add unit tests

* revert misformated changelog

* update generate component builder unit tests

* extract Metadata interface in the base package and all necesarry updates around that

* fix linting errors
  • Loading branch information
yavorsk authored Feb 7, 2024
1 parent 6cae20d commit 4a03707
Show file tree
Hide file tree
Showing 15 changed files with 423 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Our versioning strategy is as follows:
* `[sitecore-jss]` `[templates/nextjs-xmcloud]` Load the content styles for the RichText component ([#1670](https://github.com/Sitecore/jss/pull/1670))([#1683](https://github.com/Sitecore/jss/pull/1683)) ([#1684](https://github.com/Sitecore/jss/pull/1684)) ([#1693](https://github.com/Sitecore/jss/pull/1693))
* `[templates/react]` `[sitecore-jss-react]` Replace package 'deep-equal' with 'fast-deep-equal'. No functionality change only performance improvement ([#1719](https://github.com/Sitecore/jss/pull/1719)) ([#1665](https://github.com/Sitecore/jss/pull/1665))
* `[templates/nextjs-xmcloud]` `[sitecore-jss]` `[sitecore-jss-nextjs]` `[sitecore-jss-react]` Add support for loading appropriate stylesheets whenever a theme is applied to BYOC and SXA components by introducing new function getComponentLibraryStylesheetLinks, which replaces getFEAASLibraryStylesheetLinks (which has been marked as deprecated) ([#1722](https://github.com/Sitecore/jss/pull/1722))
* `[templates/nextjs-xmcloud]` `[sitecore-jss-nextjs]` Add protected endpoint which provides configuration information (the sitecore packages used by the app and all registered components) to be used to determine feature compatibility on Pages side. ([#1724](https://github.com/Sitecore/jss/pull/1724))
* `[templates/nextjs]` `[templates/nextjs-styleguide]` Modify all GraphQLRequestClient import statements so that it gets imported from the /graphql submodule ([#1728](https://github.com/Sitecore/jss/pull/1728))

### 🐛 Bug Fixes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EditingConfigMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/editing';
import { components } from 'temp/componentBuilder';
import metadata from 'temp/metadata.json';

/**
* This Next.js API route is used by Sitecore editors (Pages) in XM Cloud
* to determine feature compatibility and configuration.
*/

const handler = new EditingConfigMiddleware({
components,
metadata,
}).getHandler();

export default handler;
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ import './generate-config';
COMPONENT BUILDER GENERATION
*/
import './generate-component-builder';

/*
META DATA GENERATION
*/
import './generate-metadata';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from 'fs';
import path from 'path';
import { Metadata, getMetadata } from '@sitecore-jss/sitecore-jss-dev-tools';

/*
METADATA GENERATION
Generates the /src/temp/metadata.json file which contains application
configuration metadata that is used for Sitecore XM Cloud integration.
*/
generateMetadata();

function generateMetadata(): void {
const metadata: Metadata = getMetadata();
writeMetadata(metadata);
}

/**
* Writes the metadata object to disk.
* @param {Metadata} metadata metadata to write.
*/
function writeMetadata(metadata: Metadata): void {
const filePath = path.resolve('src/temp/metadata.json');
console.log(`Writing metadata to ${filePath}`);
fs.writeFileSync(filePath, JSON.stringify(metadata, null, 2), { encoding: 'utf8' });
}
1 change: 1 addition & 0 deletions packages/sitecore-jss-dev-tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export {
DisconnectedServerOptions,
} from './disconnected-server/create-default-disconnected-server';
export { ScJssConfig, JssConfiguration, resolveScJssConfig } from './resolve-scjssconfig';
export { Metadata } from '@sitecore-jss/sitecore-jss/utils';

export * from './templating';
export * from './manifest';
Expand Down
1 change: 1 addition & 0 deletions packages/sitecore-jss-dev-tools/src/templating/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { ComponentFile, PackageDefinition, getComponentList } from './components
export { PluginDefinition, generatePlugins, ModuleType } from './plugins';
export { scaffoldFile } from './scaffold';
export { strip } from './strip';
export { getMetadata } from './metadata';
151 changes: 151 additions & 0 deletions packages/sitecore-jss-dev-tools/src/templating/metadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* eslint-disable quotes */
/* eslint-disable no-unused-expressions */
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';
import { getMetadata } from './metadata';
import sinon, { SinonStub } from 'sinon';
import { Metadata } from '@sitecore-jss/sitecore-jss/utils';

describe('metadata', () => {
afterEach(() => {
sinon.restore();
});

describe('getMetadata', () => {
let readdirSync: SinonStub;
let readFileSync: SinonStub;

afterEach(() => {
readdirSync?.restore();
readFileSync?.restore();
});

it('should return packages metadata from @sitecore scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore']);
readdirSync.withArgs(path.join('node_modules', '@sitecore')).returns(['byoc', 'components']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore', 'byoc', 'package.json'))
.returns('{"name": "@sitecore/byoc","version": "0.2.8"}');
readFileSync
.withArgs(path.join('node_modules', '@sitecore', 'components', 'package.json'))
.returns('{"name": "@sitecore/components","version": "1.1.6"}');

const expected: Metadata = {
packages: {
'@sitecore/byoc': '0.2.8',
'@sitecore/components': '1.1.6',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should return packages metadata from @sitecore-jss scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-jss']);
readdirSync
.withArgs(path.join('node_modules', '@sitecore-jss'))
.returns(['sitecore-jss-cli', 'sitecore-jss-nextjs']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-jss', 'sitecore-jss-cli', 'package.json'))
.returns('{"name": "@sitecore-jss/sitecore-jss-cli","version": "21.7.0-canary.55"}');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-jss', 'sitecore-jss-nextjs', 'package.json'))
.returns('{"name": "@sitecore-jss/sitecore-jss-nextjs","version": "21.7.0-canary.55"}');

const expected: Metadata = {
packages: {
'@sitecore-jss/sitecore-jss-cli': '21.7.0-canary.55',
'@sitecore-jss/sitecore-jss-nextjs': '21.7.0-canary.55',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should return packages metadata from @sitecore-cloudsdk scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-cloudsdk']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-cloudsdk')).returns(['core']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-cloudsdk', 'core', 'package.json'))
.returns('{"name": "@sitecore-cloudsdk/core","version": "0.1.5"}');

const expected: Metadata = {
packages: {
'@sitecore-cloudsdk/core': '0.1.5',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should return packages metadata from @sitecore-feaas scope', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-feaas']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-feaas')).returns(['clientside']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-feaas', 'clientside', 'package.json'))
.returns('{"name": "@sitecore-feaas/clientside","version": "0.5.12"}');

const expected: Metadata = {
packages: {
'@sitecore-feaas/clientside': '0.5.12',
},
};

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should not return packages metadata for not tracked scopes', () => {
const scope = '@nottracked-scope';
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns([scope]);

const expected: Metadata = { packages: {} };

const packagesMetadata = getMetadata();
expect(packagesMetadata).to.deep.equal(expected);
});

it('should throw if package.json not found', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-feaas']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-feaas')).returns(['clientside']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-feaas', 'clientside', 'package.json'))
.returns(null);

expect(() => getMetadata()).to.throw;
});

it('should throw if json not valid', () => {
readdirSync = sinon.stub(fs, 'readdirSync');
readdirSync.withArgs('node_modules').returns(['@sitecore-feaas']);
readdirSync.withArgs(path.join('node_modules', '@sitecore-feaas')).returns(['clientside']);

readFileSync = sinon.stub(fs, 'readFileSync');
readFileSync
.withArgs(path.join('node_modules', '@sitecore-feaas', 'clientside', 'package.json'))
.returns('{"name": "@sitecore-feaas/clientside","version": "0.5.12"');

expect(() => getMetadata()).to.throw;
});
});
});
31 changes: 31 additions & 0 deletions packages/sitecore-jss-dev-tools/src/templating/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fs from 'fs';
import path from 'path';
import { Metadata } from '@sitecore-jss/sitecore-jss/utils';

/**
* Get application metadata
*/
export function getMetadata(): Metadata {
const metadata: Metadata = { packages: {} };
const trackedScopes = ['@sitecore', '@sitecore-cloudsdk', '@sitecore-feaas', '@sitecore-jss'];
const dirs = fs.readdirSync('node_modules');

dirs.forEach((dir: any) => {
if (trackedScopes.includes(dir)) {
const packageNames = fs.readdirSync(path.join('node_modules', dir));
packageNames.forEach((pkg: any) => {
try {
const json = JSON.parse(
fs.readFileSync(path.join('node_modules', dir, pkg, 'package.json'), 'utf8')
);

metadata.packages[json.name] = json.version;
} catch (e) {
console.error(`Failed to read/parse package.json for ${pkg}`, e);
}
});
}
});

return metadata;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('generate-component-builder', () => {
"import * as Text from 'src/custom-components/Text';\n" +
"import * as barModule from 'bar';\n" +
'\n' +
'const components = new Map();\n' +
'export const components = new Map();\n' +
"components.set('Foo', Foo);\n" +
'\n' +
"components.set('TextComponent', Text);\n" +
Expand Down Expand Up @@ -129,7 +129,7 @@ describe('generate-component-builder', () => {
" element: (isEditing?: boolean) => isEditing ? require('car.dynamic')?.default : dynamic(carModule.module)\n" +
'}\n' +
'\n' +
'const components = new Map();\n' +
'export const components = new Map();\n' +
"components.set('Foo', Foo);\n" +
'\n' +
"components.set('TextComponent', Text);\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ ${componentFiles
})
.join('\n')}
const components = new Map();
export const components = new Map();
${packages
.map((p) =>
p.components.map(
Expand Down
Loading

0 comments on commit 4a03707

Please sign in to comment.