-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathget-resource-path.ts
141 lines (126 loc) · 4.85 KB
/
get-resource-path.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { createLogger } from '@sap-cloud-sdk/util';
import { EntityBase, Constructable } from '../entity';
import { FieldType, Field } from '../selectable';
import { UriConverter } from '../uri-conversion';
import { toStaticPropertyFormat } from '../name-converter';
const logger = createLogger({
package: 'core',
messageContext: 'get-resource-path'
});
type GetResourcePathForKeysType<EntityT extends EntityBase> = (
keys: Record<string, FieldType>,
entityConstructor: Constructable<EntityT>
) => string;
interface GetResourcePathForKeys<EntityT extends EntityBase = any> {
getResourcePathForKeys: GetResourcePathForKeysType<EntityT>;
}
/**
* Creates a getResourcePathForKeys function using the OData v2 or OData v4 URI converter.
* The concrete instances for v2 or v4 are initiated in odata/v2/uri-conversion/odata-uri.ts and odata/v4/uri-conversion/odata-uri.ts.
*
* @param uriConverter Uri converter for v2 or v4.
* @returns The filter getter. See [[GetFilter]]
*/
export function createGetResourcePathForKeys(
uriConverter: UriConverter
): GetResourcePathForKeys {
/**
* Get the resource path of an entity specified by key-value pairs.
*
* @typeparam EntityT - Type of the entity to get the resource path for
* @param keys - Key-value pairs where the key is the name of a key property of the given entity and the value is the respective value
* @param entityConstructor - Constructor type of the entity to get the resource path for
* @param uriConverter - OData version specific converter for strings in URIs
* @returns The path to the resource
*/
function getResourcePathForKeys<EntityT extends EntityBase>(
keys: Record<string, FieldType> = {},
entityConstructor: Constructable<EntityT>
): string {
keys = filterNonKeyProperties(keys, entityConstructor);
validateKeys(keys, entityConstructor);
if (Object.keys(keys).length) {
const byKey = Object.entries(keys)
.map(([key, value]) => keyToOData(key, value, entityConstructor))
.join(',');
return `${entityConstructor._entityName}(${byKey})`;
}
return entityConstructor._entityName;
}
function getMissingKeys<EntityT extends EntityBase>(
keys: Record<string, FieldType>,
entityConstructor: Constructable<EntityT>
): string[] {
const givenKeys = Object.keys(keys);
return (entityConstructor._keyFields as Field<EntityT>[]) // type assertion for backwards compatibility, TODO: remove in v2.0
.map(field => field._fieldName)
.filter(fieldName => !givenKeys.includes(fieldName));
}
function getInvalidKeys<EntityT extends EntityBase>(
keys: Record<string, FieldType>,
entityConstructor: Constructable<EntityT>
): string[] {
// type assertion for backwards compatibility, TODO: remove in v2.0
const validKeys = (entityConstructor._keyFields as Field<EntityT>[]).map(
field => field._fieldName
);
return Object.keys(keys).filter(key => !validKeys.includes(key));
}
function getNullishKeys(keys: Record<string, FieldType>): string[] {
return Object.entries(keys)
.filter(([_, value]) => typeof value === 'undefined' || value === null)
.map(([key]) => key);
}
function filterNonKeyProperties<EntityT extends EntityBase>(
keys: Record<string, FieldType>,
entityConstructor: Constructable<EntityT>
): Record<string, FieldType> {
const invalidKeys = getInvalidKeys(keys, entityConstructor);
if (invalidKeys.length) {
logger.warn(
`There are too many key properties. Ignoring the following keys: ${invalidKeys.join(
', '
)}`
);
return Object.entries(keys)
.filter(([key]) => !invalidKeys.includes(key))
.reduce(
(validKeys, [key, value]) => ({ ...validKeys, [key]: value }),
{}
);
}
return keys;
}
function keyToOData<EntityT extends EntityBase>(
key: string,
value: any,
entityConstructor: Constructable<EntityT>
): string {
const edmType = entityConstructor[toStaticPropertyFormat(key)].edmType;
return `${key}=${uriConverter.convertToUriFormat(value, edmType)}`;
}
function validateKeys<EntityT extends EntityBase>(
keys: Record<string, FieldType>,
entityConstructor: Constructable<EntityT>
): void {
const missingKeys = getMissingKeys(keys, entityConstructor);
if (missingKeys.length) {
throw new Error(
`Cannot get resource path for entity ${
entityConstructor._entityName
}. The following keys are missing: ${missingKeys.join(', ')}`
);
}
const nullishKeys = getNullishKeys(keys);
if (nullishKeys.length) {
throw new Error(
`Cannot get resource path for entity ${
entityConstructor._entityName
}. The following keys have nullish values, but are not nullable: ${nullishKeys.join(
', '
)}`
);
}
}
return { getResourcePathForKeys };
}