@quintype/framework 7.4.0-upgrade-react.1 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [7.4.0](https://github.com/quintype/quintype-node-framework/compare/v7.2.0...v7.4.0) (2022-05-23)
6
+
7
+
8
+ ### Features
9
+
10
+ * **request:** Implement request fallback for external calls ([#296](https://github.com/quintype/quintype-node-framework/issues/296)) ([44e1f7e](https://github.com/quintype/quintype-node-framework/commit/44e1f7eea9825225c0c300fb57a8178bd22c0630))
11
+
12
+ ### [7.3.4](https://github.com/quintype/quintype-node-framework/compare/v7.3.3...v7.3.4) (2022-05-20)
13
+
14
+ ### [7.3.3](https://github.com/quintype/quintype-node-framework/compare/v7.3.2...v7.3.3) (2022-05-20)
15
+
5
16
  ### [7.3.2](https://github.com/quintype/quintype-node-framework/compare/v7.3.1...v7.3.2) (2022-04-18)
6
17
 
7
18
  ### [7.3.1](https://github.com/quintype/quintype-node-framework/compare/v7.3.0...v7.3.1) (2022-03-03)
package/README.md CHANGED
@@ -145,23 +145,30 @@ startApp(renderApplication, CUSTOM_REDUCERS, {
145
145
 
146
146
  Steps to Integrate FCM in your project
147
147
 
148
- 1. While executing startApp in your project set enableFCM to true.
148
+ 1. app/client/app.js While executing startApp in your project send the firebaseConfig via opts. `firebaseConfig` can either be an object or a function that returns the firebase config object
149
149
 
150
- An Example
150
+ Example:
151
151
 
152
- ```
152
+ ```js
153
153
  startApp(renderApplication,
154
154
  CUSTOM_REDUCERS,
155
155
  {
156
156
  enableServiceWorker: process.env.NODE_ENV === "production",
157
- fcmMessagingSenderId: (page) => <MessageSenderId> || fcmMessagingSenderId: <MessageSenderId> {(function|string)}
157
+ firebaseConfig: {
158
+ messagingSenderId: --YOUR KEY-- ,
159
+ projectId: --YOUR KEY--,
160
+ apiKey: --YOUR KEY--,
161
+ storageBucket: --YOUR KEY--,
162
+ authDomain: --YOUR KEY--,
163
+ appId: --YOUR KEY--,
164
+ }
158
165
  ...
159
166
  })
160
167
  ```
161
168
 
162
169
  2. app/server/app.js should have the fcm configuration as below:
163
170
 
164
- ```
171
+ ```js
165
172
  isomorphicRoutes(app, {
166
173
  ...
167
174
  fcmServerKey: (config) => <ServerKey> || fcmServerKey: <ServerKey> {(function|string)}
@@ -174,28 +181,45 @@ isomorphicRoutes(app, {
174
181
 
175
182
  Example of the script:
176
183
 
177
- ```
178
- importScripts("https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js");
179
- importScripts("https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js");
180
- firebase.initializeApp({
181
- messagingSenderId: <your message sender Id>
182
- });
183
- const messaging = firebase.messaging();
184
- function messageHandler(payload) {
185
- const data = payload["data"];
186
-
187
- var notificationTitle = data.title;
188
- var notificationOptions = {
189
- body: data.body,
190
- icon: data["hero_image_s3_url"],
191
- image: data["hero_image_s3_url"],
192
- data: data
193
- };
194
-
195
- return self.registration.showNotification(notificationTitle,
196
- notificationOptions);
197
- }
198
- messaging.setBackgroundMessageHandler(messageHandler);
184
+ ```js
185
+ importScripts("https://www.gstatic.com/firebasejs/9.6.10/firebase-app-compat.js");
186
+ importScripts("https://www.gstatic.com/firebasejs/9.6.10/firebase-messaging-compat.js");
187
+
188
+ firebase.initializeApp({
189
+ messagingSenderId: --YOUR KEY-- ,
190
+ projectId: --YOUR KEY--,
191
+ apiKey: --YOUR KEY--,
192
+ storageBucket: --YOUR KEY--,
193
+ authDomain: --YOUR KEY--,
194
+ appId: --YOUR KEY--,
195
+ });
196
+ self.addEventListener('notificationclick', (event) => {
197
+
198
+ const url = event.notification.data.url;
199
+ if(url) {
200
+ clients.openWindow(url);
201
+ }
202
+ clients.openWindow(-- YOUR Sketches Host --);
203
+ }, false);
204
+
205
+ const messaging = firebase.messaging();
206
+
207
+ messaging.onBackgroundMessage(function(payload) {
208
+ const data = payload["data"];
209
+ const notificationTitle = data.title || ""
210
+ const notificationOptions = {
211
+ body: data.body,
212
+ icon: data["hero_image_s3_url"],
213
+ image: data["hero_image_s3_url"],
214
+ data: {
215
+ url: data["click_action"],
216
+ }
217
+ };
218
+
219
+ self.registration.showNotification(notificationTitle,
220
+ notificationOptions);
221
+ });
222
+
199
223
  ```
200
224
 
201
225
  4. Make sure that the page data should have config with key fcmMessageSenderId refer doStartApp function in app/client/start.js.
@@ -1,39 +1,26 @@
1
- export function initializeFCM(messageSenderId) {
2
- if (!messageSenderId) {
3
- console.log("messageSenderId is required");
4
- return false;
5
- }
1
+ export function initializeFCM(firebaseConfig) {
6
2
  Promise.all([
7
3
  import(/* webpackChunkName: "firebase-app" */ "firebase/app"),
8
4
  import(/* webpackChunkName: "firebase-messaging" */ "firebase/messaging"),
9
- ]).then(([firebase, m]) => {
10
- try {
11
- firebase.initializeApp({
12
- messagingSenderId: messageSenderId.toString(),
5
+ ])
6
+ .then(([firebase, m]) => {
7
+ const app = firebase.initializeApp({
8
+ messagingSenderId: firebaseConfig.messagingSenderId.toString(),
9
+ projectId: firebaseConfig.projectId,
10
+ apiKey: firebaseConfig.apiKey,
11
+ storageBucket: firebaseConfig.storageBucket,
12
+ authDomain: firebaseConfig.authDomain,
13
+ appId: firebaseConfig.appId,
13
14
  });
14
- const messaging = firebase.messaging();
15
- messaging.requestPermission().then(() => {
16
- updateToken(firebase)
17
- .then(() => {
18
- messaging.onTokenRefresh(() => updateToken(firebase));
19
- })
20
- .catch(console.error);
21
- });
22
- } catch (error) {
23
- console.error(error);
24
- }
25
- });
26
- }
27
-
28
- function updateToken(firebaseInstance) {
29
- return firebaseInstance
30
- .messaging()
31
- .getToken()
15
+ const messaging = m.getMessaging(app);
16
+ return m.getToken(messaging);
17
+ // No need to refresh token https://github.com/firebase/firebase-js-sdk/issues/4132
18
+ })
32
19
  .then((token) => {
33
20
  return registerFCMTopic(token);
34
21
  })
35
22
  .catch((err) => {
36
- throw new Error(err);
23
+ console.error(err);
37
24
  });
38
25
  }
39
26
 
package/client/start.js CHANGED
@@ -282,10 +282,14 @@ export function startApp(renderApplication, reducers, opts) {
282
282
 
283
283
  store.dispatch({ type: NAVIGATE_TO_PAGE, page, currentPath: path });
284
284
 
285
- if (opts.fcmMessagingSenderId) {
286
- const mssgSenderId =
287
- typeof opts.fcmMessagingSenderId === "function" ? opts.fcmMessagingSenderId(page) : opts.fcmMessagingSenderId;
288
- mssgSenderId && initializeFCM(mssgSenderId);
285
+ if (opts.firebaseConfig) {
286
+ const fcm = typeof opts.firebaseConfig === "function" ? opts.firebaseConfig(page) : opts.firebaseConfig;
287
+ if (fcm) {
288
+ const mssgSenderId = fcm.messagingSenderId;
289
+ const projectId = fcm.projectId;
290
+ const apiKey = fcm.apiKey;
291
+ if (mssgSenderId && projectId && apiKey) initializeFCM(fcm);
292
+ }
289
293
  }
290
294
 
291
295
  const { config: { "theme-attributes": pageThemeAttributes = {} } = {} } = page;
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@quintype/framework",
3
- "version": "7.4.0-upgrade-react.1",
3
+ "version": "7.4.0",
4
4
  "description": "Libraries to help build Quintype Node.js apps",
5
5
  "main": "index.js",
6
+ "engines": {
7
+ "node": "^16.0.0",
8
+ "npm": "^8.5.0"
9
+ },
6
10
  "scripts": {
7
11
  "prepublishOnly": "npm test && ./bin-dev-scripts/standard-version-release.sh",
8
12
  "test": "NODE_ENV=test npx mocha --recursive --require ./test/babel",
@@ -27,18 +31,19 @@
27
31
  "homepage": "https://github.com/quintype/quintype-node-framework#readme",
28
32
  "dependencies": {
29
33
  "@ampproject/toolbox-optimizer": "2.8.3",
30
- "@quintype/amp": "^2.4.19",
34
+ "@quintype/amp": "^2.4.21",
31
35
  "@quintype/backend": "^2.3.1",
32
36
  "@quintype/components": "^3.0.0",
33
37
  "@quintype/prerender-node": "^3.2.24",
34
- "@quintype/seo": "^1.38.1",
38
+ "@quintype/seo": "^1.39.0",
35
39
  "atob": "^2.1.2",
40
+ "babel-plugin-react-css-modules": "^5.2.6",
36
41
  "chalk": "^4.1.2",
37
42
  "cluster": "^0.7.7",
38
43
  "compression": "^1.7.4",
39
44
  "ejs": "^3.1.6",
40
45
  "express": "^4.17.1",
41
- "firebase": "^6.0.2",
46
+ "firebase": "^9.6.10",
42
47
  "get-youtube-id": "^1.0.1",
43
48
  "grpc": "^1.21.1",
44
49
  "http-proxy": "^1.18.1",
@@ -47,8 +52,8 @@
47
52
  "mocha-snapshots": "^4.2.0",
48
53
  "morgan": "^1.10.0",
49
54
  "path-to-regexp": "^6.2.0",
50
- "react": "^17.0.2",
51
- "react-dom": "^17.0.2",
55
+ "react": "^16.14.0",
56
+ "react-dom": "^16.14.0",
52
57
  "react-redux": "^7.2.5",
53
58
  "react-router": "^5.2.1",
54
59
  "redux": "^4.1.1",
@@ -60,9 +65,8 @@
60
65
  "@babel/eslint-parser": "^7.15.7",
61
66
  "@loadable/component": "^5.15.0",
62
67
  "@loadable/server": "^5.15.1",
63
- "@quintype/build": "^3.13.1",
68
+ "@quintype/build": "^4.0.0",
64
69
  "babel-plugin-quintype-assets": "^1.1.1",
65
- "babel-plugin-react-css-modules": "^5.2.6",
66
70
  "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
67
71
  "babel-plugin-transform-object-rest-spread": "^6.26.0",
68
72
  "babel-preset-es2015": "^6.24.1",
@@ -7,6 +7,7 @@ const { Story, AmpConfig } = require("../../impl/api-client-impl");
7
7
  const { optimize, getDomainSpecificOpts } = require("../helpers");
8
8
  const { storyToCacheKey } = require("../../caching");
9
9
  const { addCacheHeadersToResult } = require("../../handlers/cdn-caching");
10
+ const { getRedirectUrl } = require("../../../server/redirect-url-helper");
10
11
 
11
12
  /**
12
13
  * ampStoryPageHandler gets all the things needed and calls "ampifyStory" function (which comes from ampLib)
@@ -37,6 +38,20 @@ async function ampStoryPageHandler(
37
38
  ) {
38
39
  try {
39
40
  const opts = cloneDeep(rest);
41
+
42
+ const redirectUrls = opts && opts.redirectUrls;
43
+ const getEnableAmp = get(opts, ["enableAmp"], true);
44
+
45
+ const enableAmp = typeof getEnableAmp === "function" ? opts.enableAmp(config) : getEnableAmp;
46
+
47
+ if (typeof redirectUrls === "function" || (redirectUrls && Object.keys(redirectUrls).length > 0)) {
48
+ await getRedirectUrl(req, res, next, { redirectUrls, config });
49
+ }
50
+
51
+ if (!enableAmp) {
52
+ return res.redirect(301, `/${req.params[0]}`);
53
+ }
54
+
40
55
  const domainSpecificOpts = getDomainSpecificOpts(opts, domainSlug);
41
56
  const url = urlLib.parse(req.url, true);
42
57
  const { ampifyStory, unsupportedStoryElementsPresent } = ampLibrary;
package/server/routes.js CHANGED
@@ -232,12 +232,13 @@ function wrapLoadDataWithMultiDomain(publisherConfig, f, configPos) {
232
232
  * @param {Object} opts Options that will be passed to the handler. These options will be merged with a *config* and *client*
233
233
  */
234
234
  function getWithConfig(app, route, handler, opts = {}) {
235
+ const configWrapper = opts.configWrapper;
235
236
  const {
236
237
  getClient = require("./api-client").getClient,
237
238
  publisherConfig = require("./publisher-config"),
238
239
  logError = require("./logger").error,
239
240
  } = opts;
240
- const withConfig = withConfigPartial(getClient, logError, publisherConfig);
241
+ const withConfig = withConfigPartial(getClient, logError, publisherConfig, configWrapper);
241
242
  app.get(route, withConfig(handler, opts));
242
243
  }
243
244
 
@@ -608,6 +609,8 @@ exports.mountQuintypeAt = function (app, mountAt) {
608
609
  * @param {Object} opts.templates An object that's used to pass custom templates. Each key corresponds to the template name and corresponding value is the template
609
610
  * @param {Object} opts.slots An object used to pass slot data.
610
611
  * @param {SEO} opts.seo An SEO object that will generate html tags for each page. See [@quintype/seo](https://developers.quintype.com/malibu/isomorphic-rendering/server-side-architecture#quintypeseo)
612
+ * @param {boolean|function} opts.enableAmp 'amp/story/:slug' should redirect to non-amp page if enableAmp is false
613
+ * @param {object|function} opts.redirectUrls list of urls which is used to redirect URL(sourceUrl) to a different URL(destinationUrl). Eg: redirectUrls: { "/amp/story/sports/ipl-2021": {destinationUrl: "/amp/story/sports/cricket-2022", statusCode: 302,},}
611
614
  * @param {function} opts.headerCardRender Render prop for story headerCard. If passed, the headerCard in default stories will be replaced with this
612
615
  * @param {function} opts.relatedStoriesRender Render prop for relatedStories in a story page. If passed, this will replace the related stories
613
616
  *
package/test/babel.js CHANGED
@@ -2,7 +2,11 @@ const ROOT_PATH = require("path").resolve(__dirname, "..");
2
2
 
3
3
  require("babel-register")({
4
4
  presets: ["react"],
5
- plugins: ["transform-es2015-modules-commonjs", "transform-object-rest-spread", "quintype-assets"],
5
+ plugins: [
6
+ "transform-es2015-modules-commonjs",
7
+ "transform-object-rest-spread",
8
+ "quintype-assets",
9
+ ],
6
10
  ignore(file) {
7
11
  return file.startsWith(ROOT_PATH + "/node_modules");
8
12
  },
@@ -70,16 +70,14 @@ function getClientStub({
70
70
  "story-elements": [
71
71
  {
72
72
  description: "",
73
- "page-url":
74
- "/story/7f3d5bdb-ec52-4047-ac0d-df4036ec974b/element/9eb8f5cc-6ebe-4fb0-88b8-eca79efde210",
73
+ "page-url": "/story/7f3d5bdb-ec52-4047-ac0d-df4036ec974b/element/9eb8f5cc-6ebe-4fb0-88b8-eca79efde210",
75
74
  type: "text",
76
75
  "family-id": "e9e12f9f-8b9f-4b93-a8c8-83c7b278000f",
77
76
  title: "",
78
77
  id: "9eb8f5cc-6ebe-4fb0-88b8-eca79efde210",
79
78
  metadata: {},
80
79
  subtype: null,
81
- text:
82
- "<p>In India today, the legal profession is growing in lockstep with one of the world’s most dynamic economies. It’s no surprise then— that in terms of absolute numbers— India’s legal profession is the world’s second largest, with over 1.4 million enrolled lawyers in legal practices nationwide.</p>",
80
+ text: "<p>In India today, the legal profession is growing in lockstep with one of the world’s most dynamic economies. It’s no surprise then— that in terms of absolute numbers— India’s legal profession is the world’s second largest, with over 1.4 million enrolled lawyers in legal practices nationwide.</p>",
83
81
  },
84
82
  ],
85
83
  "card-updated-at": 1581327522163,
@@ -195,20 +193,18 @@ const dummyAmpLib = {
195
193
 
196
194
  const getClientStubWithRelatedStories = (relatedStories) => () =>
197
195
  Object.assign({}, getClientStub(), {
198
- getRelatedStories: () =>
199
- Promise.resolve({ "related-stories": relatedStories }),
196
+ getRelatedStories: () => Promise.resolve({ "related-stories": relatedStories }),
200
197
  });
201
198
 
202
- function createApp({
203
- clientStub = getClientStub,
204
- ampLibrary = dummyAmpLib,
205
- } = {}) {
199
+ function createApp({ clientStub = getClientStub, ampLibrary = dummyAmpLib, enableAmp = true, redirectUrls = {} } = {}) {
206
200
  const app = express();
207
201
  ampRoutes(app, {
208
202
  getClient: clientStub,
209
203
  publisherConfig: {},
210
204
  ampLibrary,
211
205
  additionalConfig: {},
206
+ enableAmp,
207
+ redirectUrls,
212
208
  });
213
209
  return app;
214
210
  }
@@ -240,6 +236,38 @@ describe("ampStoryPageHandler integration tests", () => {
240
236
  return done();
241
237
  });
242
238
  });
239
+ it("should redirect to non-amp page if enableAmp is false", (done) => {
240
+ const app = createApp({
241
+ enableAmp: false,
242
+ });
243
+ supertest(app)
244
+ .get("/amp/story/cricket/ipl-2021")
245
+ .expect(301)
246
+ .expect("Location", "/cricket/ipl-2021")
247
+ .end((err) => {
248
+ if (err) return done(err);
249
+ return done();
250
+ });
251
+ });
252
+ it("Redirects the urls with status code to 302 if redirectUrls is present ", (done) => {
253
+ const app = createApp({
254
+ enableAmp: true,
255
+ redirectUrls: {
256
+ "/amp/story/sports/ipl-2021": {
257
+ destinationUrl: "/amp/story/sports/cricket-2022",
258
+ statusCode: 302,
259
+ },
260
+ },
261
+ });
262
+ supertest(app)
263
+ .get("/amp/story/sports/ipl-2021")
264
+ .expect(302)
265
+ .expect("Location", "/amp/story/sports/cricket-2022")
266
+ .end((err) => {
267
+ if (err) return done(err);
268
+ return done();
269
+ });
270
+ });
243
271
  it("should redirect to non-amp page if story contains unsuported elements and invalid-elements-strategy in /api/v1/amp/config is set to redirect-to-web-version", (done) => {
244
272
  const app = createApp({
245
273
  ampLibrary: {