Skip to content

Commit e39764d

Browse files
authored
Merge branch 'main' into renovate/fundamental-ngx-and-angular
2 parents 9d2ad51 + be0b5b6 commit e39764d

23 files changed

+584
-263
lines changed

docs/readme-generic-ui.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ Each field definition supports the following properties:
5252

5353
- `"label"`: Display name for the field
5454
- `"property"`: JSON path to the resource property (string or array of strings for fallback values)
55+
- `"propertyField"`: In case the property is a scalar value that represents an object, this property can be used to specify the field to be used for display within that object
56+
- `"key"`: The name of the field to be used for display
57+
- `"transform"`: An array of text manipulations to be applied to the value, the available are:
58+
| 'uppercase'
59+
| 'lowercase'
60+
| 'capitalize'
61+
| 'decode'
62+
| 'encode'
5563
- `"jsonPathExpression"`: Alternative JSONPath expression for complex data access (takes precedence over `property`)
5664
- `"required"`: Boolean flag indicating if the field is mandatory (for create views)
5765
- `"values"`: Array of predefined values for selection
@@ -106,7 +114,7 @@ Below is an example content-configuration for an accounts node using the generic
106114
"namespace": null,
107115
"readyCondition": {
108116
"jsonPathExpression": "status.conditions[?(@.type=='Ready' && @.status=='True')]",
109-
"property": ["status.conditions.status", "status.conditions.type"],
117+
"property": ["status.conditions.status", "status.conditions.type"]
110118
},
111119
"ui": {
112120
"logoUrl": "https://www.kcp.io/icons/logo.svg",
@@ -120,6 +128,14 @@ Below is an example content-configuration for an accounts node using the generic
120128
"label": "Display Name",
121129
"property": "spec.displayName"
122130
},
131+
{
132+
"label": "Key",
133+
"property": "data",
134+
"propertyField": {
135+
"key": "OPENAI_API_KEY",
136+
"transform": ["uppercase", "encode"]
137+
}
138+
},
123139
{
124140
"label": "Type",
125141
"property": "spec.type",
@@ -213,7 +229,7 @@ Below is an example content-configuration for an accounts node using the generic
213229
"operation": "cities",
214230
"gqlQuery": "subscription { cities { data { id name } } }",
215231
"value": "data.id",
216-
"key": "data.name",
232+
"key": "data.name"
217233
}
218234
}
219235
]

package-lock.json

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

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@platform-mesh/portal-ui-lib",
3-
"version": "0.18.8",
3+
"version": "0.18.13",
44
"repository": {
55
"url": "git+https://github.com/platform-mesh/portal-ui-lib.git"
66
},
@@ -34,19 +34,19 @@
3434
"@angular/compiler-cli": "20.3.9",
3535
"@angular/localize": "20.3.9",
3636
"@briebug/jest-schematic": "6.0.0",
37-
"@openmfp/portal-ui-lib": "0.182.12",
37+
"@openmfp/portal-ui-lib": "0.183.0",
3838
"@openmfp/config-prettier": "0.9.1",
3939
"@types/jest": "30.0.0",
4040
"@types/jmespath": "0.15.2",
4141
"@types/jsonpath": "0.2.4",
4242
"@ui5/webcomponents-ngx": "0.5.6",
43-
"kubernetes-types": "1.30.0",
4443
"cpx2": "8.0.0",
4544
"jest": "29.7.0",
4645
"jest-jasmine2": "29.7.0",
4746
"jest-junit": "16.0.0",
4847
"jest-mock-extended": "3.0.7",
4948
"jmespath": "0.16.0",
49+
"kubernetes-types": "1.30.0",
5050
"mkdirp": "3.0.1",
5151
"move-cli": "2.0.0",
5252
"ng-packagr": "20.3.0",

projects/lib/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module.exports = {
1010
branches: 90,
1111
functions: 95,
1212
lines: 95,
13-
statements: -3,
13+
statements: -4,
1414
},
1515
},
1616
moduleNameMapper: {

projects/lib/models/models/resource.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Condition, ObjectMeta } from 'kubernetes-types/meta/v1';
22

3-
43
export interface LabelDisplay {
54
backgroundColor?: string;
65
color?: string;
@@ -10,9 +9,22 @@ export interface LabelDisplay {
109
textTransform?: string;
1110
}
1211

12+
export type TransformType =
13+
| 'uppercase'
14+
| 'lowercase'
15+
| 'capitalize'
16+
| 'decode'
17+
| 'encode';
18+
19+
export interface PropertyField {
20+
key: string;
21+
transform?: TransformType[];
22+
}
23+
1324
export interface FieldDefinition {
1425
label?: string;
1526
property: string | string[];
27+
propertyField?: PropertyField;
1628
jsonPathExpression?: string;
1729
required?: boolean;
1830
values?: string[];
@@ -56,6 +68,7 @@ export interface Resource extends Record<string, any> {
5668
status?: ResourceStatus;
5769
__typename?: string;
5870
ready?: boolean;
71+
data?: Record<string, any>;
5972
}
6073

6174
export interface ResourceDefinition {
@@ -83,4 +96,4 @@ export interface UIDefinition {
8396
detailView?: UiView;
8497
}
8598

86-
export type KubernetesScope = 'Cluster' | 'Namespaced';
99+
export type KubernetesScope = 'Cluster' | 'Namespaced';

projects/lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"prettier": "@openmfp/config-prettier",
1414
"peerDependencies": {
15-
"@openmfp/portal-ui-lib": "0.182.12",
15+
"@openmfp/portal-ui-lib": "0.183.0",
1616
"@angular/common": "20.3.9",
1717
"@angular/compiler": "20.3.9",
1818
"@angular/core": "20.3.9",

projects/lib/portal-options/models/luigi-context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface PortalNodeContext extends NodeContext {
1717
translationTable?: any;
1818
namespaceId?: string;
1919
entity?: Resource;
20+
entityName?: string;
2021
entityId?: string;
2122
entityContext?: PortalEntityContext;
2223
}

projects/lib/portal-options/models/luigi-node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import { PortalNodeContext } from './luigi-context';
22
import { LuigiNode } from '@openmfp/portal-ui-lib';
33

44
export interface PortalLuigiNode extends LuigiNode {
5-
context?: PortalNodeContext;
5+
context: PortalNodeContext;
66
parent?: PortalLuigiNode;
77
}

projects/lib/portal-options/services/crd-gateway-kcp-patch-resolver.service.spec.ts

Lines changed: 15 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,26 @@ describe('CrdGatewayKcpPatchResolver', () => {
6161
).toHaveBeenCalledWith(`${kcpRootOrgsPath}:org1:acc1:acc2:acc3`);
6262
});
6363

64-
it('should use kcpPath from node context if provided', async () => {
64+
it('should set kcpPath for node context if not provided', async () => {
65+
const node: PortalLuigiNode = {
66+
context: {},
67+
parent: undefined,
68+
} as any;
69+
70+
await resolver.resolveCrdGatewayKcpPath(node);
71+
72+
expect(node.context.kcpPath).toEqual('root:orgs:org1');
73+
});
74+
75+
it('should not set kcpPath for node context if present', async () => {
6576
const node: PortalLuigiNode = {
6677
context: { kcpPath: 'customPath' },
6778
parent: undefined,
6879
} as any;
6980

7081
await resolver.resolveCrdGatewayKcpPath(node);
7182

72-
expect(
73-
gatewayServiceMock.updateCrdGatewayUrlWithEntityPath,
74-
).toHaveBeenCalledWith('customPath');
83+
expect(node.context.kcpPath).toEqual('customPath');
7584
});
7685

7786
it('should handle node without entity metadata', async () => {
@@ -84,43 +93,7 @@ describe('CrdGatewayKcpPatchResolver', () => {
8493
).toHaveBeenCalledWith(`${kcpRootOrgsPath}:org1`);
8594
});
8695

87-
describe('resolveCrdGatewayKcpPathForNextAccountEntity', () => {
88-
it('should return early if kind is not Account', async () => {
89-
const nextNode: PortalLuigiNode = {
90-
context: {},
91-
parent: undefined,
92-
} as any;
93-
94-
await resolver.resolveCrdGatewayKcpPathForNextAccountEntity(
95-
'leafAcc',
96-
'Project',
97-
nextNode,
98-
);
99-
100-
expect(
101-
gatewayServiceMock.updateCrdGatewayUrlWithEntityPath,
102-
).not.toHaveBeenCalled();
103-
expect(envConfigServiceMock.getEnvConfig).not.toHaveBeenCalled();
104-
});
105-
106-
it('should return early if entityId is empty', async () => {
107-
const nextNode: PortalLuigiNode = {
108-
context: {},
109-
parent: undefined,
110-
} as any;
111-
112-
await resolver.resolveCrdGatewayKcpPathForNextAccountEntity(
113-
'',
114-
'Account',
115-
nextNode,
116-
);
117-
118-
expect(
119-
gatewayServiceMock.updateCrdGatewayUrlWithEntityPath,
120-
).not.toHaveBeenCalled();
121-
expect(envConfigServiceMock.getEnvConfig).not.toHaveBeenCalled();
122-
});
123-
96+
describe('resolveCrdGatewayKcpPath', () => {
12497
it('should aggregate parent Account entities and append entityId', async () => {
12598
const nextNode: PortalLuigiNode = {
12699
context: {},
@@ -142,38 +115,12 @@ describe('CrdGatewayKcpPatchResolver', () => {
142115
},
143116
} as any;
144117

145-
await resolver.resolveCrdGatewayKcpPathForNextAccountEntity(
146-
'leafAcc',
147-
'Account',
148-
nextNode,
149-
);
118+
await resolver.resolveCrdGatewayKcpPath(nextNode, 'leafAcc', 'Account');
150119

151120
expect(envConfigServiceMock.getEnvConfig).toHaveBeenCalled();
152121
expect(
153122
gatewayServiceMock.updateCrdGatewayUrlWithEntityPath,
154123
).toHaveBeenCalledWith(`${kcpRootOrgsPath}:org1:acc1:acc2:leafAcc`);
155124
});
156-
157-
it('should use kcpPath from node context if provided (override)', async () => {
158-
const nextNode: PortalLuigiNode = {
159-
context: { kcpPath: 'overridePath' },
160-
parent: {
161-
context: {
162-
entity: { metadata: { name: 'accParent' }, __typename: 'Account' },
163-
},
164-
parent: undefined,
165-
},
166-
} as any;
167-
168-
await resolver.resolveCrdGatewayKcpPathForNextAccountEntity(
169-
'leafAcc',
170-
'Account',
171-
nextNode,
172-
);
173-
174-
expect(
175-
gatewayServiceMock.updateCrdGatewayUrlWithEntityPath,
176-
).toHaveBeenCalledWith('overridePath');
177-
});
178125
});
179126
});

projects/lib/portal-options/services/crd-gateway-kcp-patch-resolver.service.ts

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ export class CrdGatewayKcpPatchResolver {
99
private gatewayService = inject(GatewayService);
1010
private envConfigService = inject(EnvConfigService);
1111

12-
public async resolveCrdGatewayKcpPathForNextAccountEntity(
13-
entityId: string,
14-
kind: string,
12+
public async resolveCrdGatewayKcpPath(
1513
nextNode: PortalLuigiNode,
14+
entityId?: string,
15+
kind?: string,
1616
) {
17-
if (kind !== 'Account' || !entityId) {
18-
return;
17+
if (nextNode.context.kcpPath) {
18+
this.gatewayService.updateCrdGatewayUrlWithEntityPath(
19+
nextNode.context.kcpPath,
20+
);
21+
return nextNode.context.kcpPath;
1922
}
2023

21-
let entityKcpPath = `:${entityId}`;
22-
let node: PortalLuigiNode | undefined = nextNode.parent;
23-
24+
let entityKcpPath = kind !== 'Account' || !entityId ? '' : `:${entityId}`;
25+
let node: PortalLuigiNode | undefined = nextNode;
2426
do {
2527
const entity = node?.context?.entity;
2628
if (entity?.metadata?.name && entity['__typename'] === 'Account') {
@@ -30,25 +32,12 @@ export class CrdGatewayKcpPatchResolver {
3032
} while (node);
3133

3234
const org = (await this.envConfigService.getEnvConfig()).idpName;
33-
const kcpPath =
34-
nextNode.context?.kcpPath || `${kcpRootOrgsPath}:${org}${entityKcpPath}`;
35+
const kcpPath = `${kcpRootOrgsPath}:${org}${entityKcpPath}`;
3536
this.gatewayService.updateCrdGatewayUrlWithEntityPath(kcpPath);
36-
}
3737

38-
public async resolveCrdGatewayKcpPath(nextNode: PortalLuigiNode) {
39-
let entityKcpPath = '';
40-
let node: PortalLuigiNode | undefined = nextNode;
41-
do {
42-
const entity = node.context?.entity;
43-
if (entity?.metadata?.name && entity['__typename'] === 'Account') {
44-
entityKcpPath = `:${entity.metadata.name}${entityKcpPath}`;
45-
}
46-
node = node.parent;
47-
} while (node);
48-
49-
const org = (await this.envConfigService.getEnvConfig()).idpName;
50-
const kcpPath =
51-
nextNode.context?.kcpPath || `${kcpRootOrgsPath}:${org}${entityKcpPath}`;
52-
this.gatewayService.updateCrdGatewayUrlWithEntityPath(kcpPath);
38+
if (!nextNode.context.kcpPath) {
39+
nextNode.context.kcpPath = kcpPath;
40+
}
41+
return kcpPath;
5342
}
5443
}

0 commit comments

Comments
 (0)