Skip to content

Commit db5c1fb

Browse files
committed
lists all new fields of a custom object
1 parent 34361ce commit db5c1fb

File tree

7 files changed

+98
-41
lines changed

7 files changed

+98
-41
lines changed

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { processChangelog } from '../process-changelog';
22
import { reflect, Type } from '@cparra/apex-reflection';
33
import { CustomObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources';
4+
import { CustomFieldMetadata } from '../../reflection/sobject/reflect-custom-field-source';
45

56
function apexTypeFromRawString(raw: string): Type {
67
const result = reflect(raw);
@@ -11,14 +12,33 @@ function apexTypeFromRawString(raw: string): Type {
1112
return result.typeMirror!;
1213
}
1314

15+
class CustomFieldMetadataBuilder {
16+
build(): CustomFieldMetadata {
17+
return {
18+
type: 'Text',
19+
type_name: 'customfield',
20+
label: 'My Field',
21+
name: 'MyField',
22+
description: null,
23+
parentName: 'MyObject',
24+
};
25+
}
26+
}
27+
1428
class CustomObjectMetadataBuilder {
1529
label: string = 'MyObject';
30+
fields: CustomFieldMetadata[] = [];
1631

1732
withLabel(label: string): CustomObjectMetadataBuilder {
1833
this.label = label;
1934
return this;
2035
}
2136

37+
withField(field: CustomFieldMetadata): CustomObjectMetadataBuilder {
38+
this.fields.push(field);
39+
return this;
40+
}
41+
2242
build(): CustomObjectMetadata {
2343
return {
2444
type_name: 'customobject',
@@ -27,7 +47,7 @@ class CustomObjectMetadataBuilder {
2747
label: this.label,
2848
name: 'MyObject',
2949
description: null,
30-
fields: [],
50+
fields: this.fields,
3151
};
3252
}
3353
}
@@ -161,7 +181,28 @@ describe('when generating a changelog', () => {
161181
]);
162182
});
163183

164-
// [] - Lists all new fields of an object
184+
it('lists all new fields of a custom object', () => {
185+
const oldObject = new CustomObjectMetadataBuilder().build();
186+
const newField = new CustomFieldMetadataBuilder().build();
187+
const newObject = new CustomObjectMetadataBuilder().withField(newField).build();
188+
const oldVersion = { types: [oldObject] };
189+
const newVersion = { types: [newObject] };
190+
191+
const changeLog = processChangelog(oldVersion, newVersion);
192+
193+
expect(changeLog.customObjectModifications).toEqual([
194+
{
195+
typeName: newObject.name,
196+
modifications: [
197+
{
198+
__typename: 'NewField',
199+
name: newField.name,
200+
},
201+
],
202+
},
203+
]);
204+
});
205+
165206
// [] - Lists all removed fields of an object
166207
// [] - Lists changed field labels
167208
});

src/core/changelog/process-changelog.ts

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ClassMirror, EnumMirror, MethodMirror, Type } from '@cparra/apex-reflection';
1+
import { ClassMirror, EnumMirror, InterfaceMirror, MethodMirror, Type } from '@cparra/apex-reflection';
22
import { pipe } from 'fp-ts/function';
33
import { areMethodsEqual } from './method-changes-checker';
44
import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources';
@@ -54,7 +54,7 @@ export function processChangelog(oldVersion: VersionManifest, newVersion: Versio
5454
newOrModifiedApexMembers: getNewOrModifiedApexMembers(oldVersion, newVersion),
5555
newCustomObjects: getNewCustomObjects(oldVersion, newVersion),
5656
removedCustomObjects: getRemovedCustomObjects(oldVersion, newVersion),
57-
customObjectModifications: getModifiedCustomObjectLabels(oldVersion, newVersion),
57+
customObjectModifications: getCustomObjectModifications(oldVersion, newVersion),
5858
};
5959
}
6060

@@ -98,27 +98,48 @@ function getNewOrModifiedApexMembers(oldVersion: VersionManifest, newVersion: Ve
9898
);
9999
}
100100

101-
function getModifiedCustomObjectLabels(
102-
oldVersion: VersionManifest,
103-
newVersion: VersionManifest,
104-
): NewOrModifiedMember[] {
105-
return pipe(getCustomObjectsInBothVersions(oldVersion, newVersion), (customObjectsInBoth) =>
106-
customObjectsInBoth
107-
.filter(({ oldType, newType }) => oldType.label.toLowerCase() !== newType.label.toLowerCase())
108-
.map(({ newType }) => ({
109-
typeName: newType.name,
110-
modifications: [{ __typename: 'LabelChanged', name: newType.label }],
111-
})),
101+
function getCustomObjectModifications(oldVersion: VersionManifest, newVersion: VersionManifest): NewOrModifiedMember[] {
102+
return pipe(
103+
getCustomObjectsInBothVersions(oldVersion, newVersion),
104+
(customObjectsInBoth) => [
105+
...getModifiedCustomObjectLabels(customObjectsInBoth),
106+
...getNewOrRemovedCustomFields(customObjectsInBoth),
107+
],
108+
(customObjectModifications) => customObjectModifications.filter((member) => member.modifications.length > 0),
112109
);
113110
}
114111

112+
function getModifiedCustomObjectLabels(typesInBoth: TypeInBoth<CustomObjectMetadata>[]): NewOrModifiedMember[] {
113+
return typesInBoth
114+
.filter(({ oldType, newType }) => oldType.label.toLowerCase() !== newType.label.toLowerCase())
115+
.map(({ newType }) => ({
116+
typeName: newType.name,
117+
modifications: [{ __typename: 'LabelChanged', name: newType.label }],
118+
}));
119+
}
120+
121+
function getNewOrRemovedCustomFields(typesInBoth: TypeInBoth<CustomObjectMetadata>[]): NewOrModifiedMember[] {
122+
return typesInBoth.map(({ oldType, newType }) => {
123+
const oldCustomObject = oldType;
124+
const newCustomObject = newType;
125+
126+
return {
127+
typeName: newType.name,
128+
modifications: [
129+
...getNewValues(oldCustomObject, newCustomObject, 'fields', 'NewField'),
130+
...getRemovedValues(oldCustomObject, newCustomObject, 'fields', 'RemovedField'),
131+
],
132+
};
133+
});
134+
}
135+
115136
function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth<Type>[]): NewOrModifiedMember[] {
116137
return pipe(
117-
typesInBoth.filter((typeInBoth) => typeInBoth.oldType.type_name === 'enum'),
138+
typesInBoth.filter((typeInBoth): typeInBoth is TypeInBoth<EnumMirror> => typeInBoth.oldType.type_name === 'enum'),
118139
(enumsInBoth) =>
119140
enumsInBoth.map(({ oldType, newType }) => {
120-
const oldEnum = oldType as EnumMirror;
121-
const newEnum = newType as EnumMirror;
141+
const oldEnum = oldType;
142+
const newEnum = newType;
122143
return {
123144
typeName: newType.name,
124145
modifications: [
@@ -133,24 +154,25 @@ function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth<Type>[]): NewOrModif
133154
function getNewOrModifiedMethods(typesInBoth: TypeInBoth<Type>[]): NewOrModifiedMember[] {
134155
return pipe(
135156
typesInBoth.filter(
136-
(typeInBoth) => typeInBoth.oldType.type_name === 'class' || typeInBoth.oldType.type_name === 'interface',
157+
(typeInBoth): typeInBoth is TypeInBoth<ClassMirror | InterfaceMirror> =>
158+
typeInBoth.oldType.type_name === 'class' || typeInBoth.oldType.type_name === 'interface',
137159
),
138160
(typesInBoth) =>
139161
typesInBoth.map(({ oldType, newType }) => {
140-
const oldMethodAware = oldType as MethodAware;
141-
const newMethodAware = newType as MethodAware;
162+
const oldMethodAware = oldType;
163+
const newMethodAware = newType;
142164

143165
return {
144166
typeName: newType.name,
145167
modifications: [
146-
...getNewValues<MethodMirror, MethodAware, 'methods'>(
168+
...getNewValues<MethodMirror, InterfaceMirror | ClassMirror, 'methods'>(
147169
oldMethodAware,
148170
newMethodAware,
149171
'methods',
150172
'NewMethod',
151173
areMethodsEqual,
152174
),
153-
...getRemovedValues<MethodMirror, MethodAware, 'methods'>(
175+
...getRemovedValues<MethodMirror, InterfaceMirror | ClassMirror, 'methods'>(
154176
oldMethodAware,
155177
newMethodAware,
156178
'methods',
@@ -228,12 +250,12 @@ function areEqualByName<T extends NameAware>(oldValue: T, newValue: T): boolean
228250
return oldValue.name.toLowerCase() === newValue.name.toLowerCase();
229251
}
230252

231-
function getNewValues<Named extends NameAware, T extends Record<K, Named[]>, K extends keyof T>(
253+
function getNewValues<Searchable extends NameAware, T extends Record<K, Searchable[]>, K extends keyof T>(
232254
oldPlaceToSearch: T,
233255
newPlaceToSearch: T,
234256
keyToSearch: K,
235257
typeName: ModificationTypes,
236-
areEqualFn: AreEqualFn<Named> = areEqualByName,
258+
areEqualFn: AreEqualFn<Searchable> = areEqualByName,
237259
): MemberModificationType[] {
238260
return newPlaceToSearch[keyToSearch]
239261
.filter((newValue) => !oldPlaceToSearch[keyToSearch].some((oldValue) => areEqualFn(oldValue, newValue)))
@@ -253,7 +275,3 @@ function getRemovedValues<Named extends NameAware, T extends Record<K, Named[]>,
253275
.map((value) => value.name)
254276
.map((name) => ({ __typename: typeName, name }));
255277
}
256-
257-
type MethodAware = {
258-
methods: MethodMirror[];
259-
};

src/core/changelog/renderable-changelog.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,7 @@ function toRenderableModificationDescription(memberModificationType: MemberModif
137137
return `New Type: ${memberModificationType.name}`;
138138
case 'RemovedType':
139139
return `Removed Type: ${memberModificationType.name}`;
140+
case 'LabelChanged':
141+
return `Label Changed to ${memberModificationType.name}`;
140142
}
141143
}

src/core/markdown/adapters/type-to-renderable.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ function objectMetadataToRenderable(
264264
fields: {
265265
headingLevel: 2,
266266
heading: 'Fields',
267-
value: objectMetadata.fields.map((field) => fieldMetadataToRenderable(field.type, config, 3)),
267+
value: objectMetadata.fields.map((field) => fieldMetadataToRenderable(field, config, 3)),
268268
},
269269
};
270270
}

src/core/reflection/sobject/reflect-custom-object-sources.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type CustomObjectMetadata = {
1616
label: string;
1717
name: string;
1818
description: string | null;
19-
fields: ParsedFile<CustomFieldMetadata>[];
19+
fields: CustomFieldMetadata[];
2020
};
2121

2222
export function reflectCustomObjectSources(
@@ -78,7 +78,7 @@ function toObjectMetadata(parserResult: { CustomObject: object }): CustomObjectM
7878
deploymentStatus: 'Deployed',
7979
visibility: 'Public',
8080
description: null,
81-
fields: [] as ParsedFile<CustomFieldMetadata>[],
81+
fields: [] as CustomFieldMetadata[],
8282
};
8383
return { ...defaultValues, ...parserResult.CustomObject } as CustomObjectMetadata;
8484
}

src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import * as TE from 'fp-ts/TaskEither';
44
import { ReflectionErrors } from '../../errors/errors';
55
import { CustomFieldMetadata, reflectCustomFieldSources } from './reflect-custom-field-source';
66
import { pipe } from 'fp-ts/function';
7+
import { TaskEither } from 'fp-ts/TaskEither';
78

89
export function reflectCustomFieldsAndObjects(
910
objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[],
10-
) {
11+
): TaskEither<ReflectionErrors, ParsedFile<CustomObjectMetadata>[]> {
1112
function filterNonPublished(parsedFiles: ParsedFile<CustomObjectMetadata>[]): ParsedFile<CustomObjectMetadata>[] {
1213
return parsedFiles.filter((parsedFile) => parsedFile.type.deploymentStatus === 'Deployed');
1314
}
@@ -45,9 +46,9 @@ export function reflectCustomFieldsAndObjects(
4546
...object,
4647
type: {
4748
...object.type,
48-
fields: objectFields,
49+
fields: objectFields.map((field) => field.type),
4950
},
50-
} as ParsedFile<CustomObjectMetadata>;
51+
};
5152
});
5253
}),
5354
);

src/core/reflection/sort-types-and-members.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { ClassMirror, EnumMirror, InterfaceMirror, Type } from '@cparra/apex-ref
22
import { ParsedFile } from '../shared/types';
33
import { isApexType } from '../shared/utils';
44
import { CustomObjectMetadata } from './sobject/reflect-custom-object-sources';
5-
import { CustomFieldMetadata } from './sobject/reflect-custom-field-source';
65

76
type Named = { name: string };
87

@@ -45,14 +44,10 @@ function sortTypeMember(type: Type, shouldSort: boolean): Type {
4544
function sortCustomObjectFields(type: CustomObjectMetadata, shouldSort: boolean): CustomObjectMetadata {
4645
return {
4746
...type,
48-
fields: sortFields(type.fields, shouldSort),
47+
fields: sortNamed(shouldSort, type.fields),
4948
};
5049
}
5150

52-
function sortFields(fields: ParsedFile<CustomFieldMetadata>[], shouldSort: boolean): ParsedFile<CustomFieldMetadata>[] {
53-
return fields.sort((a, b) => sortByNames(shouldSort, a.type, b.type));
54-
}
55-
5651
function sortEnumValues(shouldSort: boolean, enumType: EnumMirror): EnumMirror {
5752
return {
5853
...enumType,

0 commit comments

Comments
 (0)