@startinblox/components-ds4go 3.3.8 → 4.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/dist/index.js +2702 -3362
- package/locales/en.xlf +129 -3
- package/package.json +2 -2
- package/src/components/cards/ds4go-card-dataspace-catalog.ts +82 -227
- package/src/components/catalog/ds4go-catalog-data-holder.ts +158 -0
- package/src/components/modal/catalog-modal/agreement-info.ts +110 -0
- package/src/components/modal/catalog-modal/index.ts +4 -0
- package/src/components/modal/catalog-modal/negotiation-button.ts +111 -0
- package/src/components/modal/catalog-modal/policy-display.ts +66 -0
- package/src/components/modal/catalog-modal/policy-selection.ts +71 -0
- package/src/components/modal/ds4go-catalog-modal.ts +158 -1105
- package/src/components/odrl/policy-composer.ts +1 -1
- package/src/components/odrl-policy-viewer.ts +0 -21
- package/src/components/solid-dsp-catalog.ts +2 -43
- package/src/ds4go.d.ts +78 -1
- package/src/helpers/dsp/agreementStorage.ts +243 -0
- package/src/helpers/dsp/policyHelpers.ts +223 -0
- package/src/helpers/index.ts +7 -0
- package/src/styles/cards/ds4go-card-catalog.scss +1 -1
- package/src/styles/cards/ds4go-card-dataspace-catalog.scss +22 -165
- package/src/styles/modal/ds4go-catalog-modal.scss +1 -1
- package/src/components/modal/ds4go-catalog-data-holder.ts +0 -349
|
@@ -930,7 +930,7 @@ export class PolicyComposer extends LitElement {
|
|
|
930
930
|
</fieldset>
|
|
931
931
|
<div class="flex align-right spacer-top">
|
|
932
932
|
<tems-button type="outline-gray" size="sm" @click=${this._resetPolicy}>
|
|
933
|
-
${msg(`Reset to ${this.template.name} default`)}
|
|
933
|
+
${msg(str`Reset to ${this.template.name} default`)}
|
|
934
934
|
</tems-button>
|
|
935
935
|
</div>
|
|
936
936
|
</form>`;
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ODRL Policy Viewer Component
|
|
3
|
-
*
|
|
4
|
-
* A Lit web component for displaying ODRL policies in a user-friendly format.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* ```html
|
|
8
|
-
* <odrl-policy-viewer .policy=${policyObject}></odrl-policy-viewer>
|
|
9
|
-
* ```
|
|
10
|
-
*
|
|
11
|
-
* Attributes:
|
|
12
|
-
* - policy: OdrlPolicy object to display
|
|
13
|
-
* - compact: boolean (optional) - Show compact view
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
1
|
import {
|
|
17
2
|
type OdrlPolicy,
|
|
18
3
|
OdrlPolicyRenderer,
|
|
@@ -171,9 +156,3 @@ export class OdrlPolicyViewer extends LitElement {
|
|
|
171
156
|
}
|
|
172
157
|
}
|
|
173
158
|
}
|
|
174
|
-
|
|
175
|
-
declare global {
|
|
176
|
-
interface HTMLElementTagNameMap {
|
|
177
|
-
"odrl-policy-viewer": OdrlPolicyViewer;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as utils from "@helpers";
|
|
2
|
-
import { msg, str } from "@lit/localize";
|
|
3
2
|
import { Task } from "@lit/task";
|
|
4
3
|
import type {
|
|
5
4
|
Resource,
|
|
@@ -12,13 +11,6 @@ import {
|
|
|
12
11
|
import { css, html, nothing } from "lit";
|
|
13
12
|
import { customElement, property, state } from "lit/decorators.js";
|
|
14
13
|
|
|
15
|
-
export interface DSPProviderConfig {
|
|
16
|
-
name: string;
|
|
17
|
-
address: string;
|
|
18
|
-
color?: string;
|
|
19
|
-
participantId?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
14
|
@customElement("solid-dsp-catalog")
|
|
23
15
|
export class DSPCatalog extends OrbitDSPComponent {
|
|
24
16
|
constructor() {
|
|
@@ -175,40 +167,7 @@ export class DSPCatalog extends OrbitDSPComponent {
|
|
|
175
167
|
return html`<tems-viewport>
|
|
176
168
|
<tems-header slot="header" heading=${this.header}></tems-header>
|
|
177
169
|
<div slot="content">
|
|
178
|
-
|
|
179
|
-
? html`<div
|
|
180
|
-
style="background: white; padding: 1rem; border-radius: 8px; margin-bottom: 1rem;"
|
|
181
|
-
>
|
|
182
|
-
<h3 style="margin: 0 0 1rem 0;">
|
|
183
|
-
${msg("Provider Statistics")}
|
|
184
|
-
</h3>
|
|
185
|
-
<div
|
|
186
|
-
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;"
|
|
187
|
-
>
|
|
188
|
-
${this.providers.map((provider: DSPProviderConfig) => {
|
|
189
|
-
const providerDatasets = datas.filter(
|
|
190
|
-
(d: Resource) => d._provider === provider.name,
|
|
191
|
-
);
|
|
192
|
-
return html`
|
|
193
|
-
<div
|
|
194
|
-
style="background: #f5f5f5; padding: 1rem; border-radius: 4px; border-left: 4px solid ${provider.color ||
|
|
195
|
-
"#1976d2"};"
|
|
196
|
-
>
|
|
197
|
-
<div
|
|
198
|
-
style="font-weight: bold; margin-bottom: 0.5rem;"
|
|
199
|
-
>
|
|
200
|
-
${provider.name}
|
|
201
|
-
</div>
|
|
202
|
-
<div style="font-size: 1.125rem; color: #1976d2;">
|
|
203
|
-
${msg(str`${providerDatasets.length} datasets`)}
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
`;
|
|
207
|
-
})}
|
|
208
|
-
</div>
|
|
209
|
-
</div>`
|
|
210
|
-
: nothing}
|
|
211
|
-
<tems-catalog-filter-holder
|
|
170
|
+
<ds4go-catalog-filter-holder
|
|
212
171
|
.displayFiltering=${false}
|
|
213
172
|
@search=${this._search}
|
|
214
173
|
.search=${this.search}
|
|
@@ -216,7 +175,7 @@ export class DSPCatalog extends OrbitDSPComponent {
|
|
|
216
175
|
.objects=${datas}
|
|
217
176
|
.resultCount=${this.resultCount}
|
|
218
177
|
.filterCount=${this.filterCount}
|
|
219
|
-
></
|
|
178
|
+
></ds4go-catalog-filter-holder>
|
|
220
179
|
<ds4go-catalog-data-holder
|
|
221
180
|
.view=${"card"}
|
|
222
181
|
.objects=${datas}
|
package/src/ds4go.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { LimitedResource } from "@src/component";
|
|
2
2
|
|
|
3
3
|
export interface FactBundle extends LimitedResource {
|
|
4
|
-
"@type"?:
|
|
4
|
+
"@type"?:
|
|
5
|
+
| "ds4go:FactBundle"
|
|
6
|
+
| "ldp:Container"
|
|
7
|
+
| ["ldp:Container", "ds4go:FactBundle"];
|
|
5
8
|
name?: string;
|
|
6
9
|
category?: string;
|
|
7
10
|
description?: string;
|
|
@@ -43,3 +46,77 @@ export interface Media extends LimitedResource {
|
|
|
43
46
|
file_type?: string;
|
|
44
47
|
description?: string;
|
|
45
48
|
}
|
|
49
|
+
|
|
50
|
+
// ODRL Policy types
|
|
51
|
+
export interface ODRLPolicy {
|
|
52
|
+
"@id"?: string;
|
|
53
|
+
"@type"?: string;
|
|
54
|
+
"odrl:permission"?: ODRLPermission | ODRLPermission[];
|
|
55
|
+
"odrl:prohibition"?: ODRLProhibition | ODRLProhibition[];
|
|
56
|
+
"odrl:obligation"?: ODRLDuty | ODRLDuty[];
|
|
57
|
+
target?: string;
|
|
58
|
+
assigner?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ODRLPermission {
|
|
62
|
+
"odrl:action"?: ODRLAction | string;
|
|
63
|
+
"odrl:constraint"?: ODRLConstraint | ODRLConstraint[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ODRLProhibition {
|
|
67
|
+
"odrl:action"?: ODRLAction | string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ODRLDuty {
|
|
71
|
+
"odrl:action"?: ODRLAction | string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ODRLAction {
|
|
75
|
+
"@id"?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ODRLConstraint {
|
|
79
|
+
"odrl:leftOperand"?: string;
|
|
80
|
+
"odrl:operator"?: string;
|
|
81
|
+
"odrl:rightOperand"?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface DcatDistribution {
|
|
85
|
+
"@type": string;
|
|
86
|
+
"dct:format": DctFormat;
|
|
87
|
+
"dcat:accessService": DcatAccessService;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DSPProviderConfig {
|
|
91
|
+
name: string;
|
|
92
|
+
address: string;
|
|
93
|
+
color?: string;
|
|
94
|
+
participantId?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface DSPOffer extends LimitedResource {
|
|
98
|
+
"@id"?: string;
|
|
99
|
+
"@type"?: "dcat:Dataset" | "tems:Service";
|
|
100
|
+
"odrl:hasPolicy"?: ODRLPolicy;
|
|
101
|
+
"dcat:distribution"?: DcatDistribution[];
|
|
102
|
+
category?: string;
|
|
103
|
+
"dcat:endpointUrl"?: string;
|
|
104
|
+
name?: string;
|
|
105
|
+
pricingTier?: string;
|
|
106
|
+
description?: string;
|
|
107
|
+
"dct:language"?: string;
|
|
108
|
+
id?: string;
|
|
109
|
+
contenttype?: string;
|
|
110
|
+
bundleSize?: number;
|
|
111
|
+
previewLinks?: string;
|
|
112
|
+
_provider?: DSPProviderConfig;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface AgreementInfo {
|
|
116
|
+
contractId: string;
|
|
117
|
+
negotiationId: string;
|
|
118
|
+
timestamp: number;
|
|
119
|
+
assetId: string;
|
|
120
|
+
providerId: string;
|
|
121
|
+
providerAddress: string;
|
|
122
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { DSPContractStorage } from "@startinblox/solid-tems-shared";
|
|
2
|
+
import type { AgreementInfo, DSPOffer } from "@src/ds4go";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get localStorage key for an asset
|
|
6
|
+
* Uses combination of provider ID and dataset ID for uniqueness across providers
|
|
7
|
+
*/
|
|
8
|
+
export function getStorageKey(offer: DSPOffer): string {
|
|
9
|
+
const datasetId = offer["@id"] || offer.id;
|
|
10
|
+
const providerId = offer._provider?.participantId || "";
|
|
11
|
+
|
|
12
|
+
if (!datasetId) return "";
|
|
13
|
+
|
|
14
|
+
// Create composite key: provider-assetId
|
|
15
|
+
const key = providerId
|
|
16
|
+
? `dsp-agreement-${providerId}-${datasetId}`
|
|
17
|
+
: `dsp-agreement-${datasetId}`;
|
|
18
|
+
return key;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Save agreement info to localStorage
|
|
23
|
+
*/
|
|
24
|
+
export function saveAgreementInfo(
|
|
25
|
+
offer: DSPOffer,
|
|
26
|
+
contractId: string,
|
|
27
|
+
negotiationId: string,
|
|
28
|
+
timestamp: number,
|
|
29
|
+
): void {
|
|
30
|
+
const key = getStorageKey(offer);
|
|
31
|
+
if (!key) return;
|
|
32
|
+
|
|
33
|
+
const agreementInfo: AgreementInfo = {
|
|
34
|
+
contractId,
|
|
35
|
+
negotiationId,
|
|
36
|
+
timestamp,
|
|
37
|
+
assetId: offer["@id"] || offer.id || "",
|
|
38
|
+
providerId: offer._provider?.participantId || "",
|
|
39
|
+
providerAddress: offer._provider?.address || "",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
localStorage.setItem(key, JSON.stringify(agreementInfo));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load agreement info from localStorage
|
|
47
|
+
*/
|
|
48
|
+
export function loadAgreementInfo(offer: DSPOffer): AgreementInfo | null {
|
|
49
|
+
const key = getStorageKey(offer);
|
|
50
|
+
if (!key) return null;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const stored = localStorage.getItem(key);
|
|
54
|
+
if (stored) {
|
|
55
|
+
const info = JSON.parse(stored) as AgreementInfo;
|
|
56
|
+
return info;
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Failed to load agreement info:", error);
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Clear agreement info from localStorage
|
|
66
|
+
*/
|
|
67
|
+
export function clearAgreementInfo(offer: DSPOffer): void {
|
|
68
|
+
const key = getStorageKey(offer);
|
|
69
|
+
if (key) {
|
|
70
|
+
localStorage.removeItem(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Save initial contract state when negotiation starts
|
|
76
|
+
*/
|
|
77
|
+
export function saveInitialContractState(
|
|
78
|
+
offer: DSPOffer,
|
|
79
|
+
negotiationId: string,
|
|
80
|
+
): void {
|
|
81
|
+
try {
|
|
82
|
+
const providerId = offer._provider?.participantId || "";
|
|
83
|
+
const assetId = offer["@id"] || offer.id;
|
|
84
|
+
|
|
85
|
+
if (!assetId) return;
|
|
86
|
+
|
|
87
|
+
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
88
|
+
assetId,
|
|
89
|
+
providerId,
|
|
90
|
+
);
|
|
91
|
+
const existingContract = existingContracts.find(
|
|
92
|
+
(c) => c.contractId === negotiationId,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (!existingContract) {
|
|
96
|
+
// Extract index endpoint URL from asset (dcat:endpointUrl)
|
|
97
|
+
const indexEndpointUrl = offer["dcat:endpointUrl"];
|
|
98
|
+
|
|
99
|
+
const assetName = offer.name || assetId || "Unknown Asset";
|
|
100
|
+
|
|
101
|
+
// Detect if this is an index asset
|
|
102
|
+
const isIndexAsset = assetName.toLowerCase().includes("index");
|
|
103
|
+
|
|
104
|
+
// Create new contract in REQUESTED state
|
|
105
|
+
DSPContractStorage.create({
|
|
106
|
+
assetId,
|
|
107
|
+
datasetId: offer["@id"] || offer.id || "",
|
|
108
|
+
assetName,
|
|
109
|
+
assetDescription: offer.description,
|
|
110
|
+
providerName: offer._provider?.name || "Unknown Provider",
|
|
111
|
+
providerAddress: offer._provider?.address || "",
|
|
112
|
+
providerParticipantId: offer._provider?.participantId || "",
|
|
113
|
+
providerColor: offer._provider?.color,
|
|
114
|
+
policy: offer["odrl:hasPolicy"],
|
|
115
|
+
state: "REQUESTED",
|
|
116
|
+
contractId: negotiationId,
|
|
117
|
+
// Index-specific fields
|
|
118
|
+
isIndexAsset,
|
|
119
|
+
indexEndpointUrl,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(
|
|
124
|
+
"[DSP Contract Catalog] Failed to save initial contract state:",
|
|
125
|
+
error,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Save contract to DSP Contract Catalog for history tracking
|
|
132
|
+
*/
|
|
133
|
+
export function saveToContractCatalog(
|
|
134
|
+
offer: DSPOffer,
|
|
135
|
+
contractId: string,
|
|
136
|
+
negotiationId: string,
|
|
137
|
+
): void {
|
|
138
|
+
try {
|
|
139
|
+
const providerId = offer._provider?.participantId || "";
|
|
140
|
+
const assetId = offer["@id"] || offer.id;
|
|
141
|
+
|
|
142
|
+
if (!assetId) return;
|
|
143
|
+
|
|
144
|
+
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
145
|
+
assetId,
|
|
146
|
+
providerId,
|
|
147
|
+
);
|
|
148
|
+
const existingContract = existingContracts.find(
|
|
149
|
+
(c) => c.contractId === negotiationId || c.agreementId === contractId,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Extract index endpoint URL from asset (dcat:endpointUrl)
|
|
153
|
+
const indexEndpointUrl = offer["dcat:endpointUrl"];
|
|
154
|
+
|
|
155
|
+
const assetName = offer.name || assetId || "Unknown Asset";
|
|
156
|
+
|
|
157
|
+
// Detect if this is an index asset
|
|
158
|
+
const isIndexAsset = assetName.toLowerCase().includes("index");
|
|
159
|
+
|
|
160
|
+
if (existingContract) {
|
|
161
|
+
// Update existing contract with index metadata
|
|
162
|
+
DSPContractStorage.updateState(existingContract.id, "FINALIZED", {
|
|
163
|
+
agreementId: contractId,
|
|
164
|
+
contractId: negotiationId,
|
|
165
|
+
isIndexAsset,
|
|
166
|
+
indexEndpointUrl,
|
|
167
|
+
});
|
|
168
|
+
} else {
|
|
169
|
+
// Create new contract entry
|
|
170
|
+
DSPContractStorage.create({
|
|
171
|
+
assetId,
|
|
172
|
+
datasetId: offer["@id"] || offer.id || "",
|
|
173
|
+
assetName,
|
|
174
|
+
assetDescription: offer.description,
|
|
175
|
+
providerName: offer._provider?.name || "Unknown Provider",
|
|
176
|
+
providerAddress: offer._provider?.address || "",
|
|
177
|
+
providerParticipantId: offer._provider?.participantId || "",
|
|
178
|
+
providerColor: offer._provider?.color,
|
|
179
|
+
policy: offer["odrl:hasPolicy"],
|
|
180
|
+
state: "FINALIZED",
|
|
181
|
+
contractId: negotiationId,
|
|
182
|
+
agreementId: contractId,
|
|
183
|
+
// Index-specific fields
|
|
184
|
+
isIndexAsset,
|
|
185
|
+
indexEndpointUrl,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error("[DSP Contract Catalog] Failed to save contract:", error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Update contract state in catalog (for failures)
|
|
195
|
+
*/
|
|
196
|
+
export function updateContractState(
|
|
197
|
+
offer: DSPOffer,
|
|
198
|
+
negotiationId: string,
|
|
199
|
+
state: "FAILED" | "TERMINATED",
|
|
200
|
+
error?: string,
|
|
201
|
+
): void {
|
|
202
|
+
try {
|
|
203
|
+
const providerId = offer._provider?.participantId || "";
|
|
204
|
+
const assetId = offer["@id"] || offer.id;
|
|
205
|
+
|
|
206
|
+
if (!assetId) return;
|
|
207
|
+
|
|
208
|
+
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
209
|
+
assetId,
|
|
210
|
+
providerId,
|
|
211
|
+
);
|
|
212
|
+
const existingContract = existingContracts.find(
|
|
213
|
+
(c) => c.contractId === negotiationId,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
if (existingContract) {
|
|
217
|
+
DSPContractStorage.updateState(existingContract.id, state, { error });
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error(
|
|
221
|
+
"[DSP Contract Catalog] Failed to update contract state:",
|
|
222
|
+
error,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Delete all contracts for a specific asset and provider
|
|
229
|
+
*/
|
|
230
|
+
export function deleteContractsForAsset(offer: DSPOffer): void {
|
|
231
|
+
const assetId = offer?.["@id"] || offer?.id;
|
|
232
|
+
const providerId = offer?._provider?.participantId || "";
|
|
233
|
+
|
|
234
|
+
if (assetId) {
|
|
235
|
+
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
236
|
+
assetId,
|
|
237
|
+
providerId,
|
|
238
|
+
);
|
|
239
|
+
for (const contract of existingContracts) {
|
|
240
|
+
DSPContractStorage.delete(contract.id);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { ODRLPolicy } from "@src/ds4go";
|
|
2
|
+
import { html, nothing } from "lit";
|
|
3
|
+
import { msg, str } from "@lit/localize";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extract policies from various formats (single policy, array, or object with numeric keys)
|
|
7
|
+
*/
|
|
8
|
+
export function extractPolicies(
|
|
9
|
+
policy: ODRLPolicy | ODRLPolicy[] | Record<string, any> | undefined,
|
|
10
|
+
): { policies: ODRLPolicy[]; defaultPolicy: ODRLPolicy | undefined } {
|
|
11
|
+
let policies: ODRLPolicy[] = [];
|
|
12
|
+
let defaultPolicy: ODRLPolicy | undefined;
|
|
13
|
+
|
|
14
|
+
if (!policy) {
|
|
15
|
+
return { policies: [], defaultPolicy: undefined };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Handle array of policies
|
|
19
|
+
if (Array.isArray(policy)) {
|
|
20
|
+
const target = (policy as any).target;
|
|
21
|
+
policies = policy.filter(
|
|
22
|
+
(item: any) => item && typeof item === "object" && item["@id"],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Add target to each policy if it exists and policy doesn't have one
|
|
26
|
+
if (target) {
|
|
27
|
+
policies = policies.map((p: any) => {
|
|
28
|
+
if (!p.target && !p["odrl:target"]) {
|
|
29
|
+
return { ...p, target, "odrl:target": target };
|
|
30
|
+
}
|
|
31
|
+
return p;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
defaultPolicy = policies.length > 0 ? policies[0] : undefined;
|
|
36
|
+
}
|
|
37
|
+
// Handle object with numeric keys (array-like object)
|
|
38
|
+
else if (typeof policy === "object" && policy !== null) {
|
|
39
|
+
const keys = Object.keys(policy);
|
|
40
|
+
const hasNumericKeys = keys.some((k) => /^\d+$/.test(k));
|
|
41
|
+
|
|
42
|
+
if (hasNumericKeys) {
|
|
43
|
+
const target = (policy as any).target;
|
|
44
|
+
const extractedPolicies: ODRLPolicy[] = [];
|
|
45
|
+
|
|
46
|
+
for (const key in policy) {
|
|
47
|
+
if (/^\d+$/.test(key)) {
|
|
48
|
+
let p = (policy as any)[key];
|
|
49
|
+
// Add target if it exists and policy doesn't have one
|
|
50
|
+
if (target && !p.target && !p["odrl:target"]) {
|
|
51
|
+
p = { ...p, target, "odrl:target": target };
|
|
52
|
+
}
|
|
53
|
+
extractedPolicies.push(p);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
policies = extractedPolicies;
|
|
58
|
+
defaultPolicy = policies.length > 0 ? policies[0] : undefined;
|
|
59
|
+
} else {
|
|
60
|
+
// Single policy object
|
|
61
|
+
defaultPolicy = policy as ODRLPolicy;
|
|
62
|
+
policies = [defaultPolicy];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { policies, defaultPolicy };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validate that a policy doesn't have numeric keys (defensive check)
|
|
71
|
+
*/
|
|
72
|
+
export function validatePolicyStructure(
|
|
73
|
+
policy: ODRLPolicy | undefined,
|
|
74
|
+
): boolean {
|
|
75
|
+
if (!policy || typeof policy !== "object") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const policyKeys = Object.keys(policy);
|
|
80
|
+
const hasNumericKeys = policyKeys.some((k) => /^\d+$/.test(k));
|
|
81
|
+
|
|
82
|
+
if (hasNumericKeys) {
|
|
83
|
+
console.error(
|
|
84
|
+
"[policyHelpers] ERROR: Policy has numeric keys! Invalid structure.",
|
|
85
|
+
policy,
|
|
86
|
+
);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get policy by index from policies array
|
|
95
|
+
*/
|
|
96
|
+
export function getPolicyByIndex(
|
|
97
|
+
policies: ODRLPolicy[],
|
|
98
|
+
index: number | undefined,
|
|
99
|
+
): ODRLPolicy | undefined {
|
|
100
|
+
if (index === undefined || !policies || policies.length === 0) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
return policies[index];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if there are multiple policies available
|
|
108
|
+
*/
|
|
109
|
+
export function hasMultiplePolicies(policies: ODRLPolicy[]): boolean {
|
|
110
|
+
return policies && policies.length > 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Format policy details for display
|
|
115
|
+
*/
|
|
116
|
+
export function formatPolicyDetails(
|
|
117
|
+
policy: ODRLPolicy | undefined,
|
|
118
|
+
): ReturnType<typeof html> {
|
|
119
|
+
if (!policy) return html`${msg("No policy details available")}`;
|
|
120
|
+
|
|
121
|
+
const permissions = policy["odrl:permission"];
|
|
122
|
+
const prohibitions = policy["odrl:prohibition"];
|
|
123
|
+
const obligations = policy["odrl:obligation"];
|
|
124
|
+
|
|
125
|
+
const permArray = permissions
|
|
126
|
+
? Array.isArray(permissions)
|
|
127
|
+
? permissions
|
|
128
|
+
: [permissions]
|
|
129
|
+
: [];
|
|
130
|
+
const prohibArray = prohibitions
|
|
131
|
+
? Array.isArray(prohibitions)
|
|
132
|
+
? prohibitions
|
|
133
|
+
: [prohibitions]
|
|
134
|
+
: [];
|
|
135
|
+
const obligArray = obligations
|
|
136
|
+
? Array.isArray(obligations)
|
|
137
|
+
? obligations
|
|
138
|
+
: [obligations]
|
|
139
|
+
: [];
|
|
140
|
+
|
|
141
|
+
return html`${policy["@id"]
|
|
142
|
+
? html`<div class="policy-detail">
|
|
143
|
+
<strong>${msg("Policy ID:")}:</strong> ${policy["@id"]}
|
|
144
|
+
</div>`
|
|
145
|
+
: nothing}
|
|
146
|
+
${policy["@type"]
|
|
147
|
+
? html`<div class="policy-detail">
|
|
148
|
+
<strong>${msg("Type:")}:</strong> ${policy["@type"]}
|
|
149
|
+
</div>`
|
|
150
|
+
: nothing}
|
|
151
|
+
${permArray.length > 0
|
|
152
|
+
? html`<div class="policy-detail">
|
|
153
|
+
<strong>${msg("Permissions:")}:</strong>
|
|
154
|
+
<ul>
|
|
155
|
+
${permArray.map(
|
|
156
|
+
(perm: any) => html`
|
|
157
|
+
<li>
|
|
158
|
+
${msg(
|
|
159
|
+
str`Action: ${perm["odrl:action"]?.["@id"] || perm["odrl:action"] || msg("use")}`,
|
|
160
|
+
)}
|
|
161
|
+
</li>
|
|
162
|
+
${perm["odrl:constraint"]
|
|
163
|
+
? html`${(Array.isArray(perm["odrl:constraint"])
|
|
164
|
+
? perm["odrl:constraint"]
|
|
165
|
+
: [perm["odrl:constraint"]]
|
|
166
|
+
).map(
|
|
167
|
+
(c: any) => html`
|
|
168
|
+
<li style="margin-left: 20px;">
|
|
169
|
+
${msg(
|
|
170
|
+
str`Constraint: ${c["odrl:leftOperand"]} ${c["odrl:operator"]} ${c["odrl:rightOperand"]}`,
|
|
171
|
+
)}
|
|
172
|
+
</li>
|
|
173
|
+
`,
|
|
174
|
+
)}`
|
|
175
|
+
: nothing}
|
|
176
|
+
`,
|
|
177
|
+
)}
|
|
178
|
+
</ul>
|
|
179
|
+
</div>`
|
|
180
|
+
: nothing}
|
|
181
|
+
${prohibArray.length > 0
|
|
182
|
+
? html`<div class="policy-detail">
|
|
183
|
+
<strong>${msg("Prohibitions:")}:</strong>
|
|
184
|
+
<ul>
|
|
185
|
+
${prohibArray.map(
|
|
186
|
+
(prohib: any) => html`
|
|
187
|
+
<li>
|
|
188
|
+
${msg(
|
|
189
|
+
str`Action: ${prohib["odrl:action"]?.["@id"] || prohib["odrl:action"] || msg("unknown")}`,
|
|
190
|
+
)}
|
|
191
|
+
</li>
|
|
192
|
+
`,
|
|
193
|
+
)}
|
|
194
|
+
</ul>
|
|
195
|
+
</div>`
|
|
196
|
+
: nothing}
|
|
197
|
+
${obligArray.length > 0
|
|
198
|
+
? html`<div class="policy-detail">
|
|
199
|
+
<strong>${msg("Obligations:")}:</strong>
|
|
200
|
+
<ul>
|
|
201
|
+
${obligArray.map(
|
|
202
|
+
(oblig: any) => html`
|
|
203
|
+
<li>
|
|
204
|
+
${msg(
|
|
205
|
+
str`Action: ${oblig["odrl:action"]?.["@id"] || oblig["odrl:action"] || msg("unknown")}`,
|
|
206
|
+
)}
|
|
207
|
+
</li>
|
|
208
|
+
`,
|
|
209
|
+
)}
|
|
210
|
+
</ul>
|
|
211
|
+
</div>`
|
|
212
|
+
: nothing}
|
|
213
|
+
${policy.target
|
|
214
|
+
? html`<div class="policy-detail">
|
|
215
|
+
<strong>${msg("Target Asset:")}:</strong> ${policy.target}
|
|
216
|
+
</div>`
|
|
217
|
+
: nothing}
|
|
218
|
+
${policy.assigner
|
|
219
|
+
? html`<div class="policy-detail">
|
|
220
|
+
<strong>${msg("Assigner:")}:</strong> ${policy.assigner}
|
|
221
|
+
</div>`
|
|
222
|
+
: nothing}`;
|
|
223
|
+
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -21,6 +21,10 @@ import requestNavigation from "@helpers/utils/requestNavigation";
|
|
|
21
21
|
import uniq from "@helpers/utils/uniq";
|
|
22
22
|
import CLIENT_CONTEXT from "@src/context.json";
|
|
23
23
|
|
|
24
|
+
// DSP helpers
|
|
25
|
+
import * as dspAgreementStorage from "@helpers/dsp/agreementStorage";
|
|
26
|
+
import * as dspPolicyHelpers from "@helpers/dsp/policyHelpers";
|
|
27
|
+
|
|
24
28
|
export {
|
|
25
29
|
CLIENT_CONTEXT,
|
|
26
30
|
filterGenerator,
|
|
@@ -44,4 +48,7 @@ export {
|
|
|
44
48
|
setupOnSaveReset,
|
|
45
49
|
sort,
|
|
46
50
|
uniq,
|
|
51
|
+
// DSP helpers
|
|
52
|
+
dspAgreementStorage,
|
|
53
|
+
dspPolicyHelpers,
|
|
47
54
|
};
|