Skip to content

Commit 84ac871

Browse files
committed
add required writeonly properties to resource state with placeholder
1 parent e8a4224 commit 84ac871

10 files changed

+3007
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { ResourceSchema } from '../ResourceSchema';
2+
import type { ResourceTemplateTransformer } from './ResourceTemplateTransformer';
3+
4+
/**
5+
* Transformer that adds tabstop placeholders for required write-only properties.
6+
* Only adds placeholders at the required property level, not for nested write-only children.
7+
* Replaces empty objects with tabstop placeholders using LSP snippet syntax.
8+
* Uses sequential tabstops (${1}, ${2}, etc.). The $0 final cursor position is handled by the client.
9+
*/
10+
export class AddWriteOnlyRequiredPropertiesTransformer implements ResourceTemplateTransformer {
11+
public transform(resourceProperties: Record<string, unknown>, schema: ResourceSchema): void {
12+
const requiredProps = schema.required ?? [];
13+
const writeOnlyPaths = schema.writeOnlyProperties ?? [];
14+
15+
if (requiredProps.length === 0 || writeOnlyPaths.length === 0) {
16+
return;
17+
}
18+
19+
const requiredWriteOnlyProps = new Set<string>();
20+
21+
for (const path of writeOnlyPaths) {
22+
const parts = this.parseJsonPointer(path);
23+
if (parts.length >= 2 && parts[0] === 'properties') {
24+
const rootProp = parts[1];
25+
if (requiredProps.includes(rootProp)) {
26+
requiredWriteOnlyProps.add(rootProp);
27+
}
28+
}
29+
}
30+
31+
let tabstopIndex = 1;
32+
for (const prop of requiredWriteOnlyProps) {
33+
if (!(prop in resourceProperties) || this.isEmpty(resourceProperties[prop])) {
34+
resourceProperties[prop] = `\${${tabstopIndex++}:update required write only property}`;
35+
}
36+
}
37+
}
38+
39+
private isEmpty(value: unknown): boolean {
40+
if (value === null || value === undefined) {
41+
return true;
42+
}
43+
if (typeof value === 'object' && !Array.isArray(value)) {
44+
return Object.keys(value as Record<string, unknown>).length === 0;
45+
}
46+
return false;
47+
}
48+
49+
private parseJsonPointer(pointer: string): string[] {
50+
if (!pointer.startsWith('/')) {
51+
return [];
52+
}
53+
return pointer
54+
.slice(1)
55+
.split('/')
56+
.map((part) => part.replaceAll('~1', '/').replaceAll('~0', '~'));
57+
}
58+
}

src/schema/transformers/TransformersUtil.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ResourceStatePurpose } from '../../resourceState/ResourceStateTypes';
2+
import { AddWriteOnlyRequiredPropertiesTransformer } from './AddWriteOnlyRequiredPropertiesTransformer';
23
import { RemoveMutuallyExclusivePropertiesTransformer } from './RemoveMutuallyExclusivePropertiesTransformer';
34
import { RemoveReadonlyPropertiesTransformer } from './RemoveReadonlyPropertiesTransformer';
45
import { RemoveRequiredXorPropertiesTransformer } from './RemoveRequiredXorPropertiesTransformer';
@@ -14,6 +15,7 @@ export class TransformersUtil {
1415
new RemoveMutuallyExclusivePropertiesTransformer(),
1516
new RemoveSystemTagsTransformer(),
1617
new RemoveRequiredXorPropertiesTransformer(),
18+
new AddWriteOnlyRequiredPropertiesTransformer(),
1719
];
1820
} else if (purpose === ResourceStatePurpose.CLONE) {
1921
return [
@@ -22,6 +24,7 @@ export class TransformersUtil {
2224
new RemoveSystemTagsTransformer(),
2325
new ReplacePrimaryIdentifierTransformer(),
2426
new RemoveRequiredXorPropertiesTransformer(),
27+
new AddWriteOnlyRequiredPropertiesTransformer(),
2528
];
2629
}
2730
return [];

0 commit comments

Comments
 (0)