@redacto.io/consent-sdk-react 10.0.0-beta.8 → 10.0.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/NOTICE.md +17 -0
- package/README.md +141 -3
- package/dist/chunk-FEGTW73Q.mjs +182 -0
- package/dist/{chunk-6A665OPR.mjs → chunk-OGL6OC3A.mjs} +4 -0
- package/dist/fonts.d.mts +35 -0
- package/dist/fonts.d.ts +35 -0
- package/dist/fonts.js +183 -0
- package/dist/fonts.mjs +10 -0
- package/dist/index.d.mts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +881 -416
- package/dist/index.mjs +502 -253
- package/dist/montserrat.generated-NBTECYRW.mjs +22 -0
- package/dist/privacy-center.d.mts +51 -11
- package/dist/privacy-center.d.ts +51 -11
- package/dist/privacy-center.js +252 -136
- package/dist/privacy-center.mjs +251 -137
- package/dist/types-6Aj7faxL.d.mts +57 -0
- package/dist/types-6Aj7faxL.d.ts +57 -0
- package/package.json +10 -1
- package/.turbo/turbo-build.log +0 -22
- package/.turbo/turbo-test.log +0 -203
- package/.turbo/turbo-verify-pack.log +0 -6
- package/CHANGELOG.md +0 -343
- package/src/RedactoNoticeAssisted/RedactoNoticeAssisted.test.tsx +0 -934
- package/src/RedactoNoticeAssisted/RedactoNoticeAssisted.tsx +0 -2160
- package/src/RedactoNoticeAssisted/api/index.ts +0 -275
- package/src/RedactoNoticeAssisted/api/types.ts +0 -182
- package/src/RedactoNoticeAssisted/assets/redacto-logo.png +0 -0
- package/src/RedactoNoticeAssisted/constants.ts +0 -51
- package/src/RedactoNoticeAssisted/i18n.ts +0 -1408
- package/src/RedactoNoticeAssisted/index.ts +0 -1
- package/src/RedactoNoticeAssisted/injectStyles.ts +0 -251
- package/src/RedactoNoticeAssisted/styles.ts +0 -730
- package/src/RedactoNoticeAssisted/types.ts +0 -26
- package/src/RedactoNoticeAssisted/useMediaQuery.ts +0 -36
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +0 -1152
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +0 -4258
- package/src/RedactoNoticeConsent/api/index.ts +0 -512
- package/src/RedactoNoticeConsent/api/types.ts +0 -469
- package/src/RedactoNoticeConsent/assets/redacto-logo.png +0 -0
- package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +0 -602
- package/src/RedactoNoticeConsent/components/index.ts +0 -7
- package/src/RedactoNoticeConsent/constants.ts +0 -20
- package/src/RedactoNoticeConsent/index.ts +0 -1
- package/src/RedactoNoticeConsent/injectStyles.ts +0 -151
- package/src/RedactoNoticeConsent/language-codes.ts +0 -95
- package/src/RedactoNoticeConsent/services/storage.ts +0 -122
- package/src/RedactoNoticeConsent/styles.ts +0 -409
- package/src/RedactoNoticeConsent/types.ts +0 -54
- package/src/RedactoNoticeConsent/useMediaQuery.ts +0 -36
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.test.tsx +0 -369
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -687
- package/src/RedactoNoticeConsentInline/api/index.ts +0 -106
- package/src/RedactoNoticeConsentInline/api/types.ts +0 -183
- package/src/RedactoNoticeConsentInline/assets/redacto-logo.png +0 -0
- package/src/RedactoNoticeConsentInline/index.ts +0 -1
- package/src/RedactoNoticeConsentInline/injectStyles.ts +0 -40
- package/src/RedactoNoticeConsentInline/styles.ts +0 -397
- package/src/RedactoNoticeConsentInline/types.ts +0 -51
- package/src/RedactoNoticeConsentInline/useMediaQuery.ts +0 -36
- package/src/RedactoPrivacyCenter/NotificationsPage.tsx +0 -182
- package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +0 -19
- package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +0 -156
- package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +0 -17
- package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +0 -79
- package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +0 -36
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +0 -177
- package/src/RedactoPrivacyCenter/PrivacyCenterMyReceiptPage.tsx +0 -26
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +0 -58
- package/src/RedactoPrivacyCenter/api/actions.ts +0 -695
- package/src/RedactoPrivacyCenter/api/client.ts +0 -911
- package/src/RedactoPrivacyCenter/api/config.ts +0 -33
- package/src/RedactoPrivacyCenter/api/fetcher.ts +0 -260
- package/src/RedactoPrivacyCenter/api/orgBranding.ts +0 -16
- package/src/RedactoPrivacyCenter/api/upload.ts +0 -93
- package/src/RedactoPrivacyCenter/assets/character.svg +0 -31
- package/src/RedactoPrivacyCenter/assets/fileAttach.svg +0 -9
- package/src/RedactoPrivacyCenter/assets/greenTick.svg +0 -4
- package/src/RedactoPrivacyCenter/assets/mail.svg +0 -3
- package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +0 -9
- package/src/RedactoPrivacyCenter/assets/sentImg.svg +0 -9
- package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +0 -9
- package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +0 -252
- package/src/RedactoPrivacyCenter/components/ActivityTimeline.tsx +0 -239
- package/src/RedactoPrivacyCenter/components/AppShell/AppShell.tsx +0 -268
- package/src/RedactoPrivacyCenter/components/AppShell/MobileNavDrawer.tsx +0 -218
- package/src/RedactoPrivacyCenter/components/AppShell/NavItem.tsx +0 -205
- package/src/RedactoPrivacyCenter/components/AppShell/PageHeader.tsx +0 -107
- package/src/RedactoPrivacyCenter/components/AppShell/SidebarNav.tsx +0 -100
- package/src/RedactoPrivacyCenter/components/AppShell/TopAppBar.tsx +0 -339
- package/src/RedactoPrivacyCenter/components/AppShell/UserMenu.tsx +0 -245
- package/src/RedactoPrivacyCenter/components/AppShell/hooks/useIsTruncated.ts +0 -28
- package/src/RedactoPrivacyCenter/components/AppShell/hooks/useKeyboardShortcut.ts +0 -40
- package/src/RedactoPrivacyCenter/components/AppShell/hooks/useSidebarState.ts +0 -62
- package/src/RedactoPrivacyCenter/components/AppShell/usePrivacyCenterExitAction.ts +0 -36
- package/src/RedactoPrivacyCenter/components/CaseDetails/AttachmentCard.tsx +0 -38
- package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +0 -31
- package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +0 -79
- package/src/RedactoPrivacyCenter/components/CaseDetails/FinalizedTooltipWrapper.tsx +0 -25
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +0 -96
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +0 -97
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +0 -156
- package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +0 -171
- package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +0 -23
- package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +0 -116
- package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +0 -43
- package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +0 -71
- package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +0 -469
- package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +0 -426
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +0 -1206
- package/src/RedactoPrivacyCenter/components/ConsentManagerCells.tsx +0 -218
- package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +0 -138
- package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +0 -7
- package/src/RedactoPrivacyCenter/components/ConsentManagerExpandedRow.tsx +0 -179
- package/src/RedactoPrivacyCenter/components/ConsentManagerGroupedColumns.tsx +0 -318
- package/src/RedactoPrivacyCenter/components/ConsentManagerProductCards.tsx +0 -333
- package/src/RedactoPrivacyCenter/components/ConsentManagerPurposeCards.tsx +0 -102
- package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +0 -159
- package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +0 -189
- package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +0 -118
- package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +0 -66
- package/src/RedactoPrivacyCenter/components/DataRequest/ProductSelectionSection.tsx +0 -222
- package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +0 -331
- package/src/RedactoPrivacyCenter/components/Form.tsx +0 -1796
- package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +0 -174
- package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +0 -337
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +0 -334
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationBell.tsx +0 -173
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationCTAButton.tsx +0 -18
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationDropdown.tsx +0 -54
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationDropdownItem.tsx +0 -57
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationIcon.tsx +0 -19
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationListPanel.tsx +0 -92
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationRefreshButton.tsx +0 -20
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationRow.tsx +0 -58
- package/src/RedactoPrivacyCenter/components/NotificationHub/StatCard.tsx +0 -10
- package/src/RedactoPrivacyCenter/components/NotificationHub/useNotificationActions.ts +0 -112
- package/src/RedactoPrivacyCenter/components/NotificationHub/utils.ts +0 -27
- package/src/RedactoPrivacyCenter/components/Pagination.tsx +0 -140
- package/src/RedactoPrivacyCenter/components/PrivacyCenterEmptyState.tsx +0 -76
- package/src/RedactoPrivacyCenter/components/PrivacyCenterSessionExpired.tsx +0 -97
- package/src/RedactoPrivacyCenter/components/ReceiptsList.tsx +0 -454
- package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +0 -270
- package/src/RedactoPrivacyCenter/components/SelectUser.tsx +0 -95
- package/src/RedactoPrivacyCenter/components/SelectUserGate.tsx +0 -187
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +0 -25
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +0 -21
- package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +0 -159
- package/src/RedactoPrivacyCenter/components/constant.ts +0 -52
- package/src/RedactoPrivacyCenter/components/groupHelpers.ts +0 -119
- package/src/RedactoPrivacyCenter/components/receiptColumns.tsx +0 -203
- package/src/RedactoPrivacyCenter/components/types.ts +0 -123
- package/src/RedactoPrivacyCenter/components/utils.tsx +0 -10
- package/src/RedactoPrivacyCenter/config/routes.ts +0 -9
- package/src/RedactoPrivacyCenter/config/table.ts +0 -25
- package/src/RedactoPrivacyCenter/context/AuthContext.tsx +0 -160
- package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +0 -47
- package/src/RedactoPrivacyCenter/context/BrandingContext.tsx +0 -52
- package/src/RedactoPrivacyCenter/context/DataAvailabilityContext.session-expired.test.tsx +0 -92
- package/src/RedactoPrivacyCenter/context/DataAvailabilityContext.tsx +0 -181
- package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +0 -11
- package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +0 -96
- package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +0 -30
- package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +0 -80
- package/src/RedactoPrivacyCenter/context/ProfileSwitchContext.tsx +0 -15
- package/src/RedactoPrivacyCenter/context/types.ts +0 -19
- package/src/RedactoPrivacyCenter/lib/api.ts +0 -759
- package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +0 -67
- package/src/RedactoPrivacyCenter/lib/constants.ts +0 -276
- package/src/RedactoPrivacyCenter/lib/i18n.ts +0 -81
- package/src/RedactoPrivacyCenter/lib/locales.ts +0 -102
- package/src/RedactoPrivacyCenter/lib/queries.ts +0 -141
- package/src/RedactoPrivacyCenter/lib/types.ts +0 -876
- package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +0 -37
- package/src/RedactoPrivacyCenter/lib/utils.refresh-circuit.test.ts +0 -127
- package/src/RedactoPrivacyCenter/lib/utils.refresh-storage.test.ts +0 -85
- package/src/RedactoPrivacyCenter/lib/utils.session-expired.test.ts +0 -109
- package/src/RedactoPrivacyCenter/lib/utils.ts +0 -783
- package/src/RedactoPrivacyCenter/locales/as/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/en/translation.json +0 -357
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +0 -345
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/or/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/te/translation.json +0 -342
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +0 -342
- package/src/RedactoPrivacyCenter/styles/applyBrandingTheme.ts +0 -66
- package/src/RedactoPrivacyCenter/styles/base.css +0 -117
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +0 -3108
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +0 -831
- package/src/RedactoPrivacyCenter/styles/responsive.ts +0 -138
- package/src/RedactoPrivacyCenter/styles/variables.css +0 -96
- package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +0 -387
- package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +0 -468
- package/src/RedactoPrivacyCenter/table/index.ts +0 -2
- package/src/RedactoPrivacyCenter/types/images.d.ts +0 -29
- package/src/RedactoPrivacyCenter/ui/Footer.tsx +0 -69
- package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +0 -54
- package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +0 -93
- package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +0 -47
- package/src/RedactoPrivacyCenter/ui/PCAvatar.test.tsx +0 -83
- package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +0 -93
- package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +0 -48
- package/src/RedactoPrivacyCenter/ui/PCButton.tsx +0 -77
- package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +0 -55
- package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +0 -107
- package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +0 -35
- package/src/RedactoPrivacyCenter/ui/PCInput.tsx +0 -132
- package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +0 -31
- package/src/RedactoPrivacyCenter/ui/PCModal.tsx +0 -106
- package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +0 -206
- package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +0 -433
- package/src/RedactoPrivacyCenter/ui/PCTable.tsx +0 -149
- package/src/RedactoPrivacyCenter/ui/PCText.tsx +0 -41
- package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +0 -43
- package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +0 -37
- package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +0 -121
- package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +0 -25
- package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +0 -53
- package/src/RedactoPrivacyCenter/ui/index.ts +0 -18
- package/src/RedactoPrivacyCenter/utils/cn.ts +0 -28
- package/src/RedactoPrivacyCenter/utils/date.ts +0 -48
- package/src/RedactoPrivacyCenter/utils/status.ts +0 -111
- package/src/RedactoPrivacyCenter/utils/toast.ts +0 -130
- package/src/index.ts +0 -19
- package/src/privacy-center.ts +0 -113
- package/src/shared/api-errors.ts +0 -180
- package/src/shared/notice-consent-status.ts +0 -190
- package/src/types/css.d.ts +0 -4
- package/src/types/images.d.ts +0 -19
- package/src/types/react-jsx.d.ts +0 -26
- package/tests/DataAvailabilityContext.test.tsx +0 -123
- package/tests/Form.test.tsx +0 -1287
- package/tests/ManageConsentErrorMapping.test.tsx +0 -85
- package/tests/ProductSelectionSection.test.tsx +0 -328
- package/tests/RequestSubmit.test.tsx +0 -135
- package/tests/RevocablePurpose.test.tsx +0 -146
- package/tests/RevokeWarningMessage.test.tsx +0 -151
- package/tests/consentClient.test.ts +0 -217
- package/tests/manageConsentServer.test.ts +0 -38
- package/tests/mocks.ts +0 -257
- package/tests/resolveContactFromJwt.test.ts +0 -44
- package/tests/setup.ts +0 -16
- package/tsconfig.json +0 -16
- package/tsup.config.ts +0 -31
- package/vitest.config.ts +0 -16
|
@@ -1,911 +0,0 @@
|
|
|
1
|
-
import { apiFetch, ApiError } from "./fetcher";
|
|
2
|
-
import type { ApiFetchOptions, ApiResponse } from "./fetcher";
|
|
3
|
-
import type {
|
|
4
|
-
CaseMessage,
|
|
5
|
-
IdentityListResponse,
|
|
6
|
-
MessageDocument,
|
|
7
|
-
NotificationActionResponse,
|
|
8
|
-
NotificationListResponse,
|
|
9
|
-
NotificationQueryParams,
|
|
10
|
-
NotificationSummary,
|
|
11
|
-
SelectPrincipalResponse,
|
|
12
|
-
UnreadCountResponse,
|
|
13
|
-
UploadDocumentResponse,
|
|
14
|
-
} from "../lib/types";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Base request parameters for consent service calls
|
|
18
|
-
*/
|
|
19
|
-
export type BaseRequest = {
|
|
20
|
-
organisationUuid: string;
|
|
21
|
-
workspaceUuid: string;
|
|
22
|
-
authToken: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Consent service client factory.
|
|
27
|
-
* Creates a client instance with a specific base URL.
|
|
28
|
-
* @param baseUrl - Consent server base URL (Python service, e.g. http://host/consent)
|
|
29
|
-
* @param ledgerBaseUrl - Go ledger base URL (e.g. http://host:8090/consent-ledger).
|
|
30
|
-
* Used for user-consents reads/writes served from the ledger's `/public/...` paths.
|
|
31
|
-
* When omitted, those calls fall back to the legacy Python
|
|
32
|
-
* `/dsar/privacy-center` endpoints and payloads (pre-ledger behavior), so
|
|
33
|
-
* integrations without a deployed ledger keep working.
|
|
34
|
-
*/
|
|
35
|
-
export function createConsentClient(baseUrl: string, ledgerBaseUrl?: string) {
|
|
36
|
-
const useLedger = Boolean(ledgerBaseUrl);
|
|
37
|
-
const ledgerBase = (ledgerBaseUrl ?? "").replace(/\/$/, "");
|
|
38
|
-
/**
|
|
39
|
-
* Privacy Center API fetch wrapper that uses Authorization: Bearer header.
|
|
40
|
-
* Privacy center endpoints require JWT tokens in Authorization header, not X-Auth-Token.
|
|
41
|
-
*/
|
|
42
|
-
async function privacyCenterApiFetch<T>(
|
|
43
|
-
url: string,
|
|
44
|
-
options: ApiFetchOptions & { authToken?: string }
|
|
45
|
-
): Promise<T> {
|
|
46
|
-
const { authToken, getAuthToken, headers = {}, ...fetchOptions } = options;
|
|
47
|
-
|
|
48
|
-
// Build headers using Headers object
|
|
49
|
-
const requestHeaders = new Headers();
|
|
50
|
-
|
|
51
|
-
// Set default headers (skip Content-Type for FormData)
|
|
52
|
-
const isFormData = fetchOptions.body instanceof FormData;
|
|
53
|
-
if (!isFormData) {
|
|
54
|
-
requestHeaders.set("Content-Type", "application/json");
|
|
55
|
-
}
|
|
56
|
-
requestHeaders.set("Accept", "application/json");
|
|
57
|
-
|
|
58
|
-
// Add custom headers from options
|
|
59
|
-
if (headers && typeof headers === "object") {
|
|
60
|
-
if (headers instanceof Headers) {
|
|
61
|
-
headers.forEach((value, key) => {
|
|
62
|
-
requestHeaders.set(key, value);
|
|
63
|
-
});
|
|
64
|
-
} else {
|
|
65
|
-
Object.entries(headers as Record<string, string>).forEach(
|
|
66
|
-
([key, value]) => {
|
|
67
|
-
if (value) {
|
|
68
|
-
requestHeaders.set(key, value);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Add Authorization Bearer token for privacy center APIs
|
|
76
|
-
const token = authToken || (getAuthToken ? await getAuthToken() : null);
|
|
77
|
-
if (token) {
|
|
78
|
-
const trimmedToken = token.trim();
|
|
79
|
-
// Format token with Bearer prefix if not already present
|
|
80
|
-
const bearerToken = trimmedToken.startsWith('Bearer ')
|
|
81
|
-
? trimmedToken
|
|
82
|
-
: `Bearer ${trimmedToken}`;
|
|
83
|
-
requestHeaders.set("Authorization", bearerToken);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Build fetch options
|
|
87
|
-
const fetchOptionsFinal: RequestInit = {
|
|
88
|
-
...fetchOptions,
|
|
89
|
-
headers: requestHeaders,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const response = await fetch(url, fetchOptionsFinal);
|
|
94
|
-
|
|
95
|
-
// Handle non-OK responses
|
|
96
|
-
if (!response.ok) {
|
|
97
|
-
let errorMessage = `Request failed with status ${response.status}`;
|
|
98
|
-
let errorCode: string | undefined;
|
|
99
|
-
let errorDetail: unknown;
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const errorData = (await response.json()) as {
|
|
103
|
-
detail?: unknown;
|
|
104
|
-
message?: string;
|
|
105
|
-
code?: string;
|
|
106
|
-
} | null;
|
|
107
|
-
if (errorData) {
|
|
108
|
-
errorDetail = errorData.detail;
|
|
109
|
-
if (typeof errorData.code === "string") {
|
|
110
|
-
errorCode = errorData.code;
|
|
111
|
-
}
|
|
112
|
-
if ("detail" in errorData && errorData.detail) {
|
|
113
|
-
errorMessage =
|
|
114
|
-
typeof errorData.detail === "string"
|
|
115
|
-
? errorData.detail
|
|
116
|
-
: JSON.stringify(errorData.detail, null, 2);
|
|
117
|
-
} else if ("message" in errorData && errorData.message) {
|
|
118
|
-
errorMessage = errorData.message;
|
|
119
|
-
} else if ("code" in errorData && errorData.code) {
|
|
120
|
-
// e.g. { code: "empty_file" } or { code: "document_already_exists" }
|
|
121
|
-
errorMessage = errorData.code;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
} catch {
|
|
125
|
-
// If response is not JSON, try to get text
|
|
126
|
-
try {
|
|
127
|
-
const errorText = await response.text();
|
|
128
|
-
if (errorText) {
|
|
129
|
-
errorMessage = errorText;
|
|
130
|
-
}
|
|
131
|
-
} catch {
|
|
132
|
-
// Ignore parsing errors
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Throw a typed error carrying the status/code. The `[status]` prefix is
|
|
137
|
-
// kept in the message for back-compat with callers that branch on it
|
|
138
|
-
// (e.g. getFormData checks for "404").
|
|
139
|
-
throw new ApiError(`[${response.status}] ${errorMessage}`, {
|
|
140
|
-
status: response.status,
|
|
141
|
-
code: errorCode,
|
|
142
|
-
detail: errorDetail,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Parse JSON response
|
|
147
|
-
const contentType = response.headers.get("Content-Type") || "";
|
|
148
|
-
if (!contentType.includes("application/json")) {
|
|
149
|
-
const textResponse = await response.text();
|
|
150
|
-
throw new Error(
|
|
151
|
-
`Expected JSON response, got ${contentType}: ${textResponse}`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const data = (await response.json()) as ApiResponse<T> | T;
|
|
156
|
-
|
|
157
|
-
// Handle wrapped API responses (code, status, detail format)
|
|
158
|
-
if (
|
|
159
|
-
typeof data === "object" &&
|
|
160
|
-
data !== null &&
|
|
161
|
-
"code" in data &&
|
|
162
|
-
"status" in data &&
|
|
163
|
-
"detail" in data
|
|
164
|
-
) {
|
|
165
|
-
const apiResponse = data as ApiResponse<T>;
|
|
166
|
-
return apiResponse.detail as T;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return data as T;
|
|
170
|
-
} catch (error) {
|
|
171
|
-
if (error instanceof Error) {
|
|
172
|
-
throw error;
|
|
173
|
-
}
|
|
174
|
-
throw new Error(`Unknown error occurred: ${String(error)}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function privacyCenterUrl(baseRequest: BaseRequest, path: string): string {
|
|
179
|
-
return `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center${path}`;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function ledgerUrl(baseRequest: BaseRequest, path: string): string {
|
|
183
|
-
if (!useLedger) {
|
|
184
|
-
throw new Error("ledgerUrl called without a configured ledgerBaseUrl");
|
|
185
|
-
}
|
|
186
|
-
return `${ledgerBase}/public/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}${path}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function caseUrl(baseRequest: BaseRequest, caseUuid: string, path = ''): string {
|
|
190
|
-
return privacyCenterUrl(baseRequest, `/cases/${caseUuid}${path}`);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function notificationUrl(baseRequest: BaseRequest, path = ''): string {
|
|
194
|
-
return privacyCenterUrl(baseRequest, `/notifications${path}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function resolveAuthToken(authToken: string | undefined, baseRequest: BaseRequest): string {
|
|
198
|
-
return authToken || baseRequest.authToken;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
/**
|
|
203
|
-
* Get form data (privacy center)
|
|
204
|
-
*/
|
|
205
|
-
async getFormData(
|
|
206
|
-
baseRequest: BaseRequest,
|
|
207
|
-
contact: string,
|
|
208
|
-
authToken?: string,
|
|
209
|
-
options?: ApiFetchOptions & { useSessionAuth?: boolean },
|
|
210
|
-
language?: string
|
|
211
|
-
): Promise<unknown> {
|
|
212
|
-
const queryParams = new URLSearchParams();
|
|
213
|
-
if (language) queryParams.append("language", language);
|
|
214
|
-
const queryString = queryParams.toString();
|
|
215
|
-
const url = privacyCenterUrl(baseRequest, `/form/data${queryString ? `?${queryString}` : ""}`);
|
|
216
|
-
const { useSessionAuth = false, getAuthToken, ...fetchOptions } = options || {};
|
|
217
|
-
|
|
218
|
-
// Case management uses session-based auth (X-Auth-Token)
|
|
219
|
-
// Privacy center uses JWT Bearer tokens (Authorization: Bearer)
|
|
220
|
-
if (useSessionAuth) {
|
|
221
|
-
return apiFetch(url, {
|
|
222
|
-
...fetchOptions,
|
|
223
|
-
method: "POST",
|
|
224
|
-
body: JSON.stringify({ contact }),
|
|
225
|
-
getAuthToken,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Privacy center: use JWT Bearer token
|
|
230
|
-
return privacyCenterApiFetch(url, {
|
|
231
|
-
...fetchOptions,
|
|
232
|
-
method: "POST",
|
|
233
|
-
body: JSON.stringify({ contact }),
|
|
234
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
235
|
-
});
|
|
236
|
-
},
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Create case request (privacy center or case management)
|
|
240
|
-
*/
|
|
241
|
-
async createCaseRequest(
|
|
242
|
-
baseRequest: BaseRequest,
|
|
243
|
-
payload: unknown,
|
|
244
|
-
authToken?: string,
|
|
245
|
-
options?: ApiFetchOptions & { useSessionAuth?: boolean }
|
|
246
|
-
): Promise<unknown> {
|
|
247
|
-
const url = privacyCenterUrl(baseRequest, '/case');
|
|
248
|
-
const { useSessionAuth = false, getAuthToken, ...fetchOptions } = options || {};
|
|
249
|
-
|
|
250
|
-
// Case management uses session-based auth (X-Auth-Token)
|
|
251
|
-
// Privacy center uses JWT Bearer tokens (Authorization: Bearer)
|
|
252
|
-
if (useSessionAuth) {
|
|
253
|
-
return apiFetch(url, {
|
|
254
|
-
...fetchOptions,
|
|
255
|
-
method: "POST",
|
|
256
|
-
body: JSON.stringify(payload),
|
|
257
|
-
getAuthToken,
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Privacy center: use JWT Bearer token
|
|
262
|
-
return privacyCenterApiFetch(url, {
|
|
263
|
-
...fetchOptions,
|
|
264
|
-
method: "POST",
|
|
265
|
-
body: JSON.stringify(payload),
|
|
266
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
267
|
-
});
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Create OTP (public)
|
|
272
|
-
*/
|
|
273
|
-
async createOtp(
|
|
274
|
-
organisationId: string,
|
|
275
|
-
workspaceId: string,
|
|
276
|
-
email: string,
|
|
277
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
278
|
-
): Promise<unknown> {
|
|
279
|
-
const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/create`;
|
|
280
|
-
return apiFetch(url, {
|
|
281
|
-
...options,
|
|
282
|
-
method: "POST",
|
|
283
|
-
body: JSON.stringify({ to: email, channel: "email" }),
|
|
284
|
-
});
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Verify OTP (public)
|
|
289
|
-
*/
|
|
290
|
-
async verifyOtp(
|
|
291
|
-
organisationId: string,
|
|
292
|
-
workspaceId: string,
|
|
293
|
-
otpId: string,
|
|
294
|
-
otp: string,
|
|
295
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
296
|
-
): Promise<unknown> {
|
|
297
|
-
const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/verify`;
|
|
298
|
-
return apiFetch(url, {
|
|
299
|
-
...options,
|
|
300
|
-
method: "POST",
|
|
301
|
-
body: JSON.stringify({ uuid: otpId, otp }),
|
|
302
|
-
});
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Exchange one-time token for JWT tokens (public)
|
|
307
|
-
*/
|
|
308
|
-
async exchangeToken(
|
|
309
|
-
token: string,
|
|
310
|
-
): Promise<unknown> {
|
|
311
|
-
const url = `${baseUrl}/public/tokens/token-exchange?token=${encodeURIComponent(token)}`;
|
|
312
|
-
const response = await fetch(url, {
|
|
313
|
-
method: "GET",
|
|
314
|
-
headers: { "Accept": "application/json" },
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
if (!response.ok) {
|
|
318
|
-
const errorData = await response.json().catch(() => null) as { detail?: string; message?: string } | null;
|
|
319
|
-
throw new Error(
|
|
320
|
-
errorData?.detail || errorData?.message || `Token exchange failed with status ${response.status}`
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const data = await response.json() as { code?: number; status?: string; detail?: unknown };
|
|
325
|
-
if (data && typeof data === "object" && "code" in data && "status" in data && "detail" in data) {
|
|
326
|
-
return data.detail;
|
|
327
|
-
}
|
|
328
|
-
return data;
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Refresh OTP token (public)
|
|
333
|
-
*/
|
|
334
|
-
async refreshOtpToken(
|
|
335
|
-
organisationId: string,
|
|
336
|
-
workspaceId: string,
|
|
337
|
-
refreshToken: string,
|
|
338
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
339
|
-
): Promise<unknown> {
|
|
340
|
-
const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/refresh`;
|
|
341
|
-
return apiFetch(url, {
|
|
342
|
-
...options,
|
|
343
|
-
method: "POST",
|
|
344
|
-
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
345
|
-
});
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Get products
|
|
350
|
-
*/
|
|
351
|
-
async getProducts(
|
|
352
|
-
baseRequest: BaseRequest,
|
|
353
|
-
skip: number = 0,
|
|
354
|
-
limit: number = 10,
|
|
355
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
356
|
-
): Promise<unknown> {
|
|
357
|
-
const url = `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/notice-management/products/?skip=${skip}&limit=${limit}`;
|
|
358
|
-
return apiFetch<unknown>(url, {
|
|
359
|
-
...options,
|
|
360
|
-
getAuthToken: async () => baseRequest.authToken,
|
|
361
|
-
});
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Get user consents (privacy center)
|
|
366
|
-
*/
|
|
367
|
-
async getUserConsents(
|
|
368
|
-
baseRequest: BaseRequest,
|
|
369
|
-
params: {
|
|
370
|
-
offset?: number;
|
|
371
|
-
limit?: number;
|
|
372
|
-
nominated_offset?: number;
|
|
373
|
-
nominated_limit?: number;
|
|
374
|
-
status?: string;
|
|
375
|
-
product_uuid?: string;
|
|
376
|
-
search?: string;
|
|
377
|
-
language?: string;
|
|
378
|
-
},
|
|
379
|
-
authToken?: string,
|
|
380
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
381
|
-
): Promise<unknown> {
|
|
382
|
-
const queryParams = new URLSearchParams();
|
|
383
|
-
if (params.offset !== undefined) queryParams.append("offset", params.offset.toString());
|
|
384
|
-
if (params.limit !== undefined) queryParams.append("limit", params.limit.toString());
|
|
385
|
-
if (params.nominated_offset !== undefined) queryParams.append("nominated_offset", params.nominated_offset.toString());
|
|
386
|
-
if (params.nominated_limit !== undefined) queryParams.append("nominated_limit", params.nominated_limit.toString());
|
|
387
|
-
if (params.search) queryParams.append("search", params.search);
|
|
388
|
-
if (params.status) queryParams.append("status", params.status);
|
|
389
|
-
if (params.product_uuid) queryParams.append("product_uuid", params.product_uuid);
|
|
390
|
-
if (params.language) queryParams.append("language", params.language);
|
|
391
|
-
const queryString = queryParams.toString();
|
|
392
|
-
const suffix = `/user-consents${queryString ? `?${queryString}` : ""}`;
|
|
393
|
-
const url = useLedger
|
|
394
|
-
? ledgerUrl(baseRequest, suffix)
|
|
395
|
-
: privacyCenterUrl(baseRequest, suffix);
|
|
396
|
-
return privacyCenterApiFetch(url, {
|
|
397
|
-
...options,
|
|
398
|
-
method: "GET",
|
|
399
|
-
cache: "no-store",
|
|
400
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
401
|
-
});
|
|
402
|
-
},
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Get purposes for a single product card (privacy center) — sister endpoint
|
|
406
|
-
* for lazy-loading purposes when a card returns has_more_purposes: true.
|
|
407
|
-
*/
|
|
408
|
-
async getGroupPurposes(
|
|
409
|
-
baseRequest: BaseRequest,
|
|
410
|
-
productUuid: string,
|
|
411
|
-
params: {
|
|
412
|
-
offset?: number;
|
|
413
|
-
limit?: number;
|
|
414
|
-
status?: string;
|
|
415
|
-
search?: string;
|
|
416
|
-
language?: string;
|
|
417
|
-
nominator_uuid?: string;
|
|
418
|
-
},
|
|
419
|
-
authToken?: string,
|
|
420
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
421
|
-
): Promise<unknown> {
|
|
422
|
-
const queryParams = new URLSearchParams();
|
|
423
|
-
if (params.offset !== undefined) queryParams.append("offset", params.offset.toString());
|
|
424
|
-
if (params.limit !== undefined) queryParams.append("limit", params.limit.toString());
|
|
425
|
-
if (params.status) queryParams.append("status", params.status);
|
|
426
|
-
if (params.search) queryParams.append("search", params.search);
|
|
427
|
-
if (params.language) queryParams.append("language", params.language);
|
|
428
|
-
if (params.nominator_uuid) queryParams.append("nominator_uuid", params.nominator_uuid);
|
|
429
|
-
const queryString = queryParams.toString();
|
|
430
|
-
const suffix = `/user-consents/${productUuid}/purposes${queryString ? `?${queryString}` : ""}`;
|
|
431
|
-
const url = useLedger
|
|
432
|
-
? ledgerUrl(baseRequest, suffix)
|
|
433
|
-
: privacyCenterUrl(baseRequest, suffix);
|
|
434
|
-
return privacyCenterApiFetch(url, {
|
|
435
|
-
...options,
|
|
436
|
-
method: "GET",
|
|
437
|
-
cache: "no-store",
|
|
438
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
439
|
-
});
|
|
440
|
-
},
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Manage consent (privacy center) - revoke, regrant, or renew.
|
|
444
|
-
*
|
|
445
|
-
* The Go ledger has no manage-consent route, so with a ledger configured
|
|
446
|
-
* this is expressed as a `partial` submit-consent on the ledger — mirroring
|
|
447
|
-
* the Python dual-write relay (`build_consent_management_payload`): the
|
|
448
|
-
* affected purpose is re-submitted with `selected: false` to revoke or
|
|
449
|
-
* `selected: true` to regrant/renew, and `partial: true` so the rest of the
|
|
450
|
-
* notice's purposes are left untouched. The principal is resolved from the
|
|
451
|
-
* JWT, so no `contact`/identifiers are sent in the body, and
|
|
452
|
-
* `consent_event_uuid` is omitted so the ledger mints a fresh event.
|
|
453
|
-
*
|
|
454
|
-
* Without a ledger, the legacy Python `/manage-consent?status=` endpoint is
|
|
455
|
-
* used with its `{ purpose_uuid, contact, ... }` payload.
|
|
456
|
-
*/
|
|
457
|
-
async manageConsent(
|
|
458
|
-
baseRequest: BaseRequest,
|
|
459
|
-
purposeId: string,
|
|
460
|
-
// Sent to the legacy Python endpoint only; the ledger resolves the
|
|
461
|
-
// principal from the JWT, so no identifiers are sent in ledger mode.
|
|
462
|
-
contact: string,
|
|
463
|
-
status: 'revoke' | 'regrant' | 'renew',
|
|
464
|
-
authToken?: string,
|
|
465
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">,
|
|
466
|
-
nominatorContact?: string,
|
|
467
|
-
dataElementUuids?: string[],
|
|
468
|
-
productUuid?: string,
|
|
469
|
-
noticeUuid?: string,
|
|
470
|
-
dataElements?: Array<{ uuid: string; selected: boolean }>,
|
|
471
|
-
language?: string
|
|
472
|
-
): Promise<unknown> {
|
|
473
|
-
if (!useLedger) {
|
|
474
|
-
const url = privacyCenterUrl(baseRequest, `/manage-consent?status=${status}`);
|
|
475
|
-
const body: Record<string, unknown> = { purpose_uuid: purposeId, contact };
|
|
476
|
-
if (nominatorContact) {
|
|
477
|
-
body.nominator_contact = nominatorContact;
|
|
478
|
-
}
|
|
479
|
-
if (Array.isArray(dataElementUuids) && dataElementUuids.length > 0) {
|
|
480
|
-
body.data_element_uuids = dataElementUuids;
|
|
481
|
-
}
|
|
482
|
-
if (productUuid) {
|
|
483
|
-
body.product_uuid = productUuid;
|
|
484
|
-
}
|
|
485
|
-
if (language) {
|
|
486
|
-
body.language = language;
|
|
487
|
-
}
|
|
488
|
-
return privacyCenterApiFetch(url, {
|
|
489
|
-
...options,
|
|
490
|
-
method: "POST",
|
|
491
|
-
body: JSON.stringify(body),
|
|
492
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
if (!noticeUuid) {
|
|
496
|
-
throw new Error("notice_uuid is required to manage consent via the ledger");
|
|
497
|
-
}
|
|
498
|
-
const purposeSelected = status !== 'revoke';
|
|
499
|
-
const elements = (dataElements ?? []).map((de) => {
|
|
500
|
-
let selected: boolean;
|
|
501
|
-
if (status === 'revoke') {
|
|
502
|
-
selected = false;
|
|
503
|
-
} else if (status === 'regrant') {
|
|
504
|
-
// Only the data elements the user chose to re-grant become selected.
|
|
505
|
-
selected = Array.isArray(dataElementUuids)
|
|
506
|
-
? dataElementUuids.includes(de.uuid)
|
|
507
|
-
: de.selected;
|
|
508
|
-
} else {
|
|
509
|
-
// renew — preserve the currently-granted set.
|
|
510
|
-
selected = de.selected;
|
|
511
|
-
}
|
|
512
|
-
return { uuid: de.uuid, selected };
|
|
513
|
-
});
|
|
514
|
-
const body = {
|
|
515
|
-
notice_uuid: noticeUuid,
|
|
516
|
-
partial: true,
|
|
517
|
-
declined: false,
|
|
518
|
-
select_all_mandatory: false,
|
|
519
|
-
self_declared_adult: false,
|
|
520
|
-
purposes: [
|
|
521
|
-
{
|
|
522
|
-
purpose_uuid: purposeId,
|
|
523
|
-
selected: purposeSelected,
|
|
524
|
-
data_elements: elements,
|
|
525
|
-
},
|
|
526
|
-
],
|
|
527
|
-
...(language ? { language } : {}),
|
|
528
|
-
};
|
|
529
|
-
const url = ledgerUrl(baseRequest, "/submit-consent");
|
|
530
|
-
return privacyCenterApiFetch(url, {
|
|
531
|
-
...options,
|
|
532
|
-
method: "POST",
|
|
533
|
-
body: JSON.stringify(body),
|
|
534
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
535
|
-
});
|
|
536
|
-
},
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Get activities (privacy center)
|
|
540
|
-
*/
|
|
541
|
-
async getActivities(
|
|
542
|
-
baseRequest: BaseRequest,
|
|
543
|
-
offset?: number,
|
|
544
|
-
limit?: number,
|
|
545
|
-
language?: string,
|
|
546
|
-
authToken?: string,
|
|
547
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
548
|
-
): Promise<unknown> {
|
|
549
|
-
const queryParams = new URLSearchParams();
|
|
550
|
-
if (offset !== undefined) queryParams.append("offset", offset.toString());
|
|
551
|
-
if (limit !== undefined) queryParams.append("limit", limit.toString());
|
|
552
|
-
if (language) queryParams.append("language", language);
|
|
553
|
-
const queryString = queryParams.toString();
|
|
554
|
-
const url = privacyCenterUrl(baseRequest, `/activities${queryString ? `?${queryString}` : ""}`);
|
|
555
|
-
return privacyCenterApiFetch<unknown>(url, {
|
|
556
|
-
...options,
|
|
557
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
558
|
-
});
|
|
559
|
-
},
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* List all data principals that share this session's contact
|
|
563
|
-
* (shared-contact profile picker). Identity is derived server-side from the
|
|
564
|
-
* JWT — the caller sends nothing but its bearer token.
|
|
565
|
-
*/
|
|
566
|
-
async listIdentities(
|
|
567
|
-
baseRequest: BaseRequest,
|
|
568
|
-
authToken?: string,
|
|
569
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
570
|
-
): Promise<IdentityListResponse> {
|
|
571
|
-
const url = privacyCenterUrl(baseRequest, '/identities');
|
|
572
|
-
return privacyCenterApiFetch<IdentityListResponse>(url, {
|
|
573
|
-
...options,
|
|
574
|
-
cache: 'no-store',
|
|
575
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
576
|
-
});
|
|
577
|
-
},
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* Re-scope the session to a chosen principal uuid (must be one of the
|
|
581
|
-
* principals sharing this session's contact). Returns fresh access/refresh
|
|
582
|
-
* tokens scoped to that principal.
|
|
583
|
-
*/
|
|
584
|
-
async selectPrincipal(
|
|
585
|
-
baseRequest: BaseRequest,
|
|
586
|
-
principalUuid: string,
|
|
587
|
-
authToken?: string,
|
|
588
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
589
|
-
): Promise<SelectPrincipalResponse> {
|
|
590
|
-
const url = privacyCenterUrl(baseRequest, '/identities/select');
|
|
591
|
-
return privacyCenterApiFetch<SelectPrincipalResponse>(url, {
|
|
592
|
-
...options,
|
|
593
|
-
method: 'POST',
|
|
594
|
-
body: JSON.stringify({ uuid: principalUuid }),
|
|
595
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
596
|
-
});
|
|
597
|
-
},
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Get receipts (privacy center) — consent receipts for the authenticated user
|
|
601
|
-
*/
|
|
602
|
-
async getReceipts(
|
|
603
|
-
baseRequest: BaseRequest,
|
|
604
|
-
params: {
|
|
605
|
-
skip?: number;
|
|
606
|
-
limit?: number;
|
|
607
|
-
notice_uuid?: string;
|
|
608
|
-
event_type?: string;
|
|
609
|
-
status?: string;
|
|
610
|
-
created_after?: string;
|
|
611
|
-
created_before?: string;
|
|
612
|
-
language?: string;
|
|
613
|
-
},
|
|
614
|
-
authToken?: string,
|
|
615
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
616
|
-
): Promise<unknown> {
|
|
617
|
-
const queryParams = new URLSearchParams();
|
|
618
|
-
if (params.skip !== undefined) queryParams.append("skip", params.skip.toString());
|
|
619
|
-
if (params.limit !== undefined) queryParams.append("limit", params.limit.toString());
|
|
620
|
-
if (params.notice_uuid) queryParams.append("notice_uuid", params.notice_uuid);
|
|
621
|
-
if (params.event_type) queryParams.append("event_type", params.event_type);
|
|
622
|
-
if (params.status) queryParams.append("status", params.status);
|
|
623
|
-
if (params.created_after) queryParams.append("created_after", params.created_after);
|
|
624
|
-
if (params.created_before) queryParams.append("created_before", params.created_before);
|
|
625
|
-
if (params.language) queryParams.append("language", params.language);
|
|
626
|
-
const queryString = queryParams.toString();
|
|
627
|
-
// With a ledger configured, receipts are served by the Go ledger
|
|
628
|
-
// (paginated list under /public/...); otherwise the legacy Python
|
|
629
|
-
// privacy-center list is used. normalizeReceipts tolerates both envelopes.
|
|
630
|
-
const receiptsSuffix = `/receipts${queryString ? `?${queryString}` : ""}`;
|
|
631
|
-
const url = useLedger
|
|
632
|
-
? ledgerUrl(baseRequest, receiptsSuffix)
|
|
633
|
-
: privacyCenterUrl(baseRequest, receiptsSuffix);
|
|
634
|
-
return privacyCenterApiFetch<unknown>(url, {
|
|
635
|
-
...options,
|
|
636
|
-
method: "GET",
|
|
637
|
-
cache: "no-store",
|
|
638
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
639
|
-
});
|
|
640
|
-
},
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* Download a single receipt as a PDF (privacy center).
|
|
644
|
-
* Returns raw PDF bytes as a Blob — the caller builds an object URL to open/save it.
|
|
645
|
-
* Uses a direct fetch (not privacyCenterApiFetch) because the response is binary, not JSON,
|
|
646
|
-
* and the Authorization header must be sent (anchor navigation wouldn't carry it).
|
|
647
|
-
*/
|
|
648
|
-
async getReceiptPdf(
|
|
649
|
-
baseRequest: BaseRequest,
|
|
650
|
-
receiptUuid: string,
|
|
651
|
-
authToken?: string
|
|
652
|
-
): Promise<Blob> {
|
|
653
|
-
// The ledger serves the receipt PDF at /receipts/{uuid}/download, the
|
|
654
|
-
// legacy Python service at /receipts/{uuid}/pdf. The receiptUuid always
|
|
655
|
-
// comes from the same-mode list above, so list and download stay consistent.
|
|
656
|
-
const url = useLedger
|
|
657
|
-
? ledgerUrl(baseRequest, `/receipts/${receiptUuid}/download`)
|
|
658
|
-
: privacyCenterUrl(baseRequest, `/receipts/${receiptUuid}/pdf`);
|
|
659
|
-
const token = resolveAuthToken(authToken, baseRequest);
|
|
660
|
-
const requestHeaders = new Headers();
|
|
661
|
-
requestHeaders.set("Accept", "application/pdf");
|
|
662
|
-
if (token) {
|
|
663
|
-
const trimmedToken = token.trim();
|
|
664
|
-
requestHeaders.set(
|
|
665
|
-
"Authorization",
|
|
666
|
-
trimmedToken.startsWith("Bearer ") ? trimmedToken : `Bearer ${trimmedToken}`
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
const response = await fetch(url, { method: "GET", headers: requestHeaders });
|
|
670
|
-
if (!response.ok) {
|
|
671
|
-
let errorMessage = `Request failed with status ${response.status}`;
|
|
672
|
-
try {
|
|
673
|
-
const errorData = (await response.json()) as { detail?: unknown; error_code?: string } | null;
|
|
674
|
-
if (errorData?.detail) {
|
|
675
|
-
errorMessage =
|
|
676
|
-
typeof errorData.detail === "string" ? errorData.detail : JSON.stringify(errorData.detail);
|
|
677
|
-
} else if (errorData?.error_code) {
|
|
678
|
-
errorMessage = errorData.error_code;
|
|
679
|
-
}
|
|
680
|
-
} catch {
|
|
681
|
-
// non-JSON error body — keep default message
|
|
682
|
-
}
|
|
683
|
-
// Prepend HTTP status so callers can branch on it (e.g. 404 = receipt not found)
|
|
684
|
-
throw new Error(`[${response.status}] ${errorMessage}`);
|
|
685
|
-
}
|
|
686
|
-
return response.blob();
|
|
687
|
-
},
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* Get case history (privacy center)
|
|
691
|
-
*/
|
|
692
|
-
async getCaseHistory(
|
|
693
|
-
baseRequest: BaseRequest,
|
|
694
|
-
_contact: string,
|
|
695
|
-
offset?: number,
|
|
696
|
-
limit?: number,
|
|
697
|
-
language?: string,
|
|
698
|
-
authToken?: string,
|
|
699
|
-
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
700
|
-
): Promise<unknown> {
|
|
701
|
-
const queryParams = new URLSearchParams();
|
|
702
|
-
if (offset !== undefined) queryParams.append("offset", offset.toString());
|
|
703
|
-
if (limit !== undefined) queryParams.append("limit", limit.toString());
|
|
704
|
-
if (language) queryParams.append("language", language);
|
|
705
|
-
const queryString = queryParams.toString();
|
|
706
|
-
const url = privacyCenterUrl(baseRequest, `/cases?${queryString}`);
|
|
707
|
-
return privacyCenterApiFetch<unknown>(url, {
|
|
708
|
-
...options,
|
|
709
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
710
|
-
});
|
|
711
|
-
},
|
|
712
|
-
|
|
713
|
-
// ─── Case Communication ─────────────────────────────────────────────────
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* Get messages for a case (privacy center)
|
|
717
|
-
*/
|
|
718
|
-
async getCaseMessages(
|
|
719
|
-
baseRequest: BaseRequest,
|
|
720
|
-
caseUuid: string,
|
|
721
|
-
authToken?: string
|
|
722
|
-
): Promise<CaseMessage[]> {
|
|
723
|
-
const url = caseUrl(baseRequest, caseUuid, '/messages');
|
|
724
|
-
return privacyCenterApiFetch<CaseMessage[]>(url, {
|
|
725
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
726
|
-
});
|
|
727
|
-
},
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Send a message on a case (privacy center)
|
|
731
|
-
*/
|
|
732
|
-
async sendCaseMessage(
|
|
733
|
-
baseRequest: BaseRequest,
|
|
734
|
-
caseUuid: string,
|
|
735
|
-
body: string,
|
|
736
|
-
documentUuids: string[],
|
|
737
|
-
authToken?: string
|
|
738
|
-
): Promise<CaseMessage> {
|
|
739
|
-
const url = caseUrl(baseRequest, caseUuid, '/messages');
|
|
740
|
-
return privacyCenterApiFetch<CaseMessage>(url, {
|
|
741
|
-
method: 'POST',
|
|
742
|
-
body: JSON.stringify({ body, document_uuids: documentUuids }),
|
|
743
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
744
|
-
});
|
|
745
|
-
},
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* Upload a document on a case (multipart/form-data, privacy center)
|
|
749
|
-
*/
|
|
750
|
-
async uploadCaseDocument(
|
|
751
|
-
baseRequest: BaseRequest,
|
|
752
|
-
caseUuid: string,
|
|
753
|
-
file: File,
|
|
754
|
-
authToken?: string
|
|
755
|
-
): Promise<UploadDocumentResponse> {
|
|
756
|
-
const url = caseUrl(baseRequest, caseUuid, '/upload-document');
|
|
757
|
-
const formData = new FormData();
|
|
758
|
-
formData.append('file', file, file.name); // field name must be exactly "file"
|
|
759
|
-
// "payload" is required by the privacy-center endpoint (omitting it causes 422)
|
|
760
|
-
formData.append('payload', JSON.stringify({ metadata: null }));
|
|
761
|
-
return privacyCenterApiFetch<UploadDocumentResponse>(url, {
|
|
762
|
-
method: 'POST',
|
|
763
|
-
body: formData,
|
|
764
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
765
|
-
});
|
|
766
|
-
},
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* Submit a document against a handler document request (privacy center)
|
|
770
|
-
*/
|
|
771
|
-
async submitDocumentForRequest(
|
|
772
|
-
baseRequest: BaseRequest,
|
|
773
|
-
caseUuid: string,
|
|
774
|
-
requestEventUuid: string,
|
|
775
|
-
documentUuid: string,
|
|
776
|
-
body: string,
|
|
777
|
-
authToken?: string
|
|
778
|
-
): Promise<CaseMessage> {
|
|
779
|
-
const url = caseUrl(baseRequest, caseUuid, `/document-requests/${requestEventUuid}/submit`);
|
|
780
|
-
return privacyCenterApiFetch<CaseMessage>(url, {
|
|
781
|
-
method: 'POST',
|
|
782
|
-
body: JSON.stringify({ document_uuid: documentUuid, body }),
|
|
783
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
784
|
-
});
|
|
785
|
-
},
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* Get document info/download URL for a case document (privacy center)
|
|
789
|
-
*/
|
|
790
|
-
async getCaseDocument(
|
|
791
|
-
baseRequest: BaseRequest,
|
|
792
|
-
caseUuid: string,
|
|
793
|
-
documentUuid: string,
|
|
794
|
-
authToken?: string
|
|
795
|
-
): Promise<MessageDocument> {
|
|
796
|
-
const url = caseUrl(baseRequest, caseUuid, `/documents/${documentUuid}`);
|
|
797
|
-
return privacyCenterApiFetch<MessageDocument>(url, {
|
|
798
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
799
|
-
});
|
|
800
|
-
},
|
|
801
|
-
|
|
802
|
-
// ─── Notification Hub ─────────────────────────────────────────────────
|
|
803
|
-
|
|
804
|
-
/**
|
|
805
|
-
* List notifications (paginated, with filters)
|
|
806
|
-
*/
|
|
807
|
-
async getNotifications(
|
|
808
|
-
baseRequest: BaseRequest,
|
|
809
|
-
params?: NotificationQueryParams,
|
|
810
|
-
authToken?: string
|
|
811
|
-
): Promise<NotificationListResponse> {
|
|
812
|
-
const queryParams = new URLSearchParams();
|
|
813
|
-
if (params?.category) {
|
|
814
|
-
queryParams.append('category', params.category);
|
|
815
|
-
}
|
|
816
|
-
if (params?.notification_type) {
|
|
817
|
-
queryParams.append('notification_type', params.notification_type);
|
|
818
|
-
}
|
|
819
|
-
if (params?.is_read !== undefined) {
|
|
820
|
-
queryParams.append('is_read', String(params.is_read));
|
|
821
|
-
}
|
|
822
|
-
if (params?.skip !== undefined) {
|
|
823
|
-
queryParams.append('skip', params.skip.toString());
|
|
824
|
-
}
|
|
825
|
-
if (params?.limit !== undefined) {
|
|
826
|
-
queryParams.append('limit', params.limit.toString());
|
|
827
|
-
}
|
|
828
|
-
if (params?.include_summary) {
|
|
829
|
-
queryParams.append('include_summary', 'true');
|
|
830
|
-
}
|
|
831
|
-
if (params?.language) {
|
|
832
|
-
queryParams.append('language', params.language);
|
|
833
|
-
}
|
|
834
|
-
const queryString = queryParams.toString();
|
|
835
|
-
const url = notificationUrl(baseRequest, queryString ? `?${queryString}` : '');
|
|
836
|
-
return privacyCenterApiFetch<NotificationListResponse>(url, {
|
|
837
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
838
|
-
});
|
|
839
|
-
},
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* Get notification summary (aggregated counts only)
|
|
843
|
-
*/
|
|
844
|
-
async getNotificationSummary(
|
|
845
|
-
baseRequest: BaseRequest,
|
|
846
|
-
authToken?: string
|
|
847
|
-
): Promise<NotificationSummary> {
|
|
848
|
-
const url = notificationUrl(baseRequest, '/summary');
|
|
849
|
-
return privacyCenterApiFetch<NotificationSummary>(url, {
|
|
850
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
851
|
-
});
|
|
852
|
-
},
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* Get unread notification count (lightweight, for badge)
|
|
856
|
-
*/
|
|
857
|
-
async getUnreadCount(
|
|
858
|
-
baseRequest: BaseRequest,
|
|
859
|
-
authToken?: string
|
|
860
|
-
): Promise<UnreadCountResponse> {
|
|
861
|
-
const url = notificationUrl(baseRequest, '/unread-count');
|
|
862
|
-
return privacyCenterApiFetch<UnreadCountResponse>(url, {
|
|
863
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
864
|
-
});
|
|
865
|
-
},
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Mark a single notification as read
|
|
869
|
-
*/
|
|
870
|
-
async markNotificationRead(
|
|
871
|
-
baseRequest: BaseRequest,
|
|
872
|
-
notificationUuid: string,
|
|
873
|
-
authToken?: string
|
|
874
|
-
): Promise<NotificationActionResponse> {
|
|
875
|
-
const url = notificationUrl(baseRequest, `/${notificationUuid}/read`);
|
|
876
|
-
return privacyCenterApiFetch<NotificationActionResponse>(url, {
|
|
877
|
-
method: 'PATCH',
|
|
878
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
879
|
-
});
|
|
880
|
-
},
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* Mark all notifications as read
|
|
884
|
-
*/
|
|
885
|
-
async markAllNotificationsRead(
|
|
886
|
-
baseRequest: BaseRequest,
|
|
887
|
-
authToken?: string
|
|
888
|
-
): Promise<NotificationActionResponse> {
|
|
889
|
-
const url = notificationUrl(baseRequest, '/mark-all-read');
|
|
890
|
-
return privacyCenterApiFetch<NotificationActionResponse>(url, {
|
|
891
|
-
method: 'POST',
|
|
892
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
893
|
-
});
|
|
894
|
-
},
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* Acknowledge a notification (sets is_read and is_acknowledged to true)
|
|
898
|
-
*/
|
|
899
|
-
async acknowledgeNotification(
|
|
900
|
-
baseRequest: BaseRequest,
|
|
901
|
-
notificationUuid: string,
|
|
902
|
-
authToken?: string
|
|
903
|
-
): Promise<NotificationActionResponse> {
|
|
904
|
-
const url = notificationUrl(baseRequest, `/${notificationUuid}/acknowledge`);
|
|
905
|
-
return privacyCenterApiFetch<NotificationActionResponse>(url, {
|
|
906
|
-
method: 'POST',
|
|
907
|
-
authToken: resolveAuthToken(authToken, baseRequest),
|
|
908
|
-
});
|
|
909
|
-
},
|
|
910
|
-
};
|
|
911
|
-
}
|