Skip to content

Commit 60ddc3f

Browse files
[Angular] Edge Proxy / Context Id support (#1875)
1 parent d5b46f1 commit 60ddc3f

File tree

32 files changed

+309
-142
lines changed

32 files changed

+309
-142
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Our versioning strategy is as follows:
1414
### 🐛 Bug Fixes
1515

1616
* `[templates/nextjs-sxa]` The caption of image component has been fixed. ([#1874](https://github.com/Sitecore/jss/pull/1874))
17+
* `[templates/nextjs]` `[templates/react]` `[templates/angular]` `[templates/vue]` Fixed an issue when environment variable is undefined (not present in .env), that produced an "undefined" value in generated config file ([#1875](https://github.com/Sitecore/jss/pull/1875))
1718

1819
### 🎉 New Features & Improvements
1920

@@ -24,6 +25,7 @@ Our versioning strategy is as follows:
2425
* XMCloud-based: 'angular,angular-xmcloud'
2526
* Rework Angular initializer to support XMCloud and SXP journeys;
2627
* Add SXA styles to xmcloud addon
28+
* `[create-sitecore-jss]` `[template/angular]` `[template/angular-xmcloud]` `[template/node-xmcloud-proxy]` Edge Proxy / Context Id support ([#1875](https://github.com/Sitecore/jss/pull/1875))
2729

2830
* `[create-sitecore-jss]` Rework Angular initializer to support XMCloud and SXP journeys ([#1845](https://github.com/Sitecore/jss/pull/1845))([#1858](https://github.com/Sitecore/jss/pull/1858))([#1868](https://github.com/Sitecore/jss/pull/1868))
2931
* `[create-sitecore-jss]` Allow node-xmcloud-proxy app to be installed alongside Angular SPA application

docs/upgrades/unreleased.md

+68-6
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,90 @@ If you plan to use the Angular SDK with XMCloud, you will need to perform next s
1111
"build:client": "cross-env-shell ng build --configuration=production --base-href $npm_package_config_sitecoreDistPath/browser/ --output-path=$npm_package_config_buildArtifactsPath/browser/"
1212
```
1313

14-
* Update "server.bundle.ts" to additionally expose new properties:
14+
* Restructure /src/app/lib/client-factory.ts. This is needed in order to separate the GraphQL client factory configuration from the client factory itself, so we have a single source of GraphQL endpoint resolution that can be used in different places. For example node-xmcloud-proxy, scripts/update-graphql-fragment-data.ts, etc.
15+
* Introduce /src/app/lib/graphql-client-factory/config.ts. It should expose the _getGraphQLClientFactoryConfig_ that returns the configuration object for the GraphQL client factory, for example (full code snippet you can find in the "angular-xmcloud" add-on):
16+
17+
```ts
18+
import { GraphQLRequestClientFactoryConfig } from '@sitecore-jss/sitecore-jss-angular/cjs';
19+
import { environment as env } from '../../../environments/environment';
20+
21+
export const getGraphQLClientFactoryConfig = () => {
22+
let clientConfig: GraphQLRequestClientFactoryConfig;
23+
24+
if (env.graphQLEndpoint && env.sitecoreApiKey) {
25+
clientConfig = {
26+
endpoint: env.graphQLEndpoint,
27+
apiKey: env.sitecoreApiKey,
28+
};
29+
}
30+
31+
...
32+
33+
return clientConfig;
34+
};
35+
```
36+
37+
* Introduce /src/app/lib/graphql-client-factory/index.ts. It should contain the _default_ export that returns the GraphQL client factory, for example:
38+
39+
```ts
40+
import { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss-angular/cjs';
41+
import { getGraphQLClientFactoryConfig } from './config';
42+
43+
const createGraphQLClientFactory = () => {
44+
const clientConfig = getGraphQLClientFactoryConfig();
45+
46+
return GraphQLRequestClient.createClientFactory(clientConfig);
47+
};
48+
49+
export default createGraphQLClientFactory();
50+
```
51+
52+
* Make sure to import variables from @sitecore-jss/sitecore-jss-angular/cjs, not from @sitecore-jss/sitecore-jss-angular, since graphql-client-factory is used in the server bundle.
53+
54+
* Update all the references to the GraphQL client factory in the application to use the new structure.
55+
56+
* Update /scripts/update-graphql-fragment-data.ts to utilize the GraphQL client factory and client factory configuration. The implementation you can find in the "angular" template (/scripts/update-graphql-fragment-data.ts
57+
)
58+
* Remove "isomorphic-fetch" npm dependency
59+
60+
* Update /scripts/generate-config.ts, it's needed since generate-config can be called outside of /scripts/bootstrap.ts file. Add dotenv import:
61+
62+
```ts
63+
import 'dotenv/config';
64+
```
65+
66+
67+
* Update /server.bundle.ts to additionally expose new properties:
1568
1669
```ts
1770
import { environment } from './src/environments/environment';
18-
import { clientFactory } from './src/app/lib/graphql-client-factory';
71+
import clientFactory from './src/app/lib/graphql-client-factory';
72+
import { getGraphQLClientFactoryConfig } from './src/app/lib/graphql-client-factory/config';
1973
import { dictionaryServiceFactory } from './src/app/lib/dictionary-service-factory';
2074
import { layoutServiceFactory } from './src/app/lib/layout-service-factory';
2175
2276
...
2377
const defaultLanguage = environment.defaultLanguage;
24-
const graphQLEndpointPath = environment.graphQLEndpointPath;
25-
const graphQLEndpoint = environment.graphQLEndpoint;
78+
const getClientFactoryConfig = getGraphQLClientFactoryConfig;
2679
2780
export {
2881
...
2982
clientFactory,
83+
getClientFactoryConfig,
3084
dictionaryServiceFactory,
3185
layoutServiceFactory,
3286
defaultLanguage,
33-
graphQLEndpointPath,
34-
graphQLEndpoint,
3587
};
3688
```
3789
3890
* GraphQL FETCH_WITH method is required to be used, REST is not supported. Update FETCH_WITH environment variable if needed.
91+
92+
* Update /src/app/lib/dictionary-service-factory.ts to use new `useSiteQuery` property. You will be able to use a Site query to get the dictionary data:
93+
94+
```ts
95+
new GraphQLDictionaryService({
96+
clientFactory,
97+
siteName: env.sitecoreSiteName,
98+
useSiteQuery: true,
99+
});
100+
```

packages/create-sitecore-jss/src/templates/angular-sxp/scripts/config/plugins/disconnected.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const chalk = require('chalk');
88
*/
99
class DisconnectedPlugin implements ConfigPlugin {
1010
// should come before other plugins
11-
order = 0;
11+
order = 2;
1212

1313
async exec(config: JssConfig) {
1414
const disconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED;
@@ -20,7 +20,9 @@ class DisconnectedPlugin implements ConfigPlugin {
2020
);
2121
}
2222

23-
return config;
23+
return Object.assign({}, config, {
24+
sitecoreApiKey: config.sitecoreApiKey || 'no-api-key-set',
25+
});
2426
}
2527
}
2628

packages/create-sitecore-jss/src/templates/angular-xmcloud/.env

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11

2+
# ========== Sitecore Edge Platform ===========
3+
4+
# Your unified Sitecore Edge Context Id.
5+
# This will be used over any Sitecore Preview / Delivery Edge variables (above).
6+
SITECORE_EDGE_CONTEXT_ID=
7+
28
# ========== XM Cloud Proxy ===========
39

410
# Your XM Cloud Proxy hostname is needed to build the app and execute the client-side requests against the proxy server.

packages/create-sitecore-jss/src/templates/angular-xmcloud/scripts/config/plugins/xmcloud.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { JssConfig } from 'lib/config';
22
import { ConfigPlugin } from '..';
33
import { constantCase } from 'constant-case';
4+
import chalk from 'chalk';
45

56
/**
67
* This plugin will set XM Cloud related config props.
@@ -13,9 +14,24 @@ class XMCloudPlugin implements ConfigPlugin {
1314
const proxyBuildPath = process.env[`${constantCase('proxyBuildPath')}`]?.replace(/\/$/, '');
1415
const proxyHost = process.env[`${constantCase('proxyHost')}`];
1516

17+
const sitecoreEdgeUrl =
18+
process.env[`${constantCase('sitecoreEdgeUrl')}`]?.replace(/\/$/, '') ||
19+
'https://edge-platform.sitecorecloud.io';
20+
const sitecoreEdgeContextId = process.env[`${constantCase('sitecoreEdgeContextId')}`];
21+
22+
if (config.sitecoreApiKey && sitecoreEdgeContextId) {
23+
console.log(
24+
chalk.yellow(
25+
"You have configured both 'sitecoreApiKey' and 'sitecoreEdgeContextId' values. The 'sitecoreEdgeContextId' is used instead."
26+
)
27+
);
28+
}
29+
1630
return Object.assign({}, config, {
1731
proxyBuildPath,
1832
proxyHost,
33+
sitecoreEdgeUrl,
34+
sitecoreEdgeContextId,
1935
});
2036
}
2137
}

packages/create-sitecore-jss/src/templates/angular-xmcloud/src/app/lib/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ export interface JssConfig extends Record<string, string | boolean | undefined>
1212
defaultServerRoute?: string;
1313
proxyBuildPath?: string;
1414
proxyHost?: string;
15+
sitecoreEdgeUrl?: string;
16+
sitecoreEdgeContextId?: string;
1517
}

packages/create-sitecore-jss/src/templates/angular-xmcloud/src/app/lib/graphql-client-factory.ts

-44
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
GraphQLRequestClientFactoryConfig,
3+
getEdgeProxyContentUrl,
4+
} from '@sitecore-jss/sitecore-jss-angular/cjs';
5+
import { environment as env } from '../../../environments/environment';
6+
7+
/**
8+
* Gets the configuration for the GraphQLRequestClientFactory
9+
* @returns GraphQLRequestClientFactoryConfig
10+
*/
11+
export const getGraphQLClientFactoryConfig = () => {
12+
let clientConfig: GraphQLRequestClientFactoryConfig;
13+
14+
// Server side requests should go directly to the Sitecore, browser requests should go through the proxy.
15+
const isServer = typeof window === 'undefined';
16+
// If we are in a production environment we are going to use Node XM Cloud proxy
17+
const isProduction = env.production === 'true';
18+
19+
if (isProduction) {
20+
if (!env.proxyHost) {
21+
throw new Error('Please configure your proxyHost.');
22+
}
23+
24+
if (env.sitecoreEdgeContextId) {
25+
clientConfig = {
26+
endpoint: isServer
27+
? getEdgeProxyContentUrl(env.sitecoreEdgeContextId, env.sitecoreEdgeUrl)
28+
: getEdgeProxyContentUrl(env.sitecoreEdgeContextId, env.proxyHost),
29+
};
30+
} else if (env.graphQLEndpoint && env.sitecoreApiKey) {
31+
const graphQLEndpointPath = new URL(env.graphQLEndpoint).pathname;
32+
33+
clientConfig = {
34+
endpoint: isServer ? env.graphQLEndpoint : `${env.proxyHost}${graphQLEndpointPath}`,
35+
apiKey: env.sitecoreApiKey,
36+
};
37+
}
38+
} else {
39+
if (env.sitecoreEdgeContextId) {
40+
clientConfig = {
41+
endpoint: getEdgeProxyContentUrl(env.sitecoreEdgeContextId, env.sitecoreEdgeUrl),
42+
};
43+
} else if (env.graphQLEndpoint && env.sitecoreApiKey) {
44+
clientConfig = {
45+
endpoint: env.graphQLEndpoint,
46+
apiKey: env.sitecoreApiKey,
47+
};
48+
}
49+
}
50+
51+
if (!clientConfig.endpoint) {
52+
throw new Error(
53+
'Please configure either your sitecoreEdgeContextId, or your graphQLEndpoint and sitecoreApiKey.'
54+
);
55+
}
56+
57+
return clientConfig;
58+
};

packages/create-sitecore-jss/src/templates/angular-xmcloud/src/app/routing/layout/layout.component.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Subscription } from 'rxjs';
1111
import { JssState } from '../../JssState';
1212
import { JssMetaService } from '../../jss-meta.service';
1313
import { JssLinkService } from '../../jss-link.service';
14+
import { environment as env } from '../../../environments/environment';
1415

1516
enum LayoutState {
1617
Layout,
@@ -58,13 +59,10 @@ export class LayoutComponent implements OnInit, OnDestroy {
5859
? 'editing-mode'
5960
: 'prod-mode';
6061

61-
/** TODO: get contextId and edgeUrl properly **/
62-
const sitecoreEdgeContextId = '';
63-
const sitecoreEdgeUrl = '';
6462
const contentStyles = getContentStylesheetLink(
6563
{ sitecore: data.jssState.sitecore },
66-
sitecoreEdgeContextId,
67-
sitecoreEdgeUrl
64+
env.sitecoreEdgeContextId,
65+
env.sitecoreEdgeUrl
6866
);
6967

7068
// Clear existing stylesheets

packages/create-sitecore-jss/src/templates/angular/.env

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# ====== Sitecore Preview / Delivery Edge ======
2+
13
# Your Sitecore API key is needed to build the app. Typically, the API key is
24
# defined in `scjssconfig.json` (as `sitecore.apiKey`). This file may not exist
35
# when building locally (if you've never run `jss setup`), or when building in a
@@ -17,6 +19,8 @@ SITECORE_API_HOST=
1719
# the resolved Sitecore API hostname + the `graphQLEndpointPath` defined in your `package.json`.
1820
GRAPH_QL_ENDPOINT=
1921

22+
# ==============================================
23+
2024
# Your Sitecore site name.
2125
# Uses your `package.json` config `appName` if empty.
2226
SITECORE_SITE_NAME=

packages/create-sitecore-jss/src/templates/angular/package.json

-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@
8181
"@sitecore-jss/sitecore-jss-angular-schematics": "~22.2.0-canary",
8282
"@sitecore-jss/sitecore-jss-cli": "~22.2.0-canary",
8383
"@sitecore-jss/sitecore-jss-dev-tools": "~22.2.0-canary",
84-
"@types/isomorphic-fetch": "0.0.35",
8584
"@types/jasmine": "~3.6.7",
8685
"@types/jasminewd2": "~2.0.8",
8786
"@types/node": "~20.14.10",
@@ -99,7 +98,6 @@
9998
"eslint-plugin-import": "2.29.1",
10099
"eslint-plugin-jsdoc": "48.7.0",
101100
"eslint-plugin-prefer-arrow": "1.2.3",
102-
"isomorphic-fetch": "^3.0.0",
103101
"jasmine-core": "~3.7.1",
104102
"jasmine-spec-reporter": "~6.0.0",
105103
"karma": "^6.3.2",

packages/create-sitecore-jss/src/templates/angular/scripts/config/plugins/fallback.ts

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ class FallbackPlugin implements ConfigPlugin {
1212
async exec(config: JssConfig) {
1313
return Object.assign({}, config, {
1414
defaultLanguage: config.defaultLanguage || 'en',
15-
sitecoreApiKey: config.sitecoreApiKey || 'no-api-key-set',
1615
<% if (!locals.xmcloud) { -%>
1716
layoutServiceConfigurationName: config.layoutServiceConfigurationName || 'default',
1817
<% } -%>

packages/create-sitecore-jss/src/templates/angular/scripts/generate-config.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dotenv/config';
12
import * as fs from 'fs';
23
import * as path from 'path';
34
import { constantCase } from 'constant-case';
@@ -38,6 +39,14 @@ export function generateConfig(
3839
defaultConfig: JssConfig = defaultConfigValue,
3940
configOverrides?: { [key: string]: unknown }
4041
) {
42+
// Handle undefined values
43+
defaultConfig = Object.keys(defaultConfig).reduce((acc, key) => {
44+
return {
45+
...acc,
46+
[key]: defaultConfig[key] || '',
47+
};
48+
}, {});
49+
4150
jssConfigFactory
4251
.create(defaultConfig)
4352
.then((config) => {
@@ -67,9 +76,11 @@ export function writeConfig(config: JssConfig, outputPath?: string) {
6776

6877
// Set base configuration values, allowing override with environment variables
6978
Object.keys(config).forEach((prop) => {
70-
configText += `config.${prop} = process.env.${constantCase(prop)} || "${config[prop]
71-
?.toString()
72-
.trim()}";\n`;
79+
// Handle undefined values
80+
const value = config[prop] || '';
81+
configText += `config.${prop} = process.env.${constantCase(
82+
prop
83+
)} || "${value.toString().trim()}";\n`;
7384
});
7485

7586
configText += `module.exports.environment = config;`;

0 commit comments

Comments
 (0)