@scality/data-browser-library 1.0.0-preview.13 → 1.0.0-preview.16
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/dist/components/DataBrowserUI.d.ts +6 -14
- package/dist/components/DataBrowserUI.js +79 -55
- package/dist/components/Editor.d.ts +1 -1
- package/dist/components/Editor.js +3 -3
- package/dist/components/__tests__/BucketCreate.test.js +102 -102
- package/dist/components/__tests__/BucketDetails.test.js +122 -123
- package/dist/components/__tests__/BucketLifecycleFormPage.test.js +177 -177
- package/dist/components/__tests__/BucketLifecycleList.test.js +85 -85
- package/dist/components/__tests__/BucketList.test.js +175 -176
- package/dist/components/__tests__/BucketNotificationCreatePage.test.js +84 -84
- package/dist/components/__tests__/BucketOverview.test.js +257 -201
- package/dist/components/__tests__/BucketPolicyPage.test.js +62 -62
- package/dist/components/__tests__/BucketReplicationFormPage.test.js +542 -542
- package/dist/components/__tests__/BucketReplicationList.test.js +106 -106
- package/dist/components/__tests__/CreateFolderButton.test.js +56 -56
- package/dist/components/__tests__/DeleteBucketButton.test.js +62 -62
- package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +47 -47
- package/dist/components/__tests__/DeleteObjectButton.test.js +63 -63
- package/dist/components/__tests__/EmptyBucketButton.test.js +56 -56
- package/dist/components/__tests__/MetadataSearch.test.js +65 -65
- package/dist/components/__tests__/ObjectList.test.js +252 -251
- package/dist/components/__tests__/UploadButton.test.js +45 -45
- package/dist/components/buckets/BucketCreate.d.ts +2 -2
- package/dist/components/buckets/BucketCreate.js +41 -41
- package/dist/components/buckets/BucketDetails.d.ts +2 -2
- package/dist/components/buckets/BucketDetails.js +48 -36
- package/dist/components/buckets/BucketLifecycleFormPage.js +161 -160
- package/dist/components/buckets/BucketLifecycleList.d.ts +2 -2
- package/dist/components/buckets/BucketLifecycleList.js +46 -46
- package/dist/components/buckets/BucketList.d.ts +2 -2
- package/dist/components/buckets/BucketList.js +28 -27
- package/dist/components/buckets/BucketLocation.js +3 -3
- package/dist/components/buckets/BucketOverview.d.ts +1 -1
- package/dist/components/buckets/BucketOverview.js +64 -65
- package/dist/components/buckets/BucketPage.js +19 -11
- package/dist/components/buckets/BucketPolicyButton.js +2 -2
- package/dist/components/buckets/BucketPolicyPage.js +27 -25
- package/dist/components/buckets/BucketReplicationFormPage.js +133 -132
- package/dist/components/buckets/BucketReplicationList.d.ts +2 -2
- package/dist/components/buckets/BucketReplicationList.js +41 -41
- package/dist/components/buckets/BucketVersioning.js +11 -11
- package/dist/components/buckets/DeleteBucketButton.js +5 -5
- package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +2 -2
- package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
- package/dist/components/buckets/EmptyBucketButton.js +19 -19
- package/dist/components/buckets/EmptyBucketSummary.d.ts +1 -1
- package/dist/components/buckets/EmptyBucketSummary.js +1 -1
- package/dist/components/buckets/EmptyBucketSummaryList.js +22 -22
- package/dist/components/buckets/__tests__/BucketVersioning.test.js +45 -45
- package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +34 -33
- package/dist/components/buckets/notifications/EventsSection.js +144 -28
- package/dist/components/buckets/notifications/__tests__/events.test.d.ts +1 -0
- package/dist/components/buckets/notifications/__tests__/events.test.js +56 -0
- package/dist/components/buckets/notifications/events.d.ts +71 -7
- package/dist/components/buckets/notifications/events.js +98 -16
- package/dist/components/index.d.ts +24 -22
- package/dist/components/index.js +5 -3
- package/dist/components/layouts/ArrowNavigation.d.ts +1 -2
- package/dist/components/layouts/ArrowNavigation.js +3 -3
- package/dist/components/layouts/BrowserPageLayout.d.ts +2 -3
- package/dist/components/layouts/BrowserPageLayout.js +1 -1
- package/dist/components/objects/CreateFolderButton.d.ts +2 -2
- package/dist/components/objects/CreateFolderButton.js +9 -9
- package/dist/components/objects/DeleteObjectButton.d.ts +1 -1
- package/dist/components/objects/DeleteObjectButton.js +20 -20
- package/dist/components/objects/ObjectDetails/ObjectMetadata.d.ts +1 -1
- package/dist/components/objects/ObjectDetails/ObjectMetadata.js +56 -56
- package/dist/components/objects/ObjectDetails/ObjectSummary.d.ts +1 -1
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +39 -39
- package/dist/components/objects/ObjectDetails/ObjectTags.d.ts +1 -1
- package/dist/components/objects/ObjectDetails/ObjectTags.js +25 -25
- package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +119 -119
- package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +211 -211
- package/dist/components/objects/ObjectDetails/index.d.ts +1 -1
- package/dist/components/objects/ObjectDetails/index.js +30 -30
- package/dist/components/objects/ObjectList.d.ts +5 -5
- package/dist/components/objects/ObjectList.js +113 -112
- package/dist/components/objects/ObjectLock/EditRetentionButton.js +3 -3
- package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +14 -14
- package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +1 -1
- package/dist/components/objects/ObjectLock/ObjectLockSettings.js +29 -28
- package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +1 -1
- package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +6 -6
- package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +50 -50
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +77 -77
- package/dist/components/objects/ObjectPage.js +5 -4
- package/dist/components/objects/UploadButton.d.ts +3 -3
- package/dist/components/objects/UploadButton.js +5 -5
- package/dist/components/providers/DataBrowserProvider.d.ts +23 -12
- package/dist/components/providers/DataBrowserProvider.js +60 -38
- package/dist/components/providers/QueryProvider.d.ts +9 -0
- package/dist/components/providers/QueryProvider.js +22 -0
- package/dist/components/search/MetadataSearch.js +26 -25
- package/dist/components/search/SearchHints.js +1 -1
- package/dist/components/ui/ArrayFieldActions.js +4 -4
- package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +1 -1
- package/dist/components/ui/ConfirmDeleteRuleModal.js +1 -1
- package/dist/components/ui/DeleteObjectModalContent.d.ts +1 -1
- package/dist/components/ui/DeleteObjectModalContent.js +12 -12
- package/dist/components/ui/FilterFormSection.d.ts +2 -2
- package/dist/components/ui/FilterFormSection.js +29 -29
- package/dist/components/ui/Search.elements.d.ts +1 -1
- package/dist/components/ui/Search.elements.js +7 -7
- package/dist/components/ui/Table.elements.js +5 -5
- package/dist/config/factory.d.ts +23 -10
- package/dist/config/factory.js +22 -7
- package/dist/config/types.d.ts +20 -3
- package/dist/contexts/DataBrowserUICustomizationContext.d.ts +2 -2
- package/dist/hooks/__tests__/useISVBucketDetection.test.js +42 -42
- package/dist/hooks/__tests__/useIsBucketEmpty.test.js +25 -25
- package/dist/hooks/bucketConfiguration.d.ts +1 -1
- package/dist/hooks/bucketConfiguration.js +48 -48
- package/dist/hooks/bucketOperations.d.ts +1 -1
- package/dist/hooks/bucketOperations.js +6 -6
- package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.js +78 -78
- package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.js +78 -78
- package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.js +42 -42
- package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.js +61 -61
- package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +63 -63
- package/dist/hooks/factories/index.d.ts +4 -4
- package/dist/hooks/factories/useCreateS3InfiniteQueryHook.d.ts +2 -2
- package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +15 -12
- package/dist/hooks/factories/useCreateS3LoginHook.d.ts +2 -2
- package/dist/hooks/factories/useCreateS3MutationHook.d.ts +3 -3
- package/dist/hooks/factories/useCreateS3MutationHook.js +6 -1
- package/dist/hooks/factories/useCreateS3QueryHook.d.ts +2 -2
- package/dist/hooks/factories/useCreateS3QueryHook.js +8 -5
- package/dist/hooks/index.d.ts +16 -13
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/loginOperations.d.ts +1 -1
- package/dist/hooks/loginOperations.js +1 -1
- package/dist/hooks/objectOperations.d.ts +2 -2
- package/dist/hooks/objectOperations.js +49 -49
- package/dist/hooks/presignedOperations.d.ts +2 -2
- package/dist/hooks/presignedOperations.js +3 -3
- package/dist/hooks/useBatchObjectLegalHold.js +7 -4
- package/dist/hooks/useDataBrowserNavigate.d.ts +28 -0
- package/dist/hooks/useDataBrowserNavigate.js +24 -0
- package/dist/hooks/useDeleteBucketConfigRule.d.ts +2 -2
- package/dist/hooks/useDeleteBucketConfigRule.js +4 -4
- package/dist/hooks/useEmptyBucket.js +10 -10
- package/dist/hooks/useFeatures.d.ts +7 -0
- package/dist/hooks/useFeatures.js +8 -0
- package/dist/hooks/useISVBucketDetection.js +5 -5
- package/dist/hooks/useIsBucketEmpty.js +4 -4
- package/dist/hooks/useLoginMutation.d.ts +1 -1
- package/dist/hooks/useLoginMutation.js +1 -1
- package/dist/hooks/useS3Client.d.ts +6 -0
- package/dist/hooks/useS3Client.js +3 -2
- package/dist/hooks/useS3ConfigSwitch.d.ts +11 -0
- package/dist/hooks/useS3ConfigSwitch.js +37 -0
- package/dist/hooks/useSupportedNotificationEvents.d.ts +6 -0
- package/dist/hooks/useSupportedNotificationEvents.js +8 -0
- package/dist/index.d.ts +6 -6
- package/dist/test/msw/handlers/deleteBucket.d.ts +1 -1
- package/dist/test/msw/handlers/deleteBucket.js +20 -10
- package/dist/test/msw/handlers/getBucketAcl.d.ts +1 -1
- package/dist/test/msw/handlers/getBucketAcl.js +29 -17
- package/dist/test/msw/handlers/getBucketLocation.d.ts +1 -1
- package/dist/test/msw/handlers/getBucketLocation.js +29 -15
- package/dist/test/msw/handlers/getBucketPolicy.d.ts +1 -1
- package/dist/test/msw/handlers/getBucketPolicy.js +52 -32
- package/dist/test/msw/handlers/headObject.d.ts +1 -1
- package/dist/test/msw/handlers/headObject.js +31 -13
- package/dist/test/msw/handlers/listBuckets.d.ts +1 -1
- package/dist/test/msw/handlers/listBuckets.js +5 -3
- package/dist/test/msw/handlers/listObjectVersions.d.ts +1 -1
- package/dist/test/msw/handlers/listObjectVersions.js +38 -26
- package/dist/test/msw/handlers/listObjects.d.ts +1 -1
- package/dist/test/msw/handlers/listObjects.js +35 -23
- package/dist/test/msw/handlers/objectLegalHold.d.ts +1 -1
- package/dist/test/msw/handlers/objectLegalHold.js +31 -16
- package/dist/test/msw/handlers/objectRetention.d.ts +1 -1
- package/dist/test/msw/handlers/objectRetention.js +31 -17
- package/dist/test/msw/handlers/putBucketAcl.d.ts +1 -1
- package/dist/test/msw/handlers/putBucketAcl.js +29 -14
- package/dist/test/msw/handlers/putObject.d.ts +1 -1
- package/dist/test/msw/handlers/putObject.js +27 -12
- package/dist/test/msw/handlers.d.ts +3 -3
- package/dist/test/msw/handlers.js +72 -49
- package/dist/test/msw/index.d.ts +2 -2
- package/dist/test/msw/server.d.ts +1 -1
- package/dist/test/msw/server.js +1 -1
- package/dist/test/msw/utils.js +2 -2
- package/dist/test/setup.d.ts +1 -1
- package/dist/test/setup.js +19 -19
- package/dist/test/testUtils.d.ts +9 -15
- package/dist/test/testUtils.js +73 -91
- package/dist/test/utils/errorHandling.test.js +119 -119
- package/dist/types/index.d.ts +6 -31
- package/dist/utils/__tests__/s3ConfigIdentifier.test.d.ts +1 -0
- package/dist/utils/__tests__/s3ConfigIdentifier.test.js +429 -0
- package/dist/utils/constants.js +8 -8
- package/dist/utils/deletion/index.d.ts +2 -2
- package/dist/utils/deletion/messages.d.ts +1 -1
- package/dist/utils/deletion/messages.js +4 -4
- package/dist/utils/errorHandling.d.ts +3 -3
- package/dist/utils/errorHandling.js +6 -6
- package/dist/utils/hooks.js +8 -8
- package/dist/utils/index.d.ts +5 -4
- package/dist/utils/index.js +2 -0
- package/dist/utils/proxyMiddleware.d.ts +1 -1
- package/dist/utils/proxyMiddleware.js +6 -11
- package/dist/utils/s3Client.d.ts +2 -2
- package/dist/utils/s3Client.js +1 -1
- package/dist/utils/s3ConfigIdentifier.d.ts +68 -0
- package/dist/utils/s3ConfigIdentifier.js +55 -0
- package/dist/utils/s3RuleUtils.d.ts +5 -5
- package/dist/utils/s3RuleUtils.js +17 -17
- package/package.json +2 -2
- package/dist/utils/useFeatures.d.ts +0 -1
- package/dist/utils/useFeatures.js +0 -7
|
@@ -6,16 +6,16 @@ import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICusto
|
|
|
6
6
|
import { useGetBucketAcl, useGetBucketCors, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketVersioning } from "../../hooks/index.js";
|
|
7
7
|
import { isNotFoundError } from "../../utils/errorHandling.js";
|
|
8
8
|
import { EditRetentionButton } from "../objects/ObjectLock/EditRetentionButton.js";
|
|
9
|
-
import {
|
|
9
|
+
import { useDataBrowserConfig } from "../providers/DataBrowserProvider.js";
|
|
10
10
|
import { Body, Group, GroupContent, GroupName, GroupValues, Key, Row, Table, TableContainer, Value } from "../ui/Table.elements.js";
|
|
11
11
|
import { BucketLocation } from "./BucketLocation.js";
|
|
12
12
|
import { BucketPolicyButton } from "./BucketPolicyButton.js";
|
|
13
|
-
const ERROR_NO_SUCH_BUCKET_POLICY =
|
|
14
|
-
const DEFAULT_PUBLIC_ACL_URI =
|
|
15
|
-
const STATUS_PENDING =
|
|
16
|
-
const STATUS_ERROR =
|
|
17
|
-
const VERSIONING_STATUS_ENABLED =
|
|
18
|
-
const OBJECT_LOCK_ENABLED =
|
|
13
|
+
const ERROR_NO_SUCH_BUCKET_POLICY = 'NoSuchBucketPolicy';
|
|
14
|
+
const DEFAULT_PUBLIC_ACL_URI = 'http://acs.amazonaws.com/groups/global/AllUsers';
|
|
15
|
+
const STATUS_PENDING = 'pending';
|
|
16
|
+
const STATUS_ERROR = 'error';
|
|
17
|
+
const VERSIONING_STATUS_ENABLED = 'Enabled';
|
|
18
|
+
const OBJECT_LOCK_ENABLED = 'Enabled';
|
|
19
19
|
const ErrorField = ()=>/*#__PURE__*/ jsxs(Stack, {
|
|
20
20
|
direction: "horizontal",
|
|
21
21
|
children: [
|
|
@@ -57,8 +57,8 @@ const mergeFieldsWithExtras = (defaultFields, extraFieldConfigs, entityName, fie
|
|
|
57
57
|
];
|
|
58
58
|
};
|
|
59
59
|
const createNameField = (bucketName, nameField, renderName)=>({
|
|
60
|
-
id:
|
|
61
|
-
label:
|
|
60
|
+
id: 'name',
|
|
61
|
+
label: 'Name',
|
|
62
62
|
value: resolveFieldValue(bucketName, {
|
|
63
63
|
slotOverride: nameField,
|
|
64
64
|
renderOverride: renderName,
|
|
@@ -69,13 +69,13 @@ const createNameField = (bucketName, nameField, renderName)=>({
|
|
|
69
69
|
})
|
|
70
70
|
});
|
|
71
71
|
const createVersioningField = (bucketName, versioningData, versioningStatus, versioningField, renderVersioning)=>{
|
|
72
|
-
let defaultValue =
|
|
72
|
+
let defaultValue = 'Inactive';
|
|
73
73
|
if (versioningStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
|
|
74
74
|
else if (versioningStatus === STATUS_ERROR) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
|
|
75
|
-
else if (versioningData?.Status === VERSIONING_STATUS_ENABLED) defaultValue =
|
|
75
|
+
else if (versioningData?.Status === VERSIONING_STATUS_ENABLED) defaultValue = 'Active';
|
|
76
76
|
return {
|
|
77
|
-
id:
|
|
78
|
-
label:
|
|
77
|
+
id: 'versioning',
|
|
78
|
+
label: 'Versioning',
|
|
79
79
|
value: resolveFieldValue(bucketName, {
|
|
80
80
|
slotOverride: versioningField,
|
|
81
81
|
renderOverride: renderVersioning,
|
|
@@ -84,8 +84,8 @@ const createVersioningField = (bucketName, versioningData, versioningStatus, ver
|
|
|
84
84
|
};
|
|
85
85
|
};
|
|
86
86
|
const createLocationField = (bucketName, locationField, renderLocation)=>({
|
|
87
|
-
id:
|
|
88
|
-
label:
|
|
87
|
+
id: 'location',
|
|
88
|
+
label: 'Location',
|
|
89
89
|
value: resolveFieldValue(bucketName, {
|
|
90
90
|
slotOverride: locationField,
|
|
91
91
|
renderOverride: renderLocation,
|
|
@@ -96,10 +96,10 @@ const createLocationField = (bucketName, locationField, renderLocation)=>({
|
|
|
96
96
|
});
|
|
97
97
|
const createObjectLockField = (bucketName, objectLockStatus, isObjectLockNotConfigured, isObjectLockEnabled, objectLockField, renderObjectLock)=>{
|
|
98
98
|
let defaultValue;
|
|
99
|
-
defaultValue = objectLockStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : objectLockStatus !== STATUS_ERROR || isObjectLockNotConfigured ? isObjectLockEnabled ?
|
|
99
|
+
defaultValue = objectLockStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : objectLockStatus !== STATUS_ERROR || isObjectLockNotConfigured ? isObjectLockEnabled ? 'Enabled' : 'Disabled' : /*#__PURE__*/ jsx(ErrorField, {});
|
|
100
100
|
return {
|
|
101
|
-
id:
|
|
102
|
-
label:
|
|
101
|
+
id: 'objectLock',
|
|
102
|
+
label: 'Object-lock',
|
|
103
103
|
value: resolveFieldValue(bucketName, {
|
|
104
104
|
slotOverride: objectLockField,
|
|
105
105
|
renderOverride: renderObjectLock,
|
|
@@ -110,8 +110,8 @@ const createObjectLockField = (bucketName, objectLockStatus, isObjectLockNotConf
|
|
|
110
110
|
const createDefaultRetentionField = (bucketName, objectLockData, objectLockStatus, defaultRetentionField, renderDefaultRetention)=>{
|
|
111
111
|
const defaultValue = objectLockStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : getDefaultBucketRetention(objectLockData);
|
|
112
112
|
return {
|
|
113
|
-
id:
|
|
114
|
-
label:
|
|
113
|
+
id: 'defaultRetention',
|
|
114
|
+
label: 'Default Retention',
|
|
115
115
|
value: resolveFieldValue(bucketName, {
|
|
116
116
|
slotOverride: defaultRetentionField,
|
|
117
117
|
renderOverride: renderDefaultRetention,
|
|
@@ -123,13 +123,13 @@ const createDefaultRetentionField = (bucketName, objectLockData, objectLockStatu
|
|
|
123
123
|
};
|
|
124
124
|
};
|
|
125
125
|
const createOwnerField = (bucketName, aclData, aclStatus, ownerField, renderOwner)=>{
|
|
126
|
-
let defaultValue =
|
|
126
|
+
let defaultValue = 'N/A';
|
|
127
127
|
if (aclStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
|
|
128
128
|
else if (aclStatus === STATUS_ERROR) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
|
|
129
129
|
else if (aclData?.Owner?.DisplayName) defaultValue = aclData.Owner.DisplayName;
|
|
130
130
|
return {
|
|
131
|
-
id:
|
|
132
|
-
label:
|
|
131
|
+
id: 'owner',
|
|
132
|
+
label: 'Owner',
|
|
133
133
|
value: resolveFieldValue(bucketName, {
|
|
134
134
|
slotOverride: ownerField,
|
|
135
135
|
renderOverride: renderOwner,
|
|
@@ -138,16 +138,16 @@ const createOwnerField = (bucketName, aclData, aclStatus, ownerField, renderOwne
|
|
|
138
138
|
};
|
|
139
139
|
};
|
|
140
140
|
const createAclField = (bucketName, aclData, aclStatus, aclField, renderAcl)=>{
|
|
141
|
-
let defaultValue =
|
|
141
|
+
let defaultValue = 'N/A';
|
|
142
142
|
if (aclStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
|
|
143
143
|
else if (aclStatus === STATUS_ERROR) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
|
|
144
144
|
else if (aclData?.Grants?.length) {
|
|
145
145
|
const count = aclData.Grants.length;
|
|
146
|
-
defaultValue = `${count} Grantee${1 !== count ?
|
|
146
|
+
defaultValue = `${count} Grantee${1 !== count ? 's' : ''}`;
|
|
147
147
|
}
|
|
148
148
|
return {
|
|
149
|
-
id:
|
|
150
|
-
label:
|
|
149
|
+
id: 'acl',
|
|
150
|
+
label: 'ACL',
|
|
151
151
|
value: resolveFieldValue(bucketName, {
|
|
152
152
|
slotOverride: aclField,
|
|
153
153
|
renderOverride: renderAcl,
|
|
@@ -156,12 +156,12 @@ const createAclField = (bucketName, aclData, aclStatus, aclField, renderAcl)=>{
|
|
|
156
156
|
};
|
|
157
157
|
};
|
|
158
158
|
const createCorsField = (bucketName, corsData, corsStatus, corsField, renderCors)=>{
|
|
159
|
-
let defaultValue =
|
|
159
|
+
let defaultValue = 'No';
|
|
160
160
|
if (corsStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
|
|
161
|
-
else if (corsData?.CORSRules && corsData.CORSRules.length > 0) defaultValue =
|
|
161
|
+
else if (corsData?.CORSRules && corsData.CORSRules.length > 0) defaultValue = 'Yes';
|
|
162
162
|
return {
|
|
163
|
-
id:
|
|
164
|
-
label:
|
|
163
|
+
id: 'cors',
|
|
164
|
+
label: 'CORS',
|
|
165
165
|
value: resolveFieldValue(bucketName, {
|
|
166
166
|
slotOverride: corsField,
|
|
167
167
|
renderOverride: renderCors,
|
|
@@ -170,10 +170,10 @@ const createCorsField = (bucketName, corsData, corsStatus, corsField, renderCors
|
|
|
170
170
|
};
|
|
171
171
|
};
|
|
172
172
|
const createPublicField = (bucketName, aclStatus, isPublic, publicField, renderPublic)=>{
|
|
173
|
-
const defaultValue = aclStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : isPublic ?
|
|
173
|
+
const defaultValue = aclStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : isPublic ? 'Yes' : 'No';
|
|
174
174
|
return {
|
|
175
|
-
id:
|
|
176
|
-
label:
|
|
175
|
+
id: 'public',
|
|
176
|
+
label: 'Public',
|
|
177
177
|
value: resolveFieldValue(bucketName, {
|
|
178
178
|
slotOverride: publicField,
|
|
179
179
|
renderOverride: renderPublic,
|
|
@@ -194,7 +194,7 @@ const createBucketPolicyField = (bucketName, policyData, policyStatus, policyErr
|
|
|
194
194
|
gap: spacing.r8,
|
|
195
195
|
children: [
|
|
196
196
|
/*#__PURE__*/ jsx("span", {
|
|
197
|
-
children: hasPolicy ?
|
|
197
|
+
children: hasPolicy ? 'Configured' : 'Not configured'
|
|
198
198
|
}),
|
|
199
199
|
/*#__PURE__*/ jsx(BucketPolicyButton, {
|
|
200
200
|
hasPolicy: hasPolicy,
|
|
@@ -205,8 +205,8 @@ const createBucketPolicyField = (bucketName, policyData, policyStatus, policyErr
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
return {
|
|
208
|
-
id:
|
|
209
|
-
label:
|
|
208
|
+
id: 'bucketPolicy',
|
|
209
|
+
label: 'Bucket Policy',
|
|
210
210
|
value: resolveFieldValue(bucketName, {
|
|
211
211
|
slotOverride: bucketPolicyField,
|
|
212
212
|
renderOverride: renderBucketPolicy ? (name)=>renderBucketPolicy(name, onEditPolicy) : void 0,
|
|
@@ -217,21 +217,21 @@ const createBucketPolicyField = (bucketName, policyData, policyStatus, policyErr
|
|
|
217
217
|
const BucketOverviewContext = /*#__PURE__*/ createContext(null);
|
|
218
218
|
function useBucketOverviewContext() {
|
|
219
219
|
const context = useContext(BucketOverviewContext);
|
|
220
|
-
if (!context) throw new Error(
|
|
220
|
+
if (!context) throw new Error('BucketOverview components must be used within BucketOverview');
|
|
221
221
|
return context;
|
|
222
222
|
}
|
|
223
223
|
const getDefaultBucketRetention = (objectLockData)=>{
|
|
224
224
|
const defaultRetention = objectLockData?.ObjectLockConfiguration?.Rule?.DefaultRetention;
|
|
225
|
-
if (!defaultRetention || !defaultRetention.Days && !defaultRetention.Years) return
|
|
226
|
-
let retentionPeriod =
|
|
225
|
+
if (!defaultRetention || !defaultRetention.Days && !defaultRetention.Years) return 'Inactive';
|
|
226
|
+
let retentionPeriod = '';
|
|
227
227
|
if (defaultRetention.Days) {
|
|
228
|
-
const unit = 1 === defaultRetention.Days ?
|
|
228
|
+
const unit = 1 === defaultRetention.Days ? 'day' : 'days';
|
|
229
229
|
retentionPeriod = `${defaultRetention.Days} ${unit}`;
|
|
230
230
|
} else if (defaultRetention.Years) {
|
|
231
|
-
const unit = 1 === defaultRetention.Years ?
|
|
231
|
+
const unit = 1 === defaultRetention.Years ? 'year' : 'years';
|
|
232
232
|
retentionPeriod = `${defaultRetention.Years} ${unit}`;
|
|
233
233
|
}
|
|
234
|
-
const mode = defaultRetention.Mode ||
|
|
234
|
+
const mode = defaultRetention.Mode || 'GOVERNANCE';
|
|
235
235
|
const capitalizedMode = mode.charAt(0).toUpperCase() + mode.slice(1).toLowerCase();
|
|
236
236
|
return `${capitalizedMode} - ${retentionPeriod}`;
|
|
237
237
|
};
|
|
@@ -276,7 +276,7 @@ const Actions = /*#__PURE__*/ memo(({ onEmptyBucket, onDeleteBucket, renderEmpty
|
|
|
276
276
|
]
|
|
277
277
|
});
|
|
278
278
|
});
|
|
279
|
-
Actions.displayName =
|
|
279
|
+
Actions.displayName = 'BucketOverview.Actions';
|
|
280
280
|
const Section = /*#__PURE__*/ memo(({ title, children })=>/*#__PURE__*/ jsxs(Group, {
|
|
281
281
|
children: [
|
|
282
282
|
/*#__PURE__*/ jsx(GroupName, {
|
|
@@ -287,12 +287,12 @@ const Section = /*#__PURE__*/ memo(({ title, children })=>/*#__PURE__*/ jsxs(Gro
|
|
|
287
287
|
})
|
|
288
288
|
]
|
|
289
289
|
}));
|
|
290
|
-
Section.displayName =
|
|
291
|
-
const Field = /*#__PURE__*/ memo(({ label, value, loading, error, errorMessage =
|
|
290
|
+
Section.displayName = 'BucketOverview.Section';
|
|
291
|
+
const Field = /*#__PURE__*/ memo(({ label, value, loading, error, errorMessage = 'Error', children, actions })=>{
|
|
292
292
|
const renderValue = ()=>{
|
|
293
293
|
if (loading) return /*#__PURE__*/ jsx(Loader, {});
|
|
294
294
|
if (error) return errorMessage;
|
|
295
|
-
return children || value ||
|
|
295
|
+
return children || value || 'N/A';
|
|
296
296
|
};
|
|
297
297
|
return /*#__PURE__*/ jsxs(Row, {
|
|
298
298
|
children: [
|
|
@@ -310,13 +310,13 @@ const Field = /*#__PURE__*/ memo(({ label, value, loading, error, errorMessage =
|
|
|
310
310
|
]
|
|
311
311
|
});
|
|
312
312
|
});
|
|
313
|
-
Field.displayName =
|
|
313
|
+
Field.displayName = 'BucketOverview.Field';
|
|
314
314
|
const Sections = /*#__PURE__*/ memo(({ children })=>/*#__PURE__*/ jsx(Table, {
|
|
315
315
|
children: /*#__PURE__*/ jsx(Body, {
|
|
316
316
|
children: children
|
|
317
317
|
})
|
|
318
318
|
}));
|
|
319
|
-
Sections.displayName =
|
|
319
|
+
Sections.displayName = 'BucketOverview.Sections';
|
|
320
320
|
const GeneralSection = /*#__PURE__*/ memo(({ nameField, versioningField, locationField, renderName, renderVersioning, renderLocation })=>{
|
|
321
321
|
const { bucketName } = useBucketOverviewContext();
|
|
322
322
|
const { extraBucketOverviewGeneral } = useDataBrowserUICustomization();
|
|
@@ -330,9 +330,9 @@ const GeneralSection = /*#__PURE__*/ memo(({ nameField, versioningField, locatio
|
|
|
330
330
|
location: createLocationField(bucketName, locationField, renderLocation)
|
|
331
331
|
};
|
|
332
332
|
return mergeFieldsWithExtras(defaultFields, extraBucketOverviewGeneral, bucketName, [
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
333
|
+
'name',
|
|
334
|
+
'versioning',
|
|
335
|
+
'location'
|
|
336
336
|
]);
|
|
337
337
|
}, [
|
|
338
338
|
bucketName,
|
|
@@ -355,7 +355,7 @@ const GeneralSection = /*#__PURE__*/ memo(({ nameField, versioningField, locatio
|
|
|
355
355
|
}, field.id))
|
|
356
356
|
});
|
|
357
357
|
});
|
|
358
|
-
GeneralSection.displayName =
|
|
358
|
+
GeneralSection.displayName = 'BucketOverview.GeneralSection';
|
|
359
359
|
const DataProtectionSection = /*#__PURE__*/ memo(({ objectLockField, defaultRetentionField, renderObjectLock, renderDefaultRetention })=>{
|
|
360
360
|
const { bucketName } = useBucketOverviewContext();
|
|
361
361
|
const { extraBucketOverviewDataProtection } = useDataBrowserUICustomization();
|
|
@@ -370,10 +370,10 @@ const DataProtectionSection = /*#__PURE__*/ memo(({ objectLockField, defaultRete
|
|
|
370
370
|
defaultRetention: createDefaultRetentionField(bucketName, objectLockData, objectLockStatus, defaultRetentionField, renderDefaultRetention)
|
|
371
371
|
};
|
|
372
372
|
const fieldOrder = isObjectLockEnabled ? [
|
|
373
|
-
|
|
374
|
-
|
|
373
|
+
'objectLock',
|
|
374
|
+
'defaultRetention'
|
|
375
375
|
] : [
|
|
376
|
-
|
|
376
|
+
'objectLock'
|
|
377
377
|
];
|
|
378
378
|
return mergeFieldsWithExtras(defaultFields, extraBucketOverviewDataProtection, bucketName, fieldOrder);
|
|
379
379
|
}, [
|
|
@@ -397,12 +397,11 @@ const DataProtectionSection = /*#__PURE__*/ memo(({ objectLockField, defaultRete
|
|
|
397
397
|
}, field.id))
|
|
398
398
|
});
|
|
399
399
|
});
|
|
400
|
-
DataProtectionSection.displayName =
|
|
400
|
+
DataProtectionSection.displayName = 'BucketOverview.DataProtectionSection';
|
|
401
401
|
const PermissionsSection = /*#__PURE__*/ memo(({ onEditPolicy, ownerField, aclField, corsField, publicField, bucketPolicyField, renderOwner, renderAcl, renderCors, renderPublic, renderBucketPolicy })=>{
|
|
402
402
|
const { bucketName } = useBucketOverviewContext();
|
|
403
403
|
const { extraBucketOverviewPermissions } = useDataBrowserUICustomization();
|
|
404
|
-
const
|
|
405
|
-
const config = getS3Config();
|
|
404
|
+
const config = useDataBrowserConfig();
|
|
406
405
|
const { data: aclData, status: aclStatus } = useGetBucketAcl({
|
|
407
406
|
Bucket: bucketName
|
|
408
407
|
});
|
|
@@ -425,11 +424,11 @@ const PermissionsSection = /*#__PURE__*/ memo(({ onEditPolicy, ownerField, aclFi
|
|
|
425
424
|
bucketPolicy: createBucketPolicyField(bucketName, policyData, policyStatus, policyError, onEditPolicy, bucketPolicyField, renderBucketPolicy)
|
|
426
425
|
};
|
|
427
426
|
return mergeFieldsWithExtras(defaultFields, extraBucketOverviewPermissions, bucketName, [
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
427
|
+
'owner',
|
|
428
|
+
'acl',
|
|
429
|
+
'cors',
|
|
430
|
+
'public',
|
|
431
|
+
'bucketPolicy'
|
|
433
432
|
]);
|
|
434
433
|
}, [
|
|
435
434
|
bucketName,
|
|
@@ -463,7 +462,7 @@ const PermissionsSection = /*#__PURE__*/ memo(({ onEditPolicy, ownerField, aclFi
|
|
|
463
462
|
}, field.id))
|
|
464
463
|
});
|
|
465
464
|
});
|
|
466
|
-
PermissionsSection.displayName =
|
|
465
|
+
PermissionsSection.displayName = 'BucketOverview.PermissionsSection';
|
|
467
466
|
const BucketOverview = BucketOverviewRoot;
|
|
468
467
|
BucketOverview.Actions = Actions;
|
|
469
468
|
BucketOverview.Sections = Sections;
|
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useBuckets } from "../../hooks/index.js";
|
|
3
3
|
import { TextBadge } from "@scality/core-ui";
|
|
4
|
-
import {
|
|
4
|
+
import { useParams } from "react-router";
|
|
5
5
|
import { BrowserPageLayout } from "../layouts/BrowserPageLayout.js";
|
|
6
6
|
import { BucketDetails } from "./BucketDetails.js";
|
|
7
7
|
import { BucketList } from "./BucketList.js";
|
|
8
|
+
import { useDataBrowserNavigate } from "../../hooks/useDataBrowserNavigate.js";
|
|
9
|
+
import { useEffect } from "react";
|
|
8
10
|
const BucketPage_BucketPage = ()=>{
|
|
9
11
|
const { data, status } = useBuckets();
|
|
10
12
|
const { bucketName } = useParams();
|
|
11
|
-
const navigate =
|
|
13
|
+
const navigate = useDataBrowserNavigate();
|
|
12
14
|
const buckets = data?.Buckets || [];
|
|
13
15
|
const numInstance = buckets.length;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
useEffect(()=>{
|
|
17
|
+
if (!bucketName && buckets.length > 0 && 'success' === status) {
|
|
18
|
+
const firstBucketName = buckets[0]?.Name;
|
|
19
|
+
if (firstBucketName) navigate(`/buckets/${firstBucketName}`, {
|
|
20
|
+
replace: true
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}, [
|
|
24
|
+
bucketName,
|
|
25
|
+
buckets.length,
|
|
26
|
+
status,
|
|
27
|
+
navigate
|
|
28
|
+
]);
|
|
21
29
|
return /*#__PURE__*/ jsx(BrowserPageLayout, {
|
|
22
30
|
title: /*#__PURE__*/ jsxs(Fragment, {
|
|
23
31
|
children: [
|
|
24
|
-
|
|
32
|
+
'All Buckets',
|
|
25
33
|
/*#__PURE__*/ jsx(TextBadge, {
|
|
26
34
|
text: numInstance.toString(),
|
|
27
35
|
variant: "infoPrimary"
|
|
@@ -30,7 +38,7 @@ const BucketPage_BucketPage = ()=>{
|
|
|
30
38
|
}),
|
|
31
39
|
leftPanel: /*#__PURE__*/ jsx(BucketList, {
|
|
32
40
|
buckets: buckets,
|
|
33
|
-
bucketStatus:
|
|
41
|
+
bucketStatus: 'pending' === status ? 'loading' : 'error' === status ? 'error' : 'success',
|
|
34
42
|
selectedBucketName: bucketName,
|
|
35
43
|
onBucketSelect: (bucketName)=>navigate(`/buckets/${bucketName}`),
|
|
36
44
|
onCreateBucket: ()=>navigate("/buckets/-/create"),
|
|
@@ -8,9 +8,9 @@ const BucketPolicyButton = ({ hasPolicy, isLoading, onEdit })=>/*#__PURE__*/ jsx
|
|
|
8
8
|
children: /*#__PURE__*/ jsx(Button, {
|
|
9
9
|
isLoading: isLoading,
|
|
10
10
|
variant: "outline",
|
|
11
|
-
label: hasPolicy ?
|
|
11
|
+
label: hasPolicy ? 'Edit' : 'Create',
|
|
12
12
|
icon: /*#__PURE__*/ jsx(Icon, {
|
|
13
|
-
name: hasPolicy ?
|
|
13
|
+
name: hasPolicy ? 'Pen' : 'Create-add'
|
|
14
14
|
}),
|
|
15
15
|
onClick: onEdit
|
|
16
16
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { useParams } from "react-router";
|
|
4
|
+
import { useDataBrowserNavigate } from "../../hooks/useDataBrowserNavigate.js";
|
|
4
5
|
import { Banner, Form, FormGroup, FormSection, Icon, Loader, Stack, Text } from "@scality/core-ui";
|
|
5
6
|
import { Box, Button, CopyButton } from "@scality/core-ui/dist/next";
|
|
6
7
|
import { Controller, useForm } from "react-hook-form";
|
|
@@ -21,20 +22,20 @@ const getDefaultPolicyTemplate = (bucketName)=>`{
|
|
|
21
22
|
}`;
|
|
22
23
|
const BucketPolicyPage = ()=>{
|
|
23
24
|
const { bucketName } = useParams();
|
|
24
|
-
const navigate =
|
|
25
|
+
const navigate = useDataBrowserNavigate();
|
|
25
26
|
const { mutate: savePolicy, isPending: isSaving } = useSetBucketPolicy();
|
|
26
27
|
const { isISVManaged, isvApplication, isLoading: isISVLoading } = useISVBucketStatus(bucketName);
|
|
27
28
|
const { control, handleSubmit, watch, setValue, setError, formState: { isDirty, isValid, errors } } = useForm({
|
|
28
|
-
mode:
|
|
29
|
+
mode: 'onChange',
|
|
29
30
|
defaultValues: {
|
|
30
|
-
policyDocument:
|
|
31
|
+
policyDocument: ''
|
|
31
32
|
}
|
|
32
33
|
});
|
|
33
|
-
const policyDocument = watch(
|
|
34
|
+
const policyDocument = watch('policyDocument');
|
|
34
35
|
const { data: policyData, status: policyStatus, error: policyError } = useGetBucketPolicy({
|
|
35
36
|
Bucket: bucketName
|
|
36
37
|
});
|
|
37
|
-
const isCreateMode = useMemo(()=>policyError && (
|
|
38
|
+
const isCreateMode = useMemo(()=>policyError && ('NoSuchBucketPolicy' === policyError.name || policyError.message?.includes('does not exist')), [
|
|
38
39
|
policyError
|
|
39
40
|
]);
|
|
40
41
|
const initialPolicy = useMemo(()=>{
|
|
@@ -51,19 +52,20 @@ const BucketPolicyPage = ()=>{
|
|
|
51
52
|
bucketName
|
|
52
53
|
]);
|
|
53
54
|
useEffect(()=>{
|
|
54
|
-
setValue(
|
|
55
|
+
setValue('policyDocument', initialPolicy);
|
|
55
56
|
}, [
|
|
56
57
|
initialPolicy,
|
|
57
58
|
setValue
|
|
58
59
|
]);
|
|
59
60
|
const handleBeforeMount = useCallback((monaco)=>{
|
|
60
|
-
monaco.languages.json
|
|
61
|
+
const jsonLanguage = monaco.languages.json;
|
|
62
|
+
if (jsonLanguage?.jsonDefaults) jsonLanguage.jsonDefaults.setDiagnosticsOptions({
|
|
61
63
|
validate: true,
|
|
62
64
|
schemas: [
|
|
63
65
|
{
|
|
64
|
-
uri:
|
|
66
|
+
uri: 'http://myserver/bucket-policy-schema.json',
|
|
65
67
|
fileMatch: [
|
|
66
|
-
|
|
68
|
+
'*'
|
|
67
69
|
],
|
|
68
70
|
schema: bucketPolicySchema
|
|
69
71
|
}
|
|
@@ -80,9 +82,9 @@ const BucketPolicyPage = ()=>{
|
|
|
80
82
|
try {
|
|
81
83
|
JSON.parse(data.policyDocument);
|
|
82
84
|
} catch (error) {
|
|
83
|
-
const errorMessage =
|
|
84
|
-
setError(
|
|
85
|
-
type:
|
|
85
|
+
const errorMessage = 'Invalid JSON format';
|
|
86
|
+
setError('policyDocument', {
|
|
87
|
+
type: 'custom',
|
|
86
88
|
message: errorMessage
|
|
87
89
|
});
|
|
88
90
|
return;
|
|
@@ -96,9 +98,9 @@ const BucketPolicyPage = ()=>{
|
|
|
96
98
|
navigate(`/buckets/${bucketName}`);
|
|
97
99
|
},
|
|
98
100
|
onError: (error)=>{
|
|
99
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
100
|
-
setError(
|
|
101
|
-
type:
|
|
101
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to save policy';
|
|
102
|
+
setError('policyDocument', {
|
|
103
|
+
type: 'custom',
|
|
102
104
|
message: `Save policy error: ${errorMessage}`
|
|
103
105
|
});
|
|
104
106
|
}
|
|
@@ -109,7 +111,7 @@ const BucketPolicyPage = ()=>{
|
|
|
109
111
|
navigate,
|
|
110
112
|
setError
|
|
111
113
|
]);
|
|
112
|
-
if (
|
|
114
|
+
if ('pending' === policyStatus || isISVLoading) return /*#__PURE__*/ jsx(Loader, {
|
|
113
115
|
centered: true,
|
|
114
116
|
size: "massive",
|
|
115
117
|
children: /*#__PURE__*/ jsx(Fragment, {
|
|
@@ -119,10 +121,10 @@ const BucketPolicyPage = ()=>{
|
|
|
119
121
|
return /*#__PURE__*/ jsx(Form, {
|
|
120
122
|
onSubmit: handleSubmit(onSubmit),
|
|
121
123
|
layout: {
|
|
122
|
-
kind:
|
|
123
|
-
title: `Bucket Policy ${isCreateMode ?
|
|
124
|
+
kind: 'page',
|
|
125
|
+
title: `Bucket Policy ${isCreateMode ? 'Creation' : 'Edition'}`
|
|
124
126
|
},
|
|
125
|
-
requireMode: isCreateMode ?
|
|
127
|
+
requireMode: isCreateMode ? 'partial' : 'all',
|
|
126
128
|
rightActions: /*#__PURE__*/ jsxs(Stack, {
|
|
127
129
|
gap: "r16",
|
|
128
130
|
children: [
|
|
@@ -158,16 +160,16 @@ const BucketPolicyPage = ()=>{
|
|
|
158
160
|
children: "Warning:"
|
|
159
161
|
}),
|
|
160
162
|
" This bucket is managed by",
|
|
161
|
-
|
|
163
|
+
' ',
|
|
162
164
|
isvApplication,
|
|
163
165
|
". Adding a Bucket Policy may conflict with IAM policies created by ",
|
|
164
166
|
isvApplication,
|
|
165
167
|
", potentially disrupting",
|
|
166
|
-
|
|
168
|
+
' ',
|
|
167
169
|
isvApplication,
|
|
168
170
|
" operations (e.g., backup cleanup). Ensure your Bucket Policy does not deny actions that ",
|
|
169
171
|
isvApplication,
|
|
170
|
-
|
|
172
|
+
' ',
|
|
171
173
|
"requires."
|
|
172
174
|
]
|
|
173
175
|
})
|
|
@@ -197,13 +199,13 @@ const BucketPolicyPage = ()=>{
|
|
|
197
199
|
control: control,
|
|
198
200
|
name: "policyDocument",
|
|
199
201
|
rules: {
|
|
200
|
-
required:
|
|
202
|
+
required: 'The policy document is required',
|
|
201
203
|
validate: (value)=>{
|
|
202
204
|
try {
|
|
203
205
|
JSON.parse(value);
|
|
204
206
|
return true;
|
|
205
207
|
} catch {
|
|
206
|
-
return
|
|
208
|
+
return 'Invalid JSON format';
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
},
|