@redacto.io/consent-sdk-react 10.0.0-beta.0 → 10.0.0-beta.1
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/.turbo/turbo-build.log +22 -25
- package/CHANGELOG.md +14 -0
- package/dist/index.d.mts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +6 -8
- package/dist/index.mjs +6 -8
- package/dist/privacy-center.d.mts +60 -27
- package/dist/privacy-center.d.ts +60 -27
- package/dist/privacy-center.js +1872 -1363
- package/dist/privacy-center.mjs +1796 -1282
- package/package.json +1 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +1 -0
- package/src/RedactoNoticeConsent/api/index.ts +4 -4
- package/src/RedactoNoticeConsent/api/types.ts +3 -2
- package/src/RedactoNoticeConsent/types.ts +1 -1
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +1 -0
- package/src/RedactoNoticeConsentInline/api/index.ts +4 -4
- package/src/RedactoNoticeConsentInline/api/types.ts +2 -1
- package/src/RedactoNoticeConsentInline/types.ts +1 -1
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +8 -5
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +1 -1
- package/src/RedactoPrivacyCenter/api/actions.ts +10 -4
- package/src/RedactoPrivacyCenter/api/client.ts +101 -21
- package/src/RedactoPrivacyCenter/components/AppShell/UserMenu.tsx +64 -35
- package/src/RedactoPrivacyCenter/components/Form.tsx +7 -3
- package/src/RedactoPrivacyCenter/components/SelectUser.tsx +83 -0
- package/src/RedactoPrivacyCenter/components/SelectUserGate.tsx +193 -0
- package/src/RedactoPrivacyCenter/components/constant.ts +11 -0
- package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +1 -1
- package/src/RedactoPrivacyCenter/context/ProfileSwitchContext.tsx +15 -0
- package/src/RedactoPrivacyCenter/context/types.ts +1 -1
- package/src/RedactoPrivacyCenter/lib/types.ts +54 -2
- package/src/RedactoPrivacyCenter/locales/as/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/bho/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/en/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/or/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/te/translation.json +7 -1
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +7 -1
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +105 -0
- package/tests/Form.test.tsx +53 -7
- package/tests/consentClient.test.ts +171 -0
- package/tests/manageConsentServer.test.ts +38 -0
|
@@ -330,5 +330,11 @@
|
|
|
330
330
|
"casesCreatedCount_other": "{{count}} cases created",
|
|
331
331
|
"someRequestsFailed": "Some requests failed",
|
|
332
332
|
"retry": "Retry",
|
|
333
|
-
"retrying": "Retrying..."
|
|
333
|
+
"retrying": "Retrying...",
|
|
334
|
+
"switchProfile": "சுயவிவரத்தை மாற்று",
|
|
335
|
+
"chooseYourProfile": "உங்கள் சுயவிவரத்தைத் தேர்ந்தெடுக்கவும்",
|
|
336
|
+
"emailLinkedToMultipleProfiles": "{{email}} ஒன்றுக்கு மேற்பட்ட சுயவிவரங்களுடன் இணைக்கப்பட்டுள்ளது. நீங்கள் பார்க்க விரும்புவதைத் தேர்ந்தெடுக்கவும்.",
|
|
337
|
+
"contactLinkedToMultipleProfiles": "இந்தத் தொடர்பு ஒன்றுக்கு மேற்பட்ட சுயவிவரங்களுடன் இணைக்கப்பட்டுள்ளது. நீங்கள் பார்க்க விரும்புவதைத் தேர்ந்தெடுக்கவும்.",
|
|
338
|
+
"signingInEllipsis": "உள்நுழைகிறது…",
|
|
339
|
+
"couldNotSwitchProfile": "அந்தச் சுயவிவரத்திற்கு மாற முடியவில்லை. மீண்டும் முயற்சிக்கவும்."
|
|
334
340
|
}
|
|
@@ -330,5 +330,11 @@
|
|
|
330
330
|
"casesCreatedCount_other": "{{count}} cases created",
|
|
331
331
|
"someRequestsFailed": "Some requests failed",
|
|
332
332
|
"retry": "Retry",
|
|
333
|
-
"retrying": "Retrying..."
|
|
333
|
+
"retrying": "Retrying...",
|
|
334
|
+
"switchProfile": "ప్రొఫైల్ను మార్చండి",
|
|
335
|
+
"chooseYourProfile": "మీ ప్రొఫైల్ను ఎంచుకోండి",
|
|
336
|
+
"emailLinkedToMultipleProfiles": "{{email}} ఒకటి కంటే ఎక్కువ ప్రొఫైల్లతో అనుసంధానించబడింది. మీరు చూడాలనుకుంటున్నదాన్ని ఎంచుకోండి.",
|
|
337
|
+
"contactLinkedToMultipleProfiles": "ఈ సంప్రదింపు ఒకటి కంటే ఎక్కువ ప్రొఫైల్లతో అనుసంధానించబడింది. మీరు చూడాలనుకుంటున్నదాన్ని ఎంచుకోండి.",
|
|
338
|
+
"signingInEllipsis": "సైన్ ఇన్ అవుతోంది…",
|
|
339
|
+
"couldNotSwitchProfile": "ఆ ప్రొఫైల్కు మారడం సాధ్యం కాలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."
|
|
334
340
|
}
|
|
@@ -330,5 +330,11 @@
|
|
|
330
330
|
"casesCreatedCount_other": "{{count}} cases created",
|
|
331
331
|
"someRequestsFailed": "Some requests failed",
|
|
332
332
|
"retry": "Retry",
|
|
333
|
-
"retrying": "Retrying..."
|
|
333
|
+
"retrying": "Retrying...",
|
|
334
|
+
"switchProfile": "پروفائل تبدیل کریں",
|
|
335
|
+
"chooseYourProfile": "اپنی پروفائل منتخب کریں",
|
|
336
|
+
"emailLinkedToMultipleProfiles": "{{email}} ایک سے زیادہ پروفائلز سے منسلک ہے۔ جسے آپ دیکھنا چاہتے ہیں اسے منتخب کریں۔",
|
|
337
|
+
"contactLinkedToMultipleProfiles": "یہ رابطہ ایک سے زیادہ پروفائلز سے منسلک ہے۔ جسے آپ دیکھنا چاہتے ہیں اسے منتخب کریں۔",
|
|
338
|
+
"signingInEllipsis": "سائن ان ہو رہا ہے…",
|
|
339
|
+
"couldNotSwitchProfile": "اس پروفائل پر سوئچ نہیں کیا جا سکا۔ براہ کرم دوبارہ کوشش کریں۔"
|
|
334
340
|
}
|
|
@@ -664,6 +664,111 @@ export const injectPCStyles = () => {
|
|
|
664
664
|
color: var(--pc-text-secondary);
|
|
665
665
|
line-height: 1.25;
|
|
666
666
|
}
|
|
667
|
+
.pc-select-user {
|
|
668
|
+
display: flex;
|
|
669
|
+
flex-direction: column;
|
|
670
|
+
justify-content: center;
|
|
671
|
+
gap: var(--pc-spacing-xl);
|
|
672
|
+
width: 100%;
|
|
673
|
+
max-width: 460px;
|
|
674
|
+
margin: 0 auto;
|
|
675
|
+
min-height: min(62vh, 540px);
|
|
676
|
+
padding-top: var(--pc-spacing-2xl);
|
|
677
|
+
padding-bottom: var(--pc-spacing-2xl);
|
|
678
|
+
}
|
|
679
|
+
.pc-select-user__header {
|
|
680
|
+
display: flex;
|
|
681
|
+
flex-direction: column;
|
|
682
|
+
align-items: center;
|
|
683
|
+
text-align: center;
|
|
684
|
+
gap: var(--pc-spacing-sm);
|
|
685
|
+
}
|
|
686
|
+
.pc-select-user__header > * {
|
|
687
|
+
margin: 0;
|
|
688
|
+
}
|
|
689
|
+
.pc-select-user__list {
|
|
690
|
+
display: flex;
|
|
691
|
+
flex-direction: column;
|
|
692
|
+
gap: 0.625rem;
|
|
693
|
+
}
|
|
694
|
+
.pc-select-user__item {
|
|
695
|
+
display: flex;
|
|
696
|
+
align-items: center;
|
|
697
|
+
justify-content: space-between;
|
|
698
|
+
gap: 0.75rem;
|
|
699
|
+
width: 100%;
|
|
700
|
+
box-sizing: border-box;
|
|
701
|
+
padding: 0.75rem 1rem;
|
|
702
|
+
border: 1px solid var(--pc-border-color);
|
|
703
|
+
border-radius: 0.625rem;
|
|
704
|
+
background-color: var(--pc-bg-primary);
|
|
705
|
+
color: var(--pc-text-primary);
|
|
706
|
+
cursor: pointer;
|
|
707
|
+
text-align: left;
|
|
708
|
+
appearance: none;
|
|
709
|
+
-webkit-appearance: none;
|
|
710
|
+
font-family: inherit;
|
|
711
|
+
transition: border-color 0.2s ease, background-color 0.2s ease;
|
|
712
|
+
}
|
|
713
|
+
.pc-select-user__item:hover:not(:disabled) {
|
|
714
|
+
border-color: var(--pc-primary);
|
|
715
|
+
background-color: color-mix(in srgb, var(--pc-primary) 6%, var(--pc-bg-primary));
|
|
716
|
+
}
|
|
717
|
+
.pc-select-user__item:disabled {
|
|
718
|
+
cursor: default;
|
|
719
|
+
opacity: 0.7;
|
|
720
|
+
}
|
|
721
|
+
.pc-select-user__item:focus-visible {
|
|
722
|
+
outline: 2px solid var(--pc-border-color-focus);
|
|
723
|
+
outline-offset: 2px;
|
|
724
|
+
}
|
|
725
|
+
.pc-select-user__item-text {
|
|
726
|
+
display: flex;
|
|
727
|
+
flex-direction: column;
|
|
728
|
+
align-items: flex-start;
|
|
729
|
+
gap: 0.125rem;
|
|
730
|
+
min-width: 0;
|
|
731
|
+
}
|
|
732
|
+
.pc-select-user__item-primary {
|
|
733
|
+
font-size: 0.875rem;
|
|
734
|
+
font-weight: 600;
|
|
735
|
+
line-height: 1.25;
|
|
736
|
+
overflow: hidden;
|
|
737
|
+
text-overflow: ellipsis;
|
|
738
|
+
white-space: nowrap;
|
|
739
|
+
max-width: 100%;
|
|
740
|
+
}
|
|
741
|
+
.pc-select-user__item-secondary {
|
|
742
|
+
font-size: 0.75rem;
|
|
743
|
+
color: var(--pc-text-secondary);
|
|
744
|
+
line-height: 1.25;
|
|
745
|
+
overflow: hidden;
|
|
746
|
+
text-overflow: ellipsis;
|
|
747
|
+
white-space: nowrap;
|
|
748
|
+
max-width: 100%;
|
|
749
|
+
}
|
|
750
|
+
.pc-select-user__item-status {
|
|
751
|
+
font-size: 0.75rem;
|
|
752
|
+
font-weight: 600;
|
|
753
|
+
color: var(--pc-primary);
|
|
754
|
+
flex-shrink: 0;
|
|
755
|
+
}
|
|
756
|
+
.pc-select-user__item-chevron {
|
|
757
|
+
color: var(--pc-primary);
|
|
758
|
+
flex-shrink: 0;
|
|
759
|
+
}
|
|
760
|
+
.pc-select-user__error {
|
|
761
|
+
margin: 0.75rem 0 0;
|
|
762
|
+
font-size: 0.8125rem;
|
|
763
|
+
color: var(--pc-error);
|
|
764
|
+
text-align: center;
|
|
765
|
+
}
|
|
766
|
+
.pc-select-user-loading {
|
|
767
|
+
display: flex;
|
|
768
|
+
align-items: center;
|
|
769
|
+
justify-content: center;
|
|
770
|
+
min-height: 60vh;
|
|
771
|
+
}
|
|
667
772
|
.pc-product-purpose-panel {
|
|
668
773
|
display: flex;
|
|
669
774
|
flex-direction: column;
|
package/tests/Form.test.tsx
CHANGED
|
@@ -140,13 +140,16 @@ vi.mock('../src/RedactoPrivacyCenter/context/AuthContext', () => {
|
|
|
140
140
|
return { useAuth: () => auth };
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
});
|
|
143
|
+
// Mutable so individual tests can drop `ledgerBaseUrl` to exercise the legacy
|
|
144
|
+
// (no-ledger) mode; beforeEach restores the ledger default.
|
|
145
|
+
const baseUrlStub = vi.hoisted(() => ({
|
|
146
|
+
consentClient: { get: () => Promise.resolve(null), post: () => Promise.resolve(null) },
|
|
147
|
+
baseUrl: 'https://consent.test',
|
|
148
|
+
ledgerBaseUrl: 'https://ledger.test/consent-ledger' as string | undefined,
|
|
149
|
+
}));
|
|
150
|
+
vi.mock('../src/RedactoPrivacyCenter/context/BaseUrlContext', () => ({
|
|
151
|
+
useBaseUrl: () => baseUrlStub,
|
|
152
|
+
}));
|
|
150
153
|
|
|
151
154
|
vi.mock('../src/RedactoPrivacyCenter/context/OrganizationContext', () => {
|
|
152
155
|
const org = {
|
|
@@ -481,6 +484,8 @@ describe('Form (P2-SDK-3)', () => {
|
|
|
481
484
|
beforeEach(() => {
|
|
482
485
|
createCaseRequest.mockReset();
|
|
483
486
|
getFormData.mockReset();
|
|
487
|
+
fetchUserConsents.mockReset();
|
|
488
|
+
baseUrlStub.ledgerBaseUrl = 'https://ledger.test/consent-ledger';
|
|
484
489
|
sessionStorage.clear();
|
|
485
490
|
});
|
|
486
491
|
|
|
@@ -519,6 +524,47 @@ describe('Form (P2-SDK-3)', () => {
|
|
|
519
524
|
expect(submitButton.disabled).toBe(true);
|
|
520
525
|
});
|
|
521
526
|
|
|
527
|
+
it('skips the ledger picker overlay when no ledgerBaseUrl is configured', async () => {
|
|
528
|
+
// Without a ledger, consents still live in Python and the form/data
|
|
529
|
+
// groups are authoritative — the overlay must not fire a user-consents
|
|
530
|
+
// read (it would hit the legacy endpoint and mis-map its shape).
|
|
531
|
+
baseUrlStub.ledgerBaseUrl = undefined;
|
|
532
|
+
setupSession();
|
|
533
|
+
getFormData.mockResolvedValue(
|
|
534
|
+
makeFormData({
|
|
535
|
+
direct_groups: [
|
|
536
|
+
makeGroup(),
|
|
537
|
+
makeGroup({
|
|
538
|
+
product_uuid: 'prod-direct-2',
|
|
539
|
+
product_name: 'Acme Rewards',
|
|
540
|
+
purposes: [basePurpose('purp-2', 'Analytics')],
|
|
541
|
+
}),
|
|
542
|
+
],
|
|
543
|
+
}),
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
render(<Form onCreateRequest={vi.fn()} />);
|
|
547
|
+
await waitFor(() => expect(getFormData).toHaveBeenCalled());
|
|
548
|
+
|
|
549
|
+
pickRequestType(RequestTypeEnum.ERASURE);
|
|
550
|
+
await waitFor(() => {
|
|
551
|
+
expect(screen.getByText('Acme Loyalty')).toBeInTheDocument();
|
|
552
|
+
});
|
|
553
|
+
expect(fetchUserConsents).not.toHaveBeenCalled();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('runs the ledger picker overlay when ledgerBaseUrl is configured', async () => {
|
|
557
|
+
setupSession();
|
|
558
|
+
getFormData.mockResolvedValue(
|
|
559
|
+
makeFormData({ direct_groups: [makeGroup()] }),
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
render(<Form onCreateRequest={vi.fn()} />);
|
|
563
|
+
await waitFor(() => expect(getFormData).toHaveBeenCalled());
|
|
564
|
+
|
|
565
|
+
await waitFor(() => expect(fetchUserConsents).toHaveBeenCalled());
|
|
566
|
+
});
|
|
567
|
+
|
|
522
568
|
it('renders the product step for DSAR types when direct_groups is present', async () => {
|
|
523
569
|
setupSession();
|
|
524
570
|
const direct = makeGroup({
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { createConsentClient } from '../src/RedactoPrivacyCenter/api/client';
|
|
4
|
+
import type { BaseRequest } from '../src/RedactoPrivacyCenter/api/client';
|
|
5
|
+
|
|
6
|
+
// URL routing contract for the five consent read/write methods that moved to
|
|
7
|
+
// the Go ledger in PR #438. With a ledgerBaseUrl they must hit the ledger's
|
|
8
|
+
// /public/... paths; without one they must fall back to the legacy Python
|
|
9
|
+
// /dsar/privacy-center paths (prod has no ledger — see PR #438/#460 regression
|
|
10
|
+
// where the silent baseUrl fallback 404'd the Privacy Center).
|
|
11
|
+
|
|
12
|
+
const BASE_URL = 'https://consent.test';
|
|
13
|
+
const LEDGER_URL = 'https://ledger.test/consent-ledger';
|
|
14
|
+
const LEGACY_PREFIX =
|
|
15
|
+
'https://consent.test/organisations/org-1/workspaces/ws-1/dsar/privacy-center';
|
|
16
|
+
const LEDGER_PREFIX =
|
|
17
|
+
'https://ledger.test/consent-ledger/public/organisations/org-1/workspaces/ws-1';
|
|
18
|
+
|
|
19
|
+
const baseRequest: BaseRequest = {
|
|
20
|
+
organisationUuid: 'org-1',
|
|
21
|
+
workspaceUuid: 'ws-1',
|
|
22
|
+
authToken: 'jwt-token',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const jsonResponse = () =>
|
|
26
|
+
new Response(JSON.stringify({}), {
|
|
27
|
+
status: 200,
|
|
28
|
+
headers: { 'Content-Type': 'application/json' },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const pdfResponse = () =>
|
|
32
|
+
new Response(new Blob(['%PDF'], { type: 'application/pdf' }), {
|
|
33
|
+
status: 200,
|
|
34
|
+
headers: { 'Content-Type': 'application/pdf' },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const mockFetch = vi.fn();
|
|
38
|
+
|
|
39
|
+
const fetchedUrl = (): string => String(mockFetch.mock.calls[0]?.[0]);
|
|
40
|
+
|
|
41
|
+
const fetchedBody = (): Record<string, unknown> =>
|
|
42
|
+
JSON.parse(String(mockFetch.mock.calls[0]?.[1]?.body)) as Record<
|
|
43
|
+
string,
|
|
44
|
+
unknown
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
mockFetch.mockReset();
|
|
49
|
+
mockFetch.mockImplementation(() => Promise.resolve(jsonResponse()));
|
|
50
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.unstubAllGlobals();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('createConsentClient — legacy mode (no ledgerBaseUrl)', () => {
|
|
58
|
+
const client = createConsentClient(BASE_URL);
|
|
59
|
+
|
|
60
|
+
it('routes getUserConsents to the legacy dsar/privacy-center path', async () => {
|
|
61
|
+
await client.getUserConsents(baseRequest, { offset: 0, limit: 10 });
|
|
62
|
+
expect(fetchedUrl()).toBe(
|
|
63
|
+
`${LEGACY_PREFIX}/user-consents?offset=0&limit=10`,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('routes getGroupPurposes to the legacy dsar/privacy-center path', async () => {
|
|
68
|
+
await client.getGroupPurposes(baseRequest, 'prod-1', { limit: 5 });
|
|
69
|
+
expect(fetchedUrl()).toBe(
|
|
70
|
+
`${LEGACY_PREFIX}/user-consents/prod-1/purposes?limit=5`,
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('routes manageConsent to the legacy manage-consent endpoint with the legacy body and no noticeUuid requirement', async () => {
|
|
75
|
+
await client.manageConsent(
|
|
76
|
+
baseRequest,
|
|
77
|
+
'purp-1',
|
|
78
|
+
'jane@example.com',
|
|
79
|
+
'revoke',
|
|
80
|
+
undefined,
|
|
81
|
+
undefined,
|
|
82
|
+
'nominator@example.com',
|
|
83
|
+
['de-1'],
|
|
84
|
+
'prod-1',
|
|
85
|
+
);
|
|
86
|
+
expect(fetchedUrl()).toBe(`${LEGACY_PREFIX}/manage-consent?status=revoke`);
|
|
87
|
+
expect(fetchedBody()).toEqual({
|
|
88
|
+
purpose_uuid: 'purp-1',
|
|
89
|
+
contact: 'jane@example.com',
|
|
90
|
+
nominator_contact: 'nominator@example.com',
|
|
91
|
+
data_element_uuids: ['de-1'],
|
|
92
|
+
product_uuid: 'prod-1',
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('routes getReceipts to the legacy dsar/privacy-center path', async () => {
|
|
97
|
+
await client.getReceipts(baseRequest, { skip: 0, limit: 10 });
|
|
98
|
+
expect(fetchedUrl()).toBe(`${LEGACY_PREFIX}/receipts?skip=0&limit=10`);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('routes getReceiptPdf to the legacy /pdf path', async () => {
|
|
102
|
+
mockFetch.mockImplementation(() => Promise.resolve(pdfResponse()));
|
|
103
|
+
await client.getReceiptPdf(baseRequest, 'r-1');
|
|
104
|
+
expect(fetchedUrl()).toBe(`${LEGACY_PREFIX}/receipts/r-1/pdf`);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('createConsentClient — ledger mode (ledgerBaseUrl set)', () => {
|
|
109
|
+
// Trailing slash must be trimmed from the configured ledger base.
|
|
110
|
+
const client = createConsentClient(BASE_URL, `${LEDGER_URL}/`);
|
|
111
|
+
|
|
112
|
+
it('routes getUserConsents to the ledger public path', async () => {
|
|
113
|
+
await client.getUserConsents(baseRequest, { offset: 0, limit: 10 });
|
|
114
|
+
expect(fetchedUrl()).toBe(
|
|
115
|
+
`${LEDGER_PREFIX}/user-consents?offset=0&limit=10`,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('routes getGroupPurposes to the ledger public path', async () => {
|
|
120
|
+
await client.getGroupPurposes(baseRequest, 'prod-1', { limit: 5 });
|
|
121
|
+
expect(fetchedUrl()).toBe(
|
|
122
|
+
`${LEDGER_PREFIX}/user-consents/prod-1/purposes?limit=5`,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('routes manageConsent to the ledger submit-consent with a partial payload', async () => {
|
|
127
|
+
await client.manageConsent(
|
|
128
|
+
baseRequest,
|
|
129
|
+
'purp-1',
|
|
130
|
+
'jane@example.com',
|
|
131
|
+
'revoke',
|
|
132
|
+
undefined,
|
|
133
|
+
undefined,
|
|
134
|
+
undefined,
|
|
135
|
+
undefined,
|
|
136
|
+
undefined,
|
|
137
|
+
'notice-1',
|
|
138
|
+
[{ uuid: 'de-1', selected: true }],
|
|
139
|
+
);
|
|
140
|
+
expect(fetchedUrl()).toBe(`${LEDGER_PREFIX}/submit-consent`);
|
|
141
|
+
expect(fetchedBody()).toMatchObject({
|
|
142
|
+
notice_uuid: 'notice-1',
|
|
143
|
+
partial: true,
|
|
144
|
+
purposes: [
|
|
145
|
+
{
|
|
146
|
+
purpose_uuid: 'purp-1',
|
|
147
|
+
selected: false,
|
|
148
|
+
data_elements: [{ uuid: 'de-1', selected: false }],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('throws from manageConsent when noticeUuid is missing', async () => {
|
|
155
|
+
await expect(
|
|
156
|
+
client.manageConsent(baseRequest, 'purp-1', 'jane@example.com', 'revoke'),
|
|
157
|
+
).rejects.toThrow('notice_uuid is required');
|
|
158
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('routes getReceipts to the ledger public path', async () => {
|
|
162
|
+
await client.getReceipts(baseRequest, { skip: 0, limit: 10 });
|
|
163
|
+
expect(fetchedUrl()).toBe(`${LEDGER_PREFIX}/receipts?skip=0&limit=10`);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('routes getReceiptPdf to the ledger /download path', async () => {
|
|
167
|
+
mockFetch.mockImplementation(() => Promise.resolve(pdfResponse()));
|
|
168
|
+
await client.getReceiptPdf(baseRequest, 'r-1');
|
|
169
|
+
expect(fetchedUrl()).toBe(`${LEDGER_PREFIX}/receipts/r-1/download`);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { manageConsentServer } from '../src/RedactoPrivacyCenter/api/actions';
|
|
4
|
+
import type { createConsentClient } from '../src/RedactoPrivacyCenter/api/client';
|
|
5
|
+
|
|
6
|
+
// manageConsentServer must honor the legacy Python response's `success` flag
|
|
7
|
+
// (a 200 with { success: false } is a failure) while still treating the Go
|
|
8
|
+
// ledger's ConsentEventResponse (no `success` key) as resolved-on-2xx.
|
|
9
|
+
|
|
10
|
+
type ConsentClient = ReturnType<typeof createConsentClient>;
|
|
11
|
+
|
|
12
|
+
const makeParams = (result: unknown) => ({
|
|
13
|
+
organizationId: 'org-1',
|
|
14
|
+
workspaceId: 'ws-1',
|
|
15
|
+
purposeId: 'purp-1',
|
|
16
|
+
contact: 'jane@example.com',
|
|
17
|
+
action: 'revoke' as const,
|
|
18
|
+
authToken: 'jwt-token',
|
|
19
|
+
consentClient: {
|
|
20
|
+
manageConsent: vi.fn().mockResolvedValue(result),
|
|
21
|
+
} as unknown as ConsentClient,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('manageConsentServer success detection', () => {
|
|
25
|
+
it('returns false for a legacy { success: false } response', async () => {
|
|
26
|
+
await expect(manageConsentServer(makeParams({ success: false }))).resolves.toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns true for a legacy { success: true } response', async () => {
|
|
30
|
+
await expect(manageConsentServer(makeParams({ success: true }))).resolves.toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns true for a ledger ConsentEventResponse (no success flag)', async () => {
|
|
34
|
+
await expect(
|
|
35
|
+
manageConsentServer(makeParams({ consent_event_uuid: 'evt-1' })),
|
|
36
|
+
).resolves.toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|