Skip to content

Commit 3cf8659

Browse files
authored
Merge pull request #52 from contentstack/fix/metada_fix_ECO-3354
fix:[ECO-3354] Added datadog properties & Error boundary check
2 parents 9e42fa7 + c65c466 commit 3cf8659

File tree

8 files changed

+126
-108
lines changed

8 files changed

+126
-108
lines changed

ui/.eslintrc.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ module.exports = {
33
browser: true,
44
es2021: true,
55
},
6-
extends: [
7-
"plugin:react/recommended",
8-
"plugin:import/typescript",
9-
"airbnb",
10-
"airbnb-typescript",
11-
"prettier",
12-
],
6+
extends: ["plugin:react/recommended", "plugin:import/typescript", "airbnb", "airbnb-typescript", "prettier"],
137
parser: "@typescript-eslint/parser",
148
parserOptions: {
159
ecmaFeatures: {
@@ -23,6 +17,9 @@ module.exports = {
2317
},
2418
plugins: ["import", "react", "react-hooks", "@typescript-eslint"],
2519
rules: {
20+
"arrow-body-style": 0,
21+
"no-plusplus": 0,
22+
"no-underscore-dangle": 0,
2623
"operator-linebreak": [
2724
"error",
2825
"after",

ui/package-lock.json

Lines changed: 16 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"jotai": "^2.2.1",
1515
"jsoneditor": "^9.5.8",
1616
"jsoneditor-react": "^3.1.2",
17+
"lodash": "^4.17.21",
1718
"node-polyfill-webpack-plugin": "^2.0.1",
1819
"prop-types": "^15.8.1",
1920
"react": "^18.2.0",
@@ -68,6 +69,7 @@
6869
"@testing-library/jest-dom": "^5.16.0",
6970
"@testing-library/react": "^12.1.2",
7071
"@types/jest": "^26.0.14",
72+
"@types/lodash": "^4.14.195",
7173
"@types/node": "^20.3.3",
7274
"@typescript-eslint/eslint-plugin": "^5.4.0",
7375
"@typescript-eslint/parser": "^5.4.0",

ui/src/common/utils/function.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Extension from "@contentstack/app-sdk/dist/src/extension";
2+
import { get, isEmpty, keys } from "lodash";
3+
4+
function getAppLocation(sdk: Extension): string {
5+
const locations = keys(sdk?.location);
6+
let locationName = "";
7+
for (let i = 0; i <= locations.length; i++) {
8+
if (!isEmpty(get(sdk, `location.${locations[i]}`, undefined))) {
9+
locationName = locations[i];
10+
break;
11+
}
12+
}
13+
return locationName;
14+
}
15+
16+
export default getAppLocation;

ui/src/components/ErrorBoundary/index.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable react/prop-types */
22
/* eslint-disable react/destructuring-assignment */
33
import React from "react";
4-
import { datadogRum } from '@datadog/browser-rum';
4+
import { datadogRum } from "@datadog/browser-rum";
55

66
interface MyProps {
77
children: React.ReactElement;
@@ -11,6 +11,8 @@ interface MyState {
1111
hasError: boolean;
1212
}
1313

14+
const ENV: string = process.env.NODE_ENV;
15+
1416
// datadog-rum Installation
1517
datadogRum.init({
1618
applicationId: `${process.env.REACT_APP_DATADOG_RUM_APPLICATION_ID}`,
@@ -22,13 +24,13 @@ datadogRum.init({
2224
trackInteractions: true,
2325
trackResources: true,
2426
trackLongTasks: true,
25-
defaultPrivacyLevel: 'mask-user-input',
27+
defaultPrivacyLevel: "mask-user-input",
2628
useCrossSiteSessionCookie: true,
2729
});
2830

2931
// sending MetaData to Datadog RUM
30-
datadogRum.setGlobalContextProperty('Application Type', 'Marketplace');
31-
datadogRum.setGlobalContextProperty('Application Name', 'JSON Editor App');
32+
datadogRum.setGlobalContextProperty("Application Type", "Marketplace");
33+
datadogRum.setGlobalContextProperty("Application Name", "JSON Editor App");
3234

3335
class ErrorBoundary extends React.Component<MyProps, MyState> {
3436
constructor(props: any) {
@@ -42,11 +44,13 @@ class ErrorBoundary extends React.Component<MyProps, MyState> {
4244
return { hasError: true };
4345
}
4446

45-
componentDidCatch(error: any, errorInfo: any) {
47+
componentDidCatch(error: Error) {
4648
// You can also log the error to an error reporting service
47-
// logErrorToMyService(error, errorInfo);
48-
console.error("errorInfo ", errorInfo);
49-
throw new Error(errorInfo);
49+
if (ENV === "development") {
50+
console.error(error);
51+
return;
52+
}
53+
datadogRum.addError(error);
5054
}
5155

5256
render() {

ui/src/containers/ConfigScreen/index.tsx

Lines changed: 22 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Import React modules */
22
/* eslint-disable */
33
import React, { useState, useEffect } from "react";
4+
import { get } from "lodash";
45
/* Import other node modules */
56

67
import VenusHelp from "../../components/venus-help";
@@ -11,6 +12,7 @@ import tippy from "tippy.js";
1112
import localeTexts from "../../common/locale/en-us";
1213
import constants from "../../common/constants";
1314
import { mergeObjects } from "../../common/utils";
15+
import getAppLocation from "../../common/utils/function";
1416
import { TypeAppSdkConfigState } from "../../common/types";
1517

1618
/* Import node module CSS */
@@ -23,12 +25,10 @@ import { useAppSdk } from "../../hooks/useAppSdk";
2325
import useAnalytics from "../../hooks/useAnalytics";
2426

2527
const ConfigScreen: React.FC = function () {
26-
2728
// error tracking hooks
28-
const { addMetadata, trackError } = useJsErrorTracker();
29+
const { setErrorsMetaData, trackError } = useJsErrorTracker();
2930
const { trackEvent } = useAnalytics();
3031
const [appSdk] = useAppSdk();
31-
3232

3333
const [state, setState] = useState<TypeAppSdkConfigState>({
3434
installationData: {
@@ -45,28 +45,30 @@ const ConfigScreen: React.FC = function () {
4545
},
4646
appSdkInitialized: false,
4747
});
48-
const [isStringified,setIsStringified]=useState(false);
48+
const [isStringified, setIsStringified] = useState(false);
4949

5050
useEffect(() => {
5151
ContentstackAppSdk.init()
5252
.then(async (appSdk) => {
5353
const sdkConfigData = appSdk?.location?.AppConfigWidget?.installation;
5454
if (sdkConfigData) {
55-
const installationDataFromSDK =
56-
await sdkConfigData?.getInstallationData();
55+
const installationDataFromSDK = await sdkConfigData?.getInstallationData();
5756
const setInstallationDataOfSDK = sdkConfigData?.setInstallationData;
5857
setState({
5958
...state,
60-
installationData: mergeObjects(
61-
state?.installationData,
62-
installationDataFromSDK
63-
),
59+
installationData: mergeObjects(state?.installationData, installationDataFromSDK),
6460
setInstallationData: setInstallationDataOfSDK,
6561
appSdkInitialized: true,
6662
});
67-
setIsStringified(
68-
state?.installationData?.configuration?.isStringified
69-
);
63+
setIsStringified(state?.installationData?.configuration?.isStringified);
64+
const appLocation: string = getAppLocation(appSdk);
65+
let properties = {
66+
Stack: appSdk?.stack._data.api_key,
67+
Organization: appSdk?.currentUser.defaultOrganization,
68+
"App Location": appLocation,
69+
"User Id": get(appSdk, "stack._data.collaborators.0.uid", ""), //first uuid from collaborators
70+
};
71+
setErrorsMetaData(properties); // set global event data for errors
7072
}
7173
})
7274
.catch((error) => {
@@ -117,60 +119,26 @@ const ConfigScreen: React.FC = function () {
117119
<div className="page-wrapper">
118120
<form data-testid="cs-form" className="config-wrapper">
119121
<div className="Form__item">
120-
<div
121-
className="Field Field--full json-field"
122-
data-testid="cs-field"
123-
>
124-
<label
125-
className="FieldLabel"
126-
htmlFor="isStringified"
127-
data-testid="cs-field-label"
128-
>
122+
<div className="Field Field--full json-field" data-testid="cs-field">
123+
<label className="FieldLabel" htmlFor="isStringified" data-testid="cs-field-label">
129124
{localeTexts.configFields.entrySaveRadioButton.label}
130125
<span className="FieldLabel__required-text">
131126
<span className="">*</span>
132127
</span>
133128
</label>
134-
<div
135-
className="tippy-wrapper"
136-
id="help-text"
137-
data-testid="cs-tooltip"
138-
>
129+
<div className="tippy-wrapper" id="help-text" data-testid="cs-tooltip">
139130
<VenusHelp />
140131
</div>
141132
<div className="Radio-wrapper">
142133
<label data-testid="cs-radio-one" className="Radio label-text">
143-
<input
144-
id="jsonObject"
145-
type="radio"
146-
name="isStringified"
147-
required
148-
value="false"
149-
checked={!isStringified}
150-
onChange={updateCustomJSON}
151-
/>
134+
<input id="jsonObject" type="radio" name="isStringified" required value="false" checked={!isStringified} onChange={updateCustomJSON} />
152135
<span className="Radio__box"></span>
153-
<span className="Radio__label">
154-
{localeTexts.configFields.entrySaveRadioButton.jsonObject}
155-
</span>
136+
<span className="Radio__label">{localeTexts.configFields.entrySaveRadioButton.jsonObject}</span>
156137
</label>
157138
<label data-testid="cs-radio-two" className="Radio label-text">
158-
<input
159-
id="stringified"
160-
type="radio"
161-
name="isStringified"
162-
required
163-
value="true"
164-
checked={isStringified}
165-
onChange={updateCustomJSON}
166-
/>
139+
<input id="stringified" type="radio" name="isStringified" required value="true" checked={isStringified} onChange={updateCustomJSON} />
167140
<span className="Radio__box"></span>
168-
<span className="Radio__label">
169-
{
170-
localeTexts.configFields.entrySaveRadioButton
171-
.jsonStringified
172-
}
173-
</span>
141+
<span className="Radio__label">{localeTexts.configFields.entrySaveRadioButton.jsonStringified}</span>
174142
</label>
175143
</div>
176144
<p className="InstructionText" data-testid="cs-instruction-text">

ui/src/containers/CustomField/index.tsx

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import React, { useEffect, useState } from "react";
2+
import { get } from "lodash";
23
import ContentstackAppSdk from "@contentstack/app-sdk";
34
import constants, { eventNames } from "../../common/constants";
45
import { isEmpty } from "../../common/utils";
56
import { TypeSDKData } from "../../common/types";
67
import "./styles.scss";
78
import useAnalytics from "../../hooks/useAnalytics";
89
import JSONEditor from "../../components/jsoneditor";
10+
import getAppLocation from "../../common/utils/function";
11+
import useJsErrorTracker from "../../hooks/useJsErrorTracker";
912

1013
const CustomField: React.FC = function () {
14+
// error tracking hooks
15+
const { setErrorsMetaData, trackError } = useJsErrorTracker();
1116
const [state, setState] = useState<TypeSDKData>({
1217
config: {},
1318
location: {},
@@ -48,51 +53,39 @@ const CustomField: React.FC = function () {
4853

4954
if (initialData && !isEmpty(initialData)) {
5055
try {
51-
jsonVal =
52-
typeof initialData[0] === "string" ?
53-
[
54-
JSON.parse(
55-
initialData[0]?.trim()?.length ? initialData[0] : "{}"
56-
),
57-
]
58-
: initialData;
56+
jsonVal = typeof initialData[0] === "string" ? [JSON.parse(initialData[0]?.trim()?.length ? initialData[0] : "{}")] : initialData;
5957
} catch (e) {
6058
jsonVal = [{}];
6159
}
6260
setJsonData(jsonVal);
6361
}
64-
setSaveJsonData(
65-
toStringify(isStringified, config?.isStringified) ?
66-
[JSON.stringify(jsonVal[0])]
67-
: jsonVal
68-
);
69-
trackEvent(APP_INITIALIZE_SUCCESS)
62+
setSaveJsonData(toStringify(isStringified, config?.isStringified) ? [JSON.stringify(jsonVal[0])] : jsonVal);
63+
trackEvent(APP_INITIALIZE_SUCCESS);
64+
const appLocation: string = getAppLocation(appSdk);
65+
const properties = {
66+
Stack: appSdk?.stack._data.api_key,
67+
Organization: appSdk?.currentUser.defaultOrganization,
68+
"App Location": appLocation,
69+
"User Id": get(appSdk, "stack._data.collaborators.0.uid", ""), // first uuid from collaborators
70+
};
71+
setErrorsMetaData(properties); // set global event data for errors
7072
})
7173
.catch((error) => {
74+
trackError(error);
7275
console.error(constants.appSdkError, error);
73-
trackEvent(APP_INITIALIZE_FAILURE)
76+
trackEvent(APP_INITIALIZE_FAILURE);
7477
});
7578
}, []);
7679

7780
const onChangeSave = (saveData: any) => {
78-
state.location?.CustomField?.field?.setData(
79-
toStringify(isStringified, state?.config?.isStringified) ?
80-
[JSON.stringify(saveData)]
81-
: [saveData]
82-
);
81+
state.location?.CustomField?.field?.setData(toStringify(isStringified, state?.config?.isStringified) ? [JSON.stringify(saveData)] : [saveData]);
8382
};
8483

8584
useEffect(() => {
8685
state?.location?.CustomField?.field?.setData(saveJsonData);
8786
}, [saveJsonData]);
8887

89-
return (
90-
<div className="layout-container">
91-
{state?.appSdkInitialized && (
92-
<JSONEditor onChange={onChangeSave} value={jsonData[0]} />
93-
)}
94-
</div>
95-
);
88+
return <div className="layout-container">{state?.appSdkInitialized && <JSONEditor onChange={onChangeSave} value={jsonData[0]} />}</div>;
9689
};
9790

9891
export default CustomField;

0 commit comments

Comments
 (0)