@cparra/apexdocs 3.0.0-alpha.9 → 3.0.0-rc.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/LICENSE +1 -1
- package/README.md +442 -325
- package/dist/cli/generate.js +295 -205
- package/dist/index.d.ts +15 -17
- package/examples/markdown/docs/miscellaneous/Url.md +10 -8
- package/examples/markdown/force-app/classes/Url.cls +3 -1
- package/examples/markdown-jsconfig/.forceignore +12 -0
- package/examples/markdown-jsconfig/apexdocs.config.mjs +21 -0
- package/examples/markdown-jsconfig/config/project-scratch-def.json +5 -0
- package/examples/markdown-jsconfig/docs/index.md +12 -0
- package/examples/markdown-jsconfig/docs/miscellaneous/Url.md +315 -0
- package/examples/markdown-jsconfig/force-app/classes/Url.cls +196 -0
- package/examples/markdown-jsconfig/package-lock.json +665 -0
- package/examples/markdown-jsconfig/package.json +15 -0
- package/examples/markdown-jsconfig/sfdx-project.json +12 -0
- package/examples/open-api/config/project-scratch-def.json +13 -0
- package/examples/open-api/docs/openapi.json +582 -0
- package/examples/{force-app → open-api/force-app}/main/default/classes/SampleClass.cls +1 -0
- package/examples/open-api/package-lock.json +724 -0
- package/examples/open-api/package.json +20 -0
- package/examples/open-api/sfdx-project.json +12 -0
- package/examples/vitepress/apexdocs.config.ts +7 -2
- package/examples/vitepress/docs/index.md +11 -11
- package/examples/vitepress/docs/miscellaneous/BaseClass.md +1 -1
- package/examples/vitepress/docs/miscellaneous/MultiInheritanceClass.md +2 -2
- package/examples/vitepress/docs/miscellaneous/SampleException.md +1 -1
- package/examples/vitepress/docs/miscellaneous/SampleInterface.md +6 -6
- package/examples/vitepress/docs/miscellaneous/Url.md +3 -3
- package/examples/vitepress/docs/sample-enums/SampleEnum.md +3 -3
- package/examples/vitepress/docs/samplegroup/SampleClass.md +5 -5
- package/examples/vitepress/force-app/main/default/classes/SampleClass.cls +1 -1
- package/package.json +2 -2
- package/src/application/Apexdocs.ts +39 -7
- package/src/application/__tests__/apex-file-reader.spec.ts +0 -17
- package/src/application/file-writer.ts +37 -15
- package/src/application/generators/markdown.ts +10 -39
- package/src/application/generators/openapi.ts +22 -6
- package/src/cli/args.ts +4 -1
- package/src/cli/commands/markdown.ts +1 -3
- package/src/cli/commands/openapi.ts +36 -0
- package/src/core/markdown/__test__/generating-class-docs.spec.ts +1 -129
- package/src/core/markdown/__test__/generating-docs.spec.ts +111 -0
- package/src/core/markdown/__test__/generating-enum-docs.spec.ts +0 -64
- package/src/core/markdown/__test__/generating-interface-docs.spec.ts +0 -64
- package/src/core/markdown/adapters/documentables.ts +0 -1
- package/src/core/markdown/generate-docs.ts +2 -5
- package/src/core/markdown/reflection/__test__/filter-scope.spec.ts +306 -0
- package/src/core/markdown/reflection/reflect-source.ts +51 -50
- package/src/core/openApiSettings.ts +41 -0
- package/src/core/openapi/__tests__/open-api-docs-processor.spec.ts +2 -2
- package/src/core/openapi/open-api-docs-processor.ts +8 -4
- package/src/core/openapi/open-api.ts +5 -1
- package/src/core/openapi/openapi-type-file.ts +1 -1
- package/src/core/openapi/parser.ts +1 -15
- package/src/core/openapi/transpiler.ts +0 -5
- package/src/core/parse-apex-metadata.ts +21 -5
- package/src/core/shared/types.d.ts +18 -17
- package/src/index.ts +23 -10
- package/src/test-helpers/SettingsBuilder.ts +2 -6
- package/examples/force-app/main/default/classes/AnotherInterface.cls +0 -16
- package/examples/force-app/main/default/classes/EscapedAnnotations.cls +0 -5
- package/examples/force-app/main/default/classes/GrandparentClass.cls +0 -5
- package/examples/force-app/main/default/classes/GroupedClass.cls +0 -8
- package/examples/force-app/main/default/classes/InterfaceWithInheritance.cls +0 -1
- package/examples/force-app/main/default/classes/MemberGrouping.cls +0 -17
- package/examples/force-app/main/default/classes/ParentClass.cls +0 -16
- package/examples/force-app/main/default/classes/SampleClass.cls-meta.xml +0 -5
- package/examples/force-app/main/default/classes/SampleClassWithoutModifier.cls +0 -9
- package/examples/force-app/main/default/classes/SampleInterface.cls +0 -16
- package/src/core/settings.ts +0 -56
- /package/examples/{force-app → open-api/force-app}/main/default/classes/ChildClass.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResource.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResourceToSkip.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResourceWithInnerClass.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResourceWithoutApexDocs.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference1.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference2.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference3.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference4.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference5.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference6.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference7.cls +0 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { ParsedFile } from '../../../shared/types';
|
|
2
|
+
import { ClassMirror, EnumMirror, InterfaceMirror, reflect } from '@cparra/apex-reflection';
|
|
3
|
+
import { filterScope } from '../filter-scope';
|
|
4
|
+
|
|
5
|
+
function parsedFileFromRawString(raw: string): ParsedFile {
|
|
6
|
+
const { error, typeMirror } = reflect(raw);
|
|
7
|
+
if (error) {
|
|
8
|
+
throw new Error(error.message);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
source: {
|
|
13
|
+
filePath: 'test.cls',
|
|
14
|
+
name: typeMirror!.name,
|
|
15
|
+
type: typeMirror!.type_name,
|
|
16
|
+
},
|
|
17
|
+
type: typeMirror!,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('When filtering scope', () => {
|
|
22
|
+
it('filters out files with the @ignore annotation', () => {
|
|
23
|
+
const properties: [string, number][] = [
|
|
24
|
+
[
|
|
25
|
+
`
|
|
26
|
+
/**
|
|
27
|
+
* @ignore
|
|
28
|
+
*/
|
|
29
|
+
global class MyClass {}
|
|
30
|
+
`,
|
|
31
|
+
0,
|
|
32
|
+
],
|
|
33
|
+
['global class MyClass {}', 1],
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (const [input, expected] of properties) {
|
|
37
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
38
|
+
|
|
39
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
40
|
+
|
|
41
|
+
expect(result).toHaveLength(expected);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('when scoping a class', () => {
|
|
46
|
+
it('filters out methods tagged with @ignore', () => {
|
|
47
|
+
const properties: [string, number][] = [
|
|
48
|
+
[
|
|
49
|
+
`
|
|
50
|
+
global class MyClass {
|
|
51
|
+
/**
|
|
52
|
+
* @ignore
|
|
53
|
+
*/
|
|
54
|
+
global void myMethod() {}
|
|
55
|
+
}
|
|
56
|
+
`,
|
|
57
|
+
0,
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
`
|
|
61
|
+
global class MyClass {
|
|
62
|
+
global void myMethod() {}
|
|
63
|
+
}
|
|
64
|
+
`,
|
|
65
|
+
1,
|
|
66
|
+
],
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (const [input, expected] of properties) {
|
|
70
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
71
|
+
|
|
72
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
73
|
+
|
|
74
|
+
expect((result[0].type as ClassMirror).methods).toHaveLength(expected);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('filters out properties tagged with @ignore', () => {
|
|
79
|
+
const properties: [string, number][] = [
|
|
80
|
+
[
|
|
81
|
+
`
|
|
82
|
+
global class MyClass {
|
|
83
|
+
/**
|
|
84
|
+
* @ignore
|
|
85
|
+
*/
|
|
86
|
+
global Integer myProperty { get; set; }
|
|
87
|
+
}
|
|
88
|
+
`,
|
|
89
|
+
0,
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
`
|
|
93
|
+
global class MyClass {
|
|
94
|
+
global Integer myProperty { get; set; }
|
|
95
|
+
}
|
|
96
|
+
`,
|
|
97
|
+
1,
|
|
98
|
+
],
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
for (const [input, expected] of properties) {
|
|
102
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
103
|
+
|
|
104
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
105
|
+
|
|
106
|
+
expect((result[0].type as ClassMirror).properties).toHaveLength(expected);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('filters out fields tagged with @ignore', () => {
|
|
111
|
+
const properties: [string, number][] = [
|
|
112
|
+
[
|
|
113
|
+
`
|
|
114
|
+
global class MyClass {
|
|
115
|
+
/**
|
|
116
|
+
* @ignore
|
|
117
|
+
*/
|
|
118
|
+
global Integer myField;
|
|
119
|
+
}
|
|
120
|
+
`,
|
|
121
|
+
0,
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
`
|
|
125
|
+
global class MyClass {
|
|
126
|
+
global Integer myField;
|
|
127
|
+
}
|
|
128
|
+
`,
|
|
129
|
+
1,
|
|
130
|
+
],
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
for (const [input, expected] of properties) {
|
|
134
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
135
|
+
|
|
136
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
137
|
+
|
|
138
|
+
expect((result[0].type as ClassMirror).fields).toHaveLength(expected);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('filters out inner classes tagged with @ignore', () => {
|
|
143
|
+
const properties: [string, number][] = [
|
|
144
|
+
[
|
|
145
|
+
`
|
|
146
|
+
global class MyClass {
|
|
147
|
+
/**
|
|
148
|
+
* @ignore
|
|
149
|
+
*/
|
|
150
|
+
global class InnerClass {}
|
|
151
|
+
}
|
|
152
|
+
`,
|
|
153
|
+
0,
|
|
154
|
+
],
|
|
155
|
+
[
|
|
156
|
+
`
|
|
157
|
+
global class MyClass {
|
|
158
|
+
global class InnerClass {}
|
|
159
|
+
}
|
|
160
|
+
`,
|
|
161
|
+
1,
|
|
162
|
+
],
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
for (const [input, expected] of properties) {
|
|
166
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
167
|
+
|
|
168
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
169
|
+
|
|
170
|
+
expect((result[0].type as ClassMirror).classes).toHaveLength(expected);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('filters out inner interfaces tagged with @ignore', () => {
|
|
175
|
+
const properties: [string, number][] = [
|
|
176
|
+
[
|
|
177
|
+
`
|
|
178
|
+
global class MyClass {
|
|
179
|
+
/**
|
|
180
|
+
* @ignore
|
|
181
|
+
*/
|
|
182
|
+
global interface InnerInterface {}
|
|
183
|
+
}
|
|
184
|
+
`,
|
|
185
|
+
0,
|
|
186
|
+
],
|
|
187
|
+
[
|
|
188
|
+
`
|
|
189
|
+
global class MyClass {
|
|
190
|
+
global interface InnerInterface {}
|
|
191
|
+
}
|
|
192
|
+
`,
|
|
193
|
+
1,
|
|
194
|
+
],
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
for (const [input, expected] of properties) {
|
|
198
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
199
|
+
|
|
200
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
201
|
+
|
|
202
|
+
expect((result[0].type as ClassMirror).interfaces).toHaveLength(expected);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('filters out inner enums tagged with @ignore', () => {
|
|
207
|
+
const properties: [string, number][] = [
|
|
208
|
+
[
|
|
209
|
+
`
|
|
210
|
+
global class MyClass {
|
|
211
|
+
/**
|
|
212
|
+
* @ignore
|
|
213
|
+
*/
|
|
214
|
+
global enum InnerEnum {}
|
|
215
|
+
}
|
|
216
|
+
`,
|
|
217
|
+
0,
|
|
218
|
+
],
|
|
219
|
+
[
|
|
220
|
+
`
|
|
221
|
+
global class MyClass {
|
|
222
|
+
global enum InnerEnum {}
|
|
223
|
+
}
|
|
224
|
+
`,
|
|
225
|
+
1,
|
|
226
|
+
],
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
for (const [input, expected] of properties) {
|
|
230
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
231
|
+
|
|
232
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
233
|
+
|
|
234
|
+
expect((result[0].type as ClassMirror).enums).toHaveLength(expected);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('when scoping an interface', () => {
|
|
240
|
+
it('filters out methods tagged with @ignore', () => {
|
|
241
|
+
const properties: [string, number][] = [
|
|
242
|
+
[
|
|
243
|
+
`
|
|
244
|
+
global interface MyInterface {
|
|
245
|
+
/**
|
|
246
|
+
* @ignore
|
|
247
|
+
*/
|
|
248
|
+
void myMethod();
|
|
249
|
+
}
|
|
250
|
+
`,
|
|
251
|
+
0,
|
|
252
|
+
],
|
|
253
|
+
[
|
|
254
|
+
`
|
|
255
|
+
global interface MyInterface {
|
|
256
|
+
void myMethod();
|
|
257
|
+
}
|
|
258
|
+
`,
|
|
259
|
+
1,
|
|
260
|
+
],
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
for (const [input, expected] of properties) {
|
|
264
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
265
|
+
|
|
266
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
267
|
+
|
|
268
|
+
expect((result[0].type as InterfaceMirror).methods).toHaveLength(expected);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('when scoping an enum', () => {
|
|
274
|
+
it('never filters out enum values, even if tagged with @ignore', () => {
|
|
275
|
+
const properties: [string, number][] = [
|
|
276
|
+
[
|
|
277
|
+
`
|
|
278
|
+
global enum MyEnum {
|
|
279
|
+
/**
|
|
280
|
+
* @ignore
|
|
281
|
+
*/
|
|
282
|
+
VALUE
|
|
283
|
+
}
|
|
284
|
+
`,
|
|
285
|
+
1,
|
|
286
|
+
],
|
|
287
|
+
[
|
|
288
|
+
`
|
|
289
|
+
global enum MyEnum {
|
|
290
|
+
VALUE
|
|
291
|
+
}
|
|
292
|
+
`,
|
|
293
|
+
1,
|
|
294
|
+
],
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
for (const [input, expected] of properties) {
|
|
298
|
+
const parsedFile = parsedFileFromRawString(input);
|
|
299
|
+
|
|
300
|
+
const result = filterScope(['global'], [parsedFile]);
|
|
301
|
+
|
|
302
|
+
expect((result[0].type as EnumMirror).values).toHaveLength(expected);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ParsedFile, UnparsedSourceFile } from '../../shared/types';
|
|
2
|
-
//import * as E from 'fp-ts/Either';
|
|
3
2
|
import * as TE from 'fp-ts/TaskEither';
|
|
3
|
+
import * as E from 'fp-ts/Either';
|
|
4
4
|
import * as T from 'fp-ts/Task';
|
|
5
5
|
import * as A from 'fp-ts/lib/Array';
|
|
6
|
-
import { reflect as mirrorReflection, Type } from '@cparra/apex-reflection';
|
|
6
|
+
import { Annotation, reflect as mirrorReflection, Type } from '@cparra/apex-reflection';
|
|
7
7
|
import { pipe } from 'fp-ts/function';
|
|
8
8
|
import * as O from 'fp-ts/Option';
|
|
9
9
|
import { parseApexMetadata } from '../../parse-apex-metadata';
|
|
10
|
-
import { ParsingError } from '@cparra/apex-reflection
|
|
10
|
+
import { ParsingError } from '@cparra/apex-reflection';
|
|
11
11
|
import { apply } from '#utils/fp';
|
|
12
12
|
import { Semigroup } from 'fp-ts/Semigroup';
|
|
13
13
|
|
|
@@ -37,20 +37,20 @@ async function reflectAsync(rawSource: string): Promise<Type> {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function
|
|
40
|
+
export function reflectBundles(apexBundles: UnparsedSourceFile[]) {
|
|
41
41
|
const semiGroupReflectionError: Semigroup<ReflectionErrors> = {
|
|
42
42
|
concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]),
|
|
43
43
|
};
|
|
44
44
|
const Ap = TE.getApplicativeTaskValidation(T.ApplyPar, semiGroupReflectionError);
|
|
45
45
|
|
|
46
|
-
return pipe(apexBundles, A.traverse(Ap)(
|
|
46
|
+
return pipe(apexBundles, A.traverse(Ap)(reflectBundle));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function
|
|
49
|
+
function reflectBundle(apexBundle: UnparsedSourceFile): TE.TaskEither<ReflectionErrors, ParsedFile> {
|
|
50
50
|
const convertToParsedFile: (typeMirror: Type) => ParsedFile = apply(toParsedFile, apexBundle.filePath);
|
|
51
|
-
const withMetadata
|
|
51
|
+
const withMetadata = apply(addMetadata, apexBundle.metadataContent);
|
|
52
52
|
|
|
53
|
-
return pipe(apexBundle, reflectAsTask, TE.map(convertToParsedFile), TE.
|
|
53
|
+
return pipe(apexBundle, reflectAsTask, TE.map(convertToParsedFile), TE.flatMap(withMetadata));
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function reflectAsTask(apexBundle: UnparsedSourceFile): TE.TaskEither<ReflectionErrors, Type> {
|
|
@@ -72,51 +72,52 @@ function toParsedFile(filePath: string, typeMirror: Type): ParsedFile {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function addMetadata(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
function addMetadata(
|
|
76
|
+
rawMetadataContent: string | null,
|
|
77
|
+
parsedFile: ParsedFile,
|
|
78
|
+
): TE.TaskEither<ReflectionErrors, ParsedFile> {
|
|
79
|
+
return TE.fromEither(
|
|
80
|
+
pipe(
|
|
81
|
+
parsedFile.type,
|
|
82
|
+
(type) => addFileMetadataToTypeAnnotation(type, rawMetadataContent),
|
|
83
|
+
E.map((type) => ({ ...parsedFile, type })),
|
|
84
|
+
E.mapLeft((error) => errorToReflectionErrors(error, parsedFile.source.filePath)),
|
|
85
|
+
),
|
|
86
|
+
);
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// const { filePath, content: input, metadataContent: metadata } = apexBundle;
|
|
90
|
-
// const result = mirrorReflection(input);
|
|
91
|
-
// return result.error
|
|
92
|
-
// ? E.left(new ReflectionError(filePath, result.error.message))
|
|
93
|
-
// : E.right({
|
|
94
|
-
// source: {
|
|
95
|
-
// filePath,
|
|
96
|
-
// name: result.typeMirror!.name,
|
|
97
|
-
// type: result.typeMirror!.type_name,
|
|
98
|
-
// },
|
|
99
|
-
// type: addFileMetadataToTypeAnnotation(result.typeMirror!, metadata),
|
|
100
|
-
// });
|
|
101
|
-
// }
|
|
102
|
-
|
|
103
|
-
function addFileMetadataToTypeAnnotation(type: Type, metadata: string | null): Type {
|
|
89
|
+
function errorToReflectionErrors(error: Error, filePath: string): ReflectionErrors {
|
|
90
|
+
return new ReflectionErrors([new ReflectionError(filePath, error.message)]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function addFileMetadataToTypeAnnotation(type: Type, metadata: string | null): E.Either<Error, Type> {
|
|
94
|
+
const concatAnnotationToType = apply(concatAnnotations, type);
|
|
95
|
+
|
|
104
96
|
return pipe(
|
|
105
97
|
O.fromNullable(metadata),
|
|
106
|
-
O.map(
|
|
107
|
-
|
|
108
|
-
// Or maybe what we do is return the Either from the parse-apex-metadata function?
|
|
109
|
-
const metadataParams = parseApexMetadata(metadata);
|
|
110
|
-
metadataParams.forEach((value, key) => {
|
|
111
|
-
const declaration = `${key}: ${value}`;
|
|
112
|
-
type.annotations.push({
|
|
113
|
-
rawDeclaration: declaration,
|
|
114
|
-
name: declaration,
|
|
115
|
-
type: declaration,
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
return type;
|
|
119
|
-
}),
|
|
120
|
-
O.getOrElse(() => type),
|
|
98
|
+
O.map(concatAnnotationToType),
|
|
99
|
+
O.getOrElse(() => E.right(type)),
|
|
121
100
|
);
|
|
122
101
|
}
|
|
102
|
+
|
|
103
|
+
function concatAnnotations(type: Type, metadataInput: string): E.Either<Error, Type> {
|
|
104
|
+
return pipe(
|
|
105
|
+
metadataInput,
|
|
106
|
+
parseApexMetadata,
|
|
107
|
+
E.map((metadataMap) => ({
|
|
108
|
+
...type,
|
|
109
|
+
annotations: [...type.annotations, ...mapToAnnotations(metadataMap)],
|
|
110
|
+
})),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function mapToAnnotations(metadata: Map<string, string>): Annotation[] {
|
|
115
|
+
return Array.from(metadata.entries()).map(([key, value]) => {
|
|
116
|
+
const declaration = `${key}: ${value}`;
|
|
117
|
+
return {
|
|
118
|
+
name: declaration,
|
|
119
|
+
type: declaration,
|
|
120
|
+
rawDeclaration: declaration,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface SettingsConfig {
|
|
2
|
+
sourceDirectory: string;
|
|
3
|
+
outputDir: string;
|
|
4
|
+
openApiFileName: string;
|
|
5
|
+
namespace?: string;
|
|
6
|
+
openApiTitle?: string;
|
|
7
|
+
version: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class OpenApiSettings {
|
|
11
|
+
private static instance: OpenApiSettings;
|
|
12
|
+
|
|
13
|
+
private constructor(public config: SettingsConfig) {}
|
|
14
|
+
|
|
15
|
+
public static build(config: SettingsConfig) {
|
|
16
|
+
OpenApiSettings.instance = new OpenApiSettings(config);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static getInstance(): OpenApiSettings {
|
|
20
|
+
if (!OpenApiSettings.instance) {
|
|
21
|
+
throw new Error('Settings has not been initialized');
|
|
22
|
+
}
|
|
23
|
+
return OpenApiSettings.instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public getOpenApiTitle(): string | undefined {
|
|
27
|
+
return this.config.openApiTitle;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public getNamespace(): string | undefined {
|
|
31
|
+
return this.config.namespace;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public openApiFileName(): string {
|
|
35
|
+
return this.config.openApiFileName;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public getVersion(): string {
|
|
39
|
+
return this.config.version;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { OpenApiDocsProcessor } from '../open-api-docs-processor';
|
|
2
|
-
import {
|
|
2
|
+
import { OpenApiSettings } from '../../openApiSettings';
|
|
3
3
|
import { SettingsBuilder } from '../../../test-helpers/SettingsBuilder';
|
|
4
4
|
import { DocCommentBuilder } from '../../../test-helpers/DocCommentBuilder';
|
|
5
5
|
import { AnnotationBuilder } from '../../../test-helpers/AnnotationBuilder';
|
|
6
6
|
import { ClassMirrorBuilder } from '../../../test-helpers/ClassMirrorBuilder';
|
|
7
7
|
|
|
8
8
|
beforeEach(() => {
|
|
9
|
-
|
|
9
|
+
OpenApiSettings.build(new SettingsBuilder().build());
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
it('should add a path based on the @UrlResource annotation on the class', function () {
|
|
@@ -2,7 +2,7 @@ import { FileContainer } from './file-container';
|
|
|
2
2
|
import { ClassMirror, Type } from '@cparra/apex-reflection';
|
|
3
3
|
import { Logger } from '#utils/logger';
|
|
4
4
|
import { OpenApi } from './open-api';
|
|
5
|
-
import {
|
|
5
|
+
import { OpenApiSettings } from '../openApiSettings';
|
|
6
6
|
import { MethodParser } from './parsers/MethodParser';
|
|
7
7
|
import { camel2title } from '#utils/string-utils';
|
|
8
8
|
import { createOpenApiFile } from './openapi-type-file';
|
|
@@ -13,11 +13,15 @@ export class OpenApiDocsProcessor {
|
|
|
13
13
|
|
|
14
14
|
constructor() {
|
|
15
15
|
this._fileContainer = new FileContainer();
|
|
16
|
-
const title =
|
|
16
|
+
const title = OpenApiSettings.getInstance().getOpenApiTitle();
|
|
17
17
|
if (!title) {
|
|
18
18
|
throw Error('No OpenApi title was provided.');
|
|
19
19
|
}
|
|
20
|
-
this.openApiModel = new OpenApi(
|
|
20
|
+
this.openApiModel = new OpenApi(
|
|
21
|
+
title,
|
|
22
|
+
OpenApiSettings.getInstance().getVersion(),
|
|
23
|
+
OpenApiSettings.getInstance().getNamespace(),
|
|
24
|
+
);
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
fileBuilder(): FileContainer {
|
|
@@ -66,7 +70,7 @@ export class OpenApiDocsProcessor {
|
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
onAfterProcess: ((types: Type[]) => void) | undefined = () => {
|
|
69
|
-
const page = createOpenApiFile(
|
|
73
|
+
const page = createOpenApiFile(OpenApiSettings.getInstance().openApiFileName(), this.openApiModel);
|
|
70
74
|
this._fileContainer.pushFile(page);
|
|
71
75
|
};
|
|
72
76
|
|
|
@@ -15,7 +15,11 @@ export class OpenApi {
|
|
|
15
15
|
servers: ServerObject[];
|
|
16
16
|
components?: ComponentsObject;
|
|
17
17
|
|
|
18
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
title: string,
|
|
20
|
+
version: string,
|
|
21
|
+
private namespace?: string,
|
|
22
|
+
) {
|
|
19
23
|
this.info = {
|
|
20
24
|
title: title,
|
|
21
25
|
version: version,
|
|
@@ -4,7 +4,7 @@ import { OpenApiPageData } from '../shared/types';
|
|
|
4
4
|
export function createOpenApiFile(fileName: string, openApiModel: OpenApi): OpenApiPageData {
|
|
5
5
|
const content = JSON.stringify({ ...openApiModel, namespace: undefined }, null, 2);
|
|
6
6
|
return {
|
|
7
|
-
outputDocPath:
|
|
7
|
+
outputDocPath: `${fileName}.json`,
|
|
8
8
|
content,
|
|
9
9
|
frontmatter: null,
|
|
10
10
|
group: null,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ClassMirror, InterfaceMirror, ReflectionResult, Type } from '@cparra/apex-reflection';
|
|
2
|
-
import { parseApexMetadata } from '../parse-apex-metadata';
|
|
3
2
|
import { Logger } from '#utils/logger';
|
|
4
3
|
import { UnparsedSourceFile } from '../shared/types';
|
|
5
4
|
|
|
@@ -16,20 +15,7 @@ export class RawBodyParser implements TypeParser {
|
|
|
16
15
|
const types = this.typeBundles
|
|
17
16
|
.map((currentBundle) => {
|
|
18
17
|
Logger.log(`Parsing file: ${currentBundle.filePath}`);
|
|
19
|
-
|
|
20
|
-
if (!!result.typeMirror && !!currentBundle.metadataContent) {
|
|
21
|
-
// If successful and there is a metadata file
|
|
22
|
-
const metadataParams = parseApexMetadata(currentBundle.metadataContent);
|
|
23
|
-
metadataParams.forEach((value, key) => {
|
|
24
|
-
const declaration = `${key}: ${value}`;
|
|
25
|
-
result.typeMirror?.annotations.push({
|
|
26
|
-
rawDeclaration: declaration,
|
|
27
|
-
name: declaration,
|
|
28
|
-
type: declaration,
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
18
|
+
return reflect(currentBundle);
|
|
33
19
|
})
|
|
34
20
|
.filter((reflectionResult) => {
|
|
35
21
|
return reflectionResult.typeMirror;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Type } from '@cparra/apex-reflection';
|
|
2
|
-
import { Settings } from '../settings';
|
|
3
2
|
import { OpenApiDocsProcessor } from './open-api-docs-processor';
|
|
4
3
|
|
|
5
4
|
export default class Transpiler {
|
|
@@ -10,10 +9,6 @@ export default class Transpiler {
|
|
|
10
9
|
return 0;
|
|
11
10
|
});
|
|
12
11
|
|
|
13
|
-
if (Settings.getInstance().indexOnly) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
12
|
sortedTypes.forEach((currentType) => {
|
|
18
13
|
processor.onProcess(currentType);
|
|
19
14
|
});
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { XMLParser } from 'fast-xml-parser';
|
|
2
|
+
import * as E from 'fp-ts/Either';
|
|
3
|
+
import { pipe } from 'fp-ts/function';
|
|
4
|
+
|
|
5
|
+
type ApexMetadata = {
|
|
6
|
+
ApexClass: ApexClassMetadata;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ApexClassMetadata = {
|
|
10
|
+
apiVersion: string;
|
|
11
|
+
status?: string;
|
|
12
|
+
};
|
|
2
13
|
|
|
3
14
|
export function parseApexMetadata(input: string) {
|
|
4
|
-
|
|
5
|
-
|
|
15
|
+
return pipe(input, parse, E.map(toMap));
|
|
16
|
+
}
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
function parse(input: string): E.Either<Error, ApexMetadata> {
|
|
19
|
+
return E.tryCatch(() => new XMLParser().parse(input), E.toError);
|
|
20
|
+
}
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
22
|
+
function toMap(metadata: ApexMetadata): Map<string, string> {
|
|
23
|
+
const map = new Map<string, string>();
|
|
24
|
+
map.set('apiVersion', String(metadata.ApexClass.apiVersion));
|
|
25
|
+
if (metadata.ApexClass.status) {
|
|
26
|
+
map.set('status', String(metadata.ApexClass.status));
|
|
11
27
|
}
|
|
12
28
|
|
|
13
29
|
return map;
|