Skip to content

Commit 2348882

Browse files
authored
Merge pull request #210 from cesarParra/orphan-fields-documentation
Documenting extension fields
2 parents d456407 + 4250e94 commit 2348882

File tree

19 files changed

+473
-55
lines changed

19 files changed

+473
-55
lines changed

examples/vitepress/docs/.vitepress/sidebar.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
{
5959
"text": "Object Reference",
6060
"items": [
61+
{
62+
"text": "Contact",
63+
"link": "custom-objects/Contact.md"
64+
},
6165
{
6266
"text": "Event__c",
6367
"link": "custom-objects/Event__c.md"

examples/vitepress/docs/changelog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,12 @@ Represents a line item on a sales order.
6767
Custom object for tracking sales orders.
6868
### Speaker__c
6969

70-
Represents a speaker at an event.
70+
Represents a speaker at an event.
71+
72+
## New or Removed Fields to Custom Objects or Standard Objects
73+
74+
These custom fields have been added or removed.
75+
76+
### Contact
77+
78+
- New Field: PhotoUrl__c
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
title: Contact
3+
---
4+
5+
# Contact
6+
7+
## API Name
8+
`apexdocs__Contact`
9+
10+
## Fields
11+
### PhotoUrl
12+
13+
**API Name**
14+
15+
`apexdocs__PhotoUrl__c`
16+
17+
**Type**
18+
19+
*Url*

examples/vitepress/docs/custom-objects/Product_Inline_Fields__c.md

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,93 @@ The date the product got discontinued
2020

2121
**Type**
2222

23-
*DateTime*
23+
*DateTime*
24+
25+
---
26+
### ID
27+
28+
**API Name**
29+
30+
`apexdocs__ID__c`
31+
32+
**Type**
33+
34+
*Number*
35+
36+
---
37+
### Name
38+
39+
Product name
40+
41+
**API Name**
42+
43+
`apexdocs__Name__c`
44+
45+
**Type**
46+
47+
*Text*
48+
49+
---
50+
### Price
51+
52+
Product price in the default currency
53+
54+
**API Name**
55+
56+
`apexdocs__Price__c`
57+
58+
**Type**
59+
60+
*Number*
61+
62+
---
63+
### Products
64+
65+
**API Name**
66+
67+
`apexdocs__Products__c`
68+
69+
**Type**
70+
71+
*ExternalLookup*
72+
73+
---
74+
### Rating
75+
76+
Rating
77+
78+
**API Name**
79+
80+
`apexdocs__Rating__c`
81+
82+
**Type**
83+
84+
*Number*
85+
86+
---
87+
### ReleaseDate
88+
89+
ReleaseDate
90+
91+
**API Name**
92+
93+
`apexdocs__ReleaseDate__c`
94+
95+
**Type**
96+
97+
*DateTime*
98+
99+
---
100+
### Type
101+
102+
**API Name**
103+
104+
`apexdocs__Type__c`
105+
106+
**Type**
107+
108+
*Picklist*
109+
110+
#### Possible values are
111+
* Merchandise
112+
* Bundle

examples/vitepress/docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ hero:
1919

2020
## Custom Objects
2121

22+
### [Contact](custom-objects/Contact)
23+
2224
### [Event__c](custom-objects/Event__c)
2325

2426
Represents an event that people can register for.

src/core/changelog/__test__/generating-change-log.spec.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { assertEither } from '../../test-helpers/assert-either';
44
import { isSkip } from '../../shared/utils';
55
import { unparsedFieldBundleFromRawString } from '../../test-helpers/test-data-builders';
66
import { CustomObjectXmlBuilder } from '../../test-helpers/test-data-builders/custom-object-xml-builder';
7+
import { CustomFieldXmlBuilder } from '../../test-helpers/test-data-builders/custom-field-xml-builder';
78

89
const config = {
910
fileName: 'changelog',
@@ -347,7 +348,9 @@ describe('when generating a changelog', () => {
347348
const result = await generateChangeLog(oldBundle, newBundle, config)();
348349

349350
assertEither(result, (data) =>
350-
expect((data as ChangeLogPageData).content).toContain('## New or Removed Fields in Existing Objects'),
351+
expect((data as ChangeLogPageData).content).toContain(
352+
'## New or Removed Fields to Custom Objects or Standard Objects',
353+
),
351354
);
352355
});
353356

@@ -393,4 +396,74 @@ describe('when generating a changelog', () => {
393396
);
394397
});
395398
});
399+
400+
describe('that includes extension fields', () => {
401+
it('does not include the fields when they are in both versions', async () => {
402+
const fieldSource = new CustomFieldXmlBuilder().build();
403+
404+
const oldBundle: UnparsedSourceBundle[] = [
405+
{
406+
type: 'customfield',
407+
name: 'PhotoUrl__c',
408+
content: fieldSource,
409+
filePath: 'PhotoUrl__c.field-meta.xml',
410+
parentName: 'MyTestObject',
411+
},
412+
];
413+
const newBundle: UnparsedSourceBundle[] = [
414+
{
415+
type: 'customfield',
416+
name: 'PhotoUrl__c',
417+
content: fieldSource,
418+
filePath: 'PhotoUrl__c.field-meta.xml',
419+
parentName: 'MyTestObject',
420+
},
421+
];
422+
423+
const result = await generateChangeLog(oldBundle, newBundle, config)();
424+
425+
assertEither(result, (data) => expect((data as ChangeLogPageData).content).not.toContain('MyTestObject'));
426+
assertEither(result, (data) => expect((data as ChangeLogPageData).content).not.toContain('PhotoUrl__c'));
427+
});
428+
429+
it('includes added fields when they are not in the old version', async () => {
430+
const fieldSource = new CustomFieldXmlBuilder().build();
431+
432+
const oldBundle: UnparsedSourceBundle[] = [];
433+
const newBundle: UnparsedSourceBundle[] = [
434+
{
435+
type: 'customfield',
436+
name: 'PhotoUrl__c',
437+
content: fieldSource,
438+
filePath: 'PhotoUrl__c.field-meta.xml',
439+
parentName: 'MyTestObject',
440+
},
441+
];
442+
443+
const result = await generateChangeLog(oldBundle, newBundle, config)();
444+
445+
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('MyTestObject'));
446+
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('PhotoUrl__c'));
447+
});
448+
449+
it('includes removed fields when they are not in the new version', async () => {
450+
const fieldSource = new CustomFieldXmlBuilder().build();
451+
452+
const oldBundle: UnparsedSourceBundle[] = [
453+
{
454+
type: 'customfield',
455+
name: 'PhotoUrl__c',
456+
content: fieldSource,
457+
filePath: 'PhotoUrl__c.field-meta.xml',
458+
parentName: 'MyTestObject',
459+
},
460+
];
461+
const newBundle: UnparsedSourceBundle[] = [];
462+
463+
const result = await generateChangeLog(oldBundle, newBundle, config)();
464+
465+
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('MyTestObject'));
466+
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('PhotoUrl__c'));
467+
});
468+
});
396469
});

src/core/changelog/__test__/processing-changelog.spec.ts

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ function apexTypeFromRawString(raw: string): Type {
1313
}
1414

1515
class CustomFieldMetadataBuilder {
16+
name: string = 'MyField';
17+
18+
withName(name: string): CustomFieldMetadataBuilder {
19+
this.name = name;
20+
return this;
21+
}
22+
1623
build(): CustomFieldMetadata {
1724
return {
1825
type: 'Text',
1926
type_name: 'customfield',
2027
label: 'MyField',
21-
name: 'MyField',
28+
name: this.name,
2229
description: null,
2330
parentName: 'MyObject',
2431
};
@@ -29,11 +36,6 @@ class CustomObjectMetadataBuilder {
2936
label: string = 'MyObject';
3037
fields: CustomFieldMetadata[] = [];
3138

32-
withLabel(label: string): CustomObjectMetadataBuilder {
33-
this.label = label;
34-
return this;
35-
}
36-
3739
withField(field: CustomFieldMetadata): CustomObjectMetadataBuilder {
3840
this.fields.push(field);
3941
return this;
@@ -570,4 +572,65 @@ describe('when generating a changelog', () => {
570572
]);
571573
});
572574
});
575+
576+
describe('with custom field code', () => {
577+
it('does not list fields that are the same in both versions', () => {
578+
// A field that is the same in both versions is defined as one
579+
// with both the same name and the same parent
580+
581+
const oldField = new CustomFieldMetadataBuilder().build();
582+
const newField = new CustomFieldMetadataBuilder().build();
583+
584+
const oldManifest = { types: [oldField] };
585+
const newManifest = { types: [newField] };
586+
587+
const changeLog = processChangelog(oldManifest, newManifest);
588+
589+
expect(changeLog.customObjectModifications).toEqual([]);
590+
});
591+
592+
it('lists new fields of a custom object', () => {
593+
const oldField = new CustomFieldMetadataBuilder().build();
594+
const newField = new CustomFieldMetadataBuilder().withName('NewField').build();
595+
596+
const oldManifest = { types: [oldField] };
597+
const newManifest = { types: [oldField, newField] };
598+
599+
const changeLog = processChangelog(oldManifest, newManifest);
600+
601+
expect(changeLog.customObjectModifications).toEqual([
602+
{
603+
typeName: newField.parentName,
604+
modifications: [
605+
{
606+
__typename: 'NewField',
607+
name: newField.name,
608+
},
609+
],
610+
},
611+
]);
612+
});
613+
614+
it('lists removed fields of a custom object', () => {
615+
const oldField = new CustomFieldMetadataBuilder().withName('OldField').build();
616+
const unchangedField = new CustomFieldMetadataBuilder().build();
617+
618+
const oldManifest = { types: [unchangedField, oldField] };
619+
const newManifest = { types: [unchangedField] };
620+
621+
const changeLog = processChangelog(oldManifest, newManifest);
622+
623+
expect(changeLog.customObjectModifications).toEqual([
624+
{
625+
typeName: oldField.parentName,
626+
modifications: [
627+
{
628+
__typename: 'RemovedField',
629+
name: oldField.name,
630+
},
631+
],
632+
},
633+
]);
634+
});
635+
});
573636
});

src/core/changelog/generate-change-log.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import { changelogTemplate } from './templates/changelog-template';
1515
import { ReflectionErrors } from '../errors/errors';
1616
import { apply } from '#utils/fp';
1717
import { filterScope } from '../reflection/apex/filter-scope';
18-
import { skip } from '../shared/utils';
18+
import { isInSource, skip } from '../shared/utils';
1919
import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects';
2020
import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources';
2121
import { Type } from '@cparra/apex-reflection';
2222
import { filterApexSourceFiles, filterCustomObjectsAndFields } from '#utils/source-bundle-utils';
23+
import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source';
2324

2425
export type ChangeLogPageData = {
2526
content: string;
@@ -71,16 +72,20 @@ function reflect(bundles: UnparsedSourceBundle[], config: Omit<UserDefinedChange
7172
);
7273
}
7374

74-
function toManifests({
75-
oldVersion,
76-
newVersion,
77-
}: {
78-
oldVersion: ParsedFile<Type | CustomObjectMetadata>[];
79-
newVersion: ParsedFile<Type | CustomObjectMetadata>[];
80-
}) {
81-
function parsedFilesToManifest(parsedFiles: ParsedFile<Type | CustomObjectMetadata>[]): VersionManifest {
75+
function toManifests({ oldVersion, newVersion }: { oldVersion: ParsedFile[]; newVersion: ParsedFile[] }) {
76+
function parsedFilesToManifest(parsedFiles: ParsedFile[]): VersionManifest {
8277
return {
83-
types: parsedFiles.map((parsedFile) => parsedFile.type),
78+
types: parsedFiles.reduce(
79+
(previousValue: (Type | CustomObjectMetadata | CustomFieldMetadata)[], parsedFile: ParsedFile) => {
80+
if (!isInSource(parsedFile.source) && parsedFile.type.type_name === 'customobject') {
81+
// When we are dealing with a custom object that was not in the source (for extension fields), we return all
82+
// of its fields.
83+
return [...previousValue, ...parsedFile.type.fields];
84+
}
85+
return [...previousValue, parsedFile.type];
86+
},
87+
[] as (Type | CustomObjectMetadata | CustomFieldMetadata)[],
88+
),
8489
};
8590
}
8691

0 commit comments

Comments
 (0)