From 207a7735e1519efe630235bba69348e7cab31845 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 07:38:12 -0400 Subject: [PATCH 01/16] Initial commit. --- src/application/Apexdocs.ts | 7 ++- src/application/generators/changelog.ts | 6 +- src/core/changelog/generate-change-log.ts | 58 +++++++++++++++---- src/core/markdown/generate-docs.ts | 58 ++----------------- .../sobject/reflect-custom-object-sources.ts | 4 +- .../sobject/reflectCustomFieldsAndObjects.ts | 52 +++++++++++++++++ 6 files changed, 114 insertions(+), 71 deletions(-) create mode 100644 src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts diff --git a/src/application/Apexdocs.ts b/src/application/Apexdocs.ts index e153bc73..e317b345 100644 --- a/src/application/Apexdocs.ts +++ b/src/application/Apexdocs.ts @@ -11,6 +11,7 @@ import { DefaultFileSystem } from './file-system'; import { Logger } from '#utils/logger'; import { UnparsedApexBundle, + UnparsedSourceBundle, UserDefinedChangelogConfig, UserDefinedConfig, UserDefinedMarkdownConfig, @@ -70,10 +71,10 @@ async function processOpenApi(config: UserDefinedOpenApiConfig, logger: Logger) } async function processChangeLog(config: UserDefinedChangelogConfig) { - function loadFiles(): [UnparsedApexBundle[], UnparsedApexBundle[]] { + function loadFiles(): [UnparsedSourceBundle[], UnparsedSourceBundle[]] { return [ - readFiles(['ApexClass'])(config.previousVersionDir, config.exclude) as UnparsedApexBundle[], - readFiles(['ApexClass'])(config.currentVersionDir, config.exclude) as UnparsedApexBundle[], + readFiles(['ApexClass', 'CustomObject', 'CustomField'])(config.previousVersionDir, config.exclude), + readFiles(['ApexClass', 'CustomObject', 'CustomField'])(config.currentVersionDir, config.exclude), ]; } diff --git a/src/application/generators/changelog.ts b/src/application/generators/changelog.ts index d72dd1f9..00899238 100644 --- a/src/application/generators/changelog.ts +++ b/src/application/generators/changelog.ts @@ -1,5 +1,5 @@ import { pipe } from 'fp-ts/function'; -import { PageData, Skip, UnparsedApexBundle, UserDefinedChangelogConfig } from '../../core/shared/types'; +import { PageData, Skip, UnparsedSourceBundle, UserDefinedChangelogConfig } from '../../core/shared/types'; import * as TE from 'fp-ts/TaskEither'; import { writeFiles } from '../file-writer'; import { ChangeLogPageData, generateChangeLog } from '../../core/changelog/generate-change-log'; @@ -7,8 +7,8 @@ import { FileWritingError } from '../errors'; import { isSkip } from '../../core/shared/utils'; export default function generate( - oldBundles: UnparsedApexBundle[], - newBundles: UnparsedApexBundle[], + oldBundles: UnparsedSourceBundle[], + newBundles: UnparsedSourceBundle[], config: UserDefinedChangelogConfig, ) { function handleFile(file: ChangeLogPageData | Skip) { diff --git a/src/core/changelog/generate-change-log.ts b/src/core/changelog/generate-change-log.ts index 9552308f..f22c0fb9 100644 --- a/src/core/changelog/generate-change-log.ts +++ b/src/core/changelog/generate-change-log.ts @@ -1,4 +1,12 @@ -import { ParsedFile, Skip, UnparsedApexBundle, UserDefinedChangelogConfig } from '../shared/types'; +import { + ParsedFile, + Skip, + UnparsedApexBundle, + UnparsedCustomFieldBundle, + UnparsedCustomObjectBundle, + UnparsedSourceBundle, + UserDefinedChangelogConfig, +} from '../shared/types'; import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither'; import { reflectApexSource } from '../reflection/apex/reflect-apex-source'; @@ -10,6 +18,7 @@ import { ReflectionErrors } from '../errors/errors'; import { apply } from '#utils/fp'; import { filterScope } from '../reflection/apex/filter-scope'; import { isApexType, skip } from '../shared/utils'; +import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; export type ChangeLogPageData = { content: string; @@ -17,16 +26,10 @@ export type ChangeLogPageData = { }; export function generateChangeLog( - oldBundles: UnparsedApexBundle[], - newBundles: UnparsedApexBundle[], + oldBundles: UnparsedSourceBundle[], + newBundles: UnparsedSourceBundle[], config: Omit, ): TE.TaskEither { - const filterOutOfScope = apply(filterScope, config.scope); - - function reflect(sourceFiles: UnparsedApexBundle[]) { - return pipe(reflectApexSource(sourceFiles), TE.map(filterOutOfScope)); - } - const convertToPageData = apply(toPageData, config.fileName); function handleConversion({ changelog, newManifest }: { changelog: Changelog; newManifest: VersionManifest }) { @@ -37,9 +40,9 @@ export function generateChangeLog( } return pipe( - reflect(oldBundles), + reflect(oldBundles, config), TE.bindTo('oldVersion'), - TE.bind('newVersion', () => reflect(newBundles)), + TE.bind('newVersion', () => reflect(newBundles, config)), TE.map(toManifests), TE.map(({ oldManifest, newManifest }) => ({ changelog: processChangelog(oldManifest, newManifest), @@ -49,6 +52,39 @@ export function generateChangeLog( ); } +function reflect(bundles: UnparsedSourceBundle[], config: Omit) { + // TODO: Move to utility and reuse + function filterApexSourceFiles(sourceFiles: UnparsedSourceBundle[]): UnparsedApexBundle[] { + return sourceFiles.filter((sourceFile): sourceFile is UnparsedApexBundle => sourceFile.type === 'apex'); + } + + const filterOutOfScopeApex = apply(filterScope, config.scope); + + function reflectApexFiles(sourceFiles: UnparsedApexBundle[]) { + return pipe(reflectApexSource(sourceFiles), TE.map(filterOutOfScopeApex)); + } + + // TODO: Move to utility and reuse + function filterCustomObjectsAndFields( + sourceFiles: UnparsedSourceBundle[], + ): (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[] { + return sourceFiles.filter( + (sourceFile): sourceFile is UnparsedCustomObjectBundle => + sourceFile.type === 'customobject' || sourceFile.type === 'customfield', + ); + } + + return pipe( + reflectApexFiles(filterApexSourceFiles(bundles)), + TE.chain((parsedApexFiles) => { + return pipe( + reflectCustomFieldsAndObjects(filterCustomObjectsAndFields(bundles)), + TE.map((parsedObjectFiles) => [...parsedApexFiles, ...parsedObjectFiles]), + ); + }), + ); +} + function toManifests({ oldVersion, newVersion }: { oldVersion: ParsedFile[]; newVersion: ParsedFile[] }) { function parsedFilesToManifest(parsedFiles: ParsedFile[]): VersionManifest { return { diff --git a/src/core/markdown/generate-docs.ts b/src/core/markdown/generate-docs.ts index 71a53ec9..b67dc094 100644 --- a/src/core/markdown/generate-docs.ts +++ b/src/core/markdown/generate-docs.ts @@ -33,10 +33,10 @@ import { sortTypesAndMembers } from '../reflection/sort-types-and-members'; import { isSkip } from '../shared/utils'; import { parsedFilesToReferenceGuide } from './adapters/reference-guide'; import { removeExcludedTags } from '../reflection/apex/remove-excluded-tags'; -import { HookError, ReflectionErrors } from '../errors/errors'; -import { ObjectMetadata, reflectCustomObjectSources } from '../reflection/sobject/reflect-custom-object-sources'; -import { CustomFieldMetadata, reflectCustomFieldSources } from '../reflection/sobject/reflect-custom-field-source'; +import { HookError } from '../errors/errors'; +import { ObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; +import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; export type MarkdownGeneratorConfig = Omit< UserDefinedMarkdownConfig, @@ -45,7 +45,7 @@ export type MarkdownGeneratorConfig = Omit< referenceGuideTemplate: string; }; -export function generateDocs(unparsedApexFiles: UnparsedSourceBundle[], config: MarkdownGeneratorConfig) { +export function generateDocs(unparsedBundles: UnparsedSourceBundle[], config: MarkdownGeneratorConfig) { const convertToReferences = apply(parsedFilesToReferenceGuide, config); const convertToRenderableBundle = apply(parsedFilesToRenderableBundle, config); const convertToDocumentationBundleForTemplate = apply( @@ -75,10 +75,10 @@ export function generateDocs(unparsedApexFiles: UnparsedSourceBundle[], config: } return pipe( - generateForApex(filterApexSourceFiles(unparsedApexFiles), config), + generateForApex(filterApexSourceFiles(unparsedBundles), config), TE.chain((parsedApexFiles) => { return pipe( - generateForObject(filterCustomObjectsAndFields(unparsedApexFiles)), + reflectCustomFieldsAndObjects(filterCustomObjectsAndFields(unparsedBundles)), TE.map((parsedObjectFiles) => [...parsedApexFiles, ...parsedObjectFiles]), ); }), @@ -112,52 +112,6 @@ function generateForApex(apexBundles: UnparsedApexBundle[], config: MarkdownGene ); } -function generateForObject(objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[]) { - function filterNonPublished(parsedFiles: ParsedFile[]): ParsedFile[] { - return parsedFiles.filter((parsedFile) => parsedFile.type.deploymentStatus === 'Deployed'); - } - - function filterNonPublic(parsedFiles: ParsedFile[]): ParsedFile[] { - return parsedFiles.filter((parsedFile) => parsedFile.type.visibility === 'Public'); - } - - const customObjects = objectBundles.filter( - (object): object is UnparsedCustomObjectBundle => object.type === 'customobject', - ); - - const customFields = objectBundles.filter( - (object): object is UnparsedCustomFieldBundle => object.type === 'customfield', - ); - - function generateForFields( - fields: UnparsedCustomFieldBundle[], - ): TE.TaskEither[]> { - return pipe(fields, reflectCustomFieldSources); - } - - return pipe( - customObjects, - reflectCustomObjectSources, - TE.map(filterNonPublished), - TE.map(filterNonPublic), - TE.bindTo('objects'), - TE.bind('fields', () => generateForFields(customFields)), - // Locate the fields for each object by using the parentName property - TE.map(({ objects, fields }) => { - return objects.map((object) => { - const objectFields = fields.filter((field) => field.type.parentName === object.type.name); - return { - ...object, - type: { - ...object.type, - fields: objectFields, - }, - } as ParsedFile; - }); - }), - ); -} - function transformReferenceHook(config: MarkdownGeneratorConfig) { async function _execute( references: Record, diff --git a/src/core/reflection/sobject/reflect-custom-object-sources.ts b/src/core/reflection/sobject/reflect-custom-object-sources.ts index e301c4c1..133e000e 100644 --- a/src/core/reflection/sobject/reflect-custom-object-sources.ts +++ b/src/core/reflection/sobject/reflect-custom-object-sources.ts @@ -20,14 +20,14 @@ export type ObjectMetadata = { }; export function reflectCustomObjectSources( - objectSources: UnparsedCustomObjectBundle[], + objectBundles: UnparsedCustomObjectBundle[], ): TE.TaskEither[]> { const semiGroupReflectionError: Semigroup = { concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]), }; const Ap = TE.getApplicativeTaskValidation(T.ApplyPar, semiGroupReflectionError); - return pipe(objectSources, A.traverse(Ap)(reflectCustomObjectSource)); + return pipe(objectBundles, A.traverse(Ap)(reflectCustomObjectSource)); } function reflectCustomObjectSource( diff --git a/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts b/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts new file mode 100644 index 00000000..2ae64810 --- /dev/null +++ b/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts @@ -0,0 +1,52 @@ +import { ParsedFile, UnparsedCustomFieldBundle, UnparsedCustomObjectBundle } from '../../shared/types'; +import { ObjectMetadata, reflectCustomObjectSources } from './reflect-custom-object-sources'; +import * as TE from 'fp-ts/TaskEither'; +import { ReflectionErrors } from '../../errors/errors'; +import { CustomFieldMetadata, reflectCustomFieldSources } from './reflect-custom-field-source'; +import { pipe } from 'fp-ts/function'; + +export function reflectCustomFieldsAndObjects(objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[]) { + function filterNonPublished(parsedFiles: ParsedFile[]): ParsedFile[] { + return parsedFiles.filter((parsedFile) => parsedFile.type.deploymentStatus === 'Deployed'); + } + + function filterNonPublic(parsedFiles: ParsedFile[]): ParsedFile[] { + return parsedFiles.filter((parsedFile) => parsedFile.type.visibility === 'Public'); + } + + const customObjects = objectBundles.filter( + (object): object is UnparsedCustomObjectBundle => object.type === 'customobject', + ); + + const customFields = objectBundles.filter( + (object): object is UnparsedCustomFieldBundle => object.type === 'customfield', + ); + + function generateForFields( + fields: UnparsedCustomFieldBundle[], + ): TE.TaskEither[]> { + return pipe(fields, reflectCustomFieldSources); + } + + return pipe( + customObjects, + reflectCustomObjectSources, + TE.map(filterNonPublished), + TE.map(filterNonPublic), + TE.bindTo('objects'), + TE.bind('fields', () => generateForFields(customFields)), + // Locate the fields for each object by using the parentName property + TE.map(({ objects, fields }) => { + return objects.map((object) => { + const objectFields = fields.filter((field) => field.type.parentName === object.type.name); + return { + ...object, + type: { + ...object.type, + fields: objectFields, + }, + } as ParsedFile; + }); + }), + ); +} From 705f6eb0dc335b51db14cd0fed4d4ef830e9b76d Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 07:43:15 -0400 Subject: [PATCH 02/16] Generate changelog can parse custom objects and fields. --- src/core/changelog/generate-change-log.ts | 19 ++++++++++++------- src/core/changelog/process-changelog.ts | 3 ++- src/core/changelog/renderable-changelog.ts | 14 +++++++++----- src/core/markdown/adapters/reference-guide.ts | 6 +++--- .../markdown/adapters/renderable-bundle.ts | 12 ++++++------ .../markdown/adapters/type-to-renderable.ts | 10 +++++----- src/core/markdown/generate-docs.ts | 6 +++--- .../sobject/reflect-custom-object-sources.ts | 16 ++++++++-------- .../sobject/reflectCustomFieldsAndObjects.ts | 12 +++++++----- src/core/reflection/sort-types-and-members.ts | 8 ++++---- src/core/shared/types.d.ts | 4 ++-- src/core/shared/utils.ts | 10 +++++----- 12 files changed, 66 insertions(+), 54 deletions(-) diff --git a/src/core/changelog/generate-change-log.ts b/src/core/changelog/generate-change-log.ts index f22c0fb9..d5e2ad63 100644 --- a/src/core/changelog/generate-change-log.ts +++ b/src/core/changelog/generate-change-log.ts @@ -17,8 +17,10 @@ import { changelogTemplate } from './templates/changelog-template'; import { ReflectionErrors } from '../errors/errors'; import { apply } from '#utils/fp'; import { filterScope } from '../reflection/apex/filter-scope'; -import { isApexType, skip } from '../shared/utils'; +import { skip } from '../shared/utils'; import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; +import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; +import { Type } from '@cparra/apex-reflection'; export type ChangeLogPageData = { content: string; @@ -85,13 +87,16 @@ function reflect(bundles: UnparsedSourceBundle[], config: Omit[]; + newVersion: ParsedFile[]; +}) { + function parsedFilesToManifest(parsedFiles: ParsedFile[]): VersionManifest { return { - types: parsedFiles - .map((parsedFile) => parsedFile.type) - // Changelog does not currently support object types - .filter((type) => isApexType(type)), + types: parsedFiles.map((parsedFile) => parsedFile.type), }; } diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index 6ae1ecdc..5763644e 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -1,9 +1,10 @@ import { ClassMirror, EnumMirror, MethodMirror, Type } from '@cparra/apex-reflection'; import { pipe } from 'fp-ts/function'; import { areMethodsEqual } from './method-changes-checker'; +import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; export type VersionManifest = { - types: Type[]; + types: (Type | CustomObjectMetadata)[]; }; type ModificationTypes = diff --git a/src/core/changelog/renderable-changelog.ts b/src/core/changelog/renderable-changelog.ts index 43aa4982..2901088b 100644 --- a/src/core/changelog/renderable-changelog.ts +++ b/src/core/changelog/renderable-changelog.ts @@ -1,7 +1,8 @@ import { Changelog, MemberModificationType, NewOrModifiedMember } from './process-changelog'; -import { Type } from '@cparra/apex-reflection'; +import { ClassMirror, EnumMirror, InterfaceMirror, Type } from '@cparra/apex-reflection'; import { RenderableContent } from '../renderables/types'; import { adaptDescribable } from '../renderables/documentables'; +import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; type NewTypeRenderable = { name: string; @@ -40,14 +41,17 @@ export type RenderableChangelog = { newOrModifiedMembers: NewOrModifiedMembersSection | null; }; -export function convertToRenderableChangelog(changelog: Changelog, newManifest: Type[]): RenderableChangelog { +export function convertToRenderableChangelog( + changelog: Changelog, + newManifest: (Type | CustomObjectMetadata)[], +): RenderableChangelog { const allNewTypes = changelog.newTypes.map( (newType) => newManifest.find((type) => type.name.toLowerCase() === newType.toLowerCase())!, ); - const newClasses = allNewTypes.filter((type) => type.type_name === 'class'); - const newInterfaces = allNewTypes.filter((type) => type.type_name === 'interface'); - const newEnums = allNewTypes.filter((type) => type.type_name === 'enum'); + const newClasses = allNewTypes.filter((type): type is ClassMirror => type.type_name === 'class'); + const newInterfaces = allNewTypes.filter((type): type is InterfaceMirror => type.type_name === 'interface'); + const newEnums = allNewTypes.filter((type): type is EnumMirror => type.type_name === 'enum'); return { newClasses: diff --git a/src/core/markdown/adapters/reference-guide.ts b/src/core/markdown/adapters/reference-guide.ts index 81804530..f3a8381d 100644 --- a/src/core/markdown/adapters/reference-guide.ts +++ b/src/core/markdown/adapters/reference-guide.ts @@ -1,12 +1,12 @@ import { MarkdownGeneratorConfig } from '../generate-docs'; import { DocPageReference, ParsedFile } from '../../shared/types'; import { getTypeGroup } from '../../shared/utils'; -import { ObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; export function parsedFilesToReferenceGuide( config: MarkdownGeneratorConfig, - parsedFiles: ParsedFile[], + parsedFiles: ParsedFile[], ): Record { return parsedFiles.reduce>((acc, parsedFile) => { acc[parsedFile.source.name] = parsedFileToDocPageReference(config, parsedFile); @@ -16,7 +16,7 @@ export function parsedFilesToReferenceGuide( function parsedFileToDocPageReference( config: MarkdownGeneratorConfig, - parsedFile: ParsedFile, + parsedFile: ParsedFile, ): DocPageReference { const path = `${slugify(getTypeGroup(parsedFile.type, config))}/${parsedFile.source.name}.md`; return { diff --git a/src/core/markdown/adapters/renderable-bundle.ts b/src/core/markdown/adapters/renderable-bundle.ts index af4cd633..9c6182a6 100644 --- a/src/core/markdown/adapters/renderable-bundle.ts +++ b/src/core/markdown/adapters/renderable-bundle.ts @@ -13,17 +13,17 @@ import { apply } from '#utils/fp'; import { generateLink } from './generate-link'; import { getTypeGroup } from '../../shared/utils'; import { Type } from '@cparra/apex-reflection'; -import { ObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; export function parsedFilesToRenderableBundle( config: MarkdownGeneratorConfig, - parsedFiles: ParsedFile[], + parsedFiles: ParsedFile[], references: Record, ): RenderableBundle { const referenceFinder = apply(generateLink(config.linkingStrategy), references); function toReferenceGuide( - parsedFiles: ParsedFile[], + parsedFiles: ParsedFile[], ): Record { return parsedFiles.reduce>( addToReferenceGuide(apply(referenceFinder, '__base__'), config, references), @@ -31,7 +31,7 @@ export function parsedFilesToRenderableBundle( ); } - function toRenderables(parsedFiles: ParsedFile[]): Renderable[] { + function toRenderables(parsedFiles: ParsedFile[]): Renderable[] { return parsedFiles.reduce((acc, parsedFile) => { const renderable = typeToRenderable(parsedFile, apply(referenceFinder, parsedFile.source.name), config); acc.push(renderable); @@ -50,7 +50,7 @@ function addToReferenceGuide( config: MarkdownGeneratorConfig, references: Record, ) { - return (acc: Record, parsedFile: ParsedFile) => { + return (acc: Record, parsedFile: ParsedFile) => { const group: string = getTypeGroup(parsedFile.type, config); if (!acc[group]) { acc[group] = []; @@ -66,7 +66,7 @@ function addToReferenceGuide( } function getRenderableDescription( - type: Type | ObjectMetadata, + type: Type | CustomObjectMetadata, findLinkFromHome: (referenceName: string) => string | Link, ): RenderableContent[] | null { switch (type.type_name) { diff --git a/src/core/markdown/adapters/type-to-renderable.ts b/src/core/markdown/adapters/type-to-renderable.ts index cd66d8ec..78b24c3d 100644 --- a/src/core/markdown/adapters/type-to-renderable.ts +++ b/src/core/markdown/adapters/type-to-renderable.ts @@ -18,11 +18,11 @@ import { adaptConstructor, adaptMethod } from './methods-and-constructors'; import { adaptFieldOrProperty } from './fields-and-properties'; import { MarkdownGeneratorConfig } from '../generate-docs'; import { SourceFileMetadata } from '../../shared/types'; -import { ObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; import { getTypeGroup } from '../../shared/utils'; import { CustomFieldMetadata } from '../../reflection/sobject/reflect-custom-field-source'; -type GetReturnRenderable = T extends InterfaceMirror +type GetReturnRenderable = T extends InterfaceMirror ? RenderableInterface : T extends ClassMirror ? RenderableClass @@ -30,7 +30,7 @@ type GetReturnRenderable = T extends InterfaceM ? RenderableEnum : RenderableCustomObject; -export function typeToRenderable( +export function typeToRenderable( parsedFile: { source: SourceFileMetadata; type: T }, linkGenerator: GetRenderableContentByTypeName, config: MarkdownGeneratorConfig, @@ -45,7 +45,7 @@ export function typeToRenderable( case 'class': return classTypeToClassSource(type as ClassMirrorWithInheritanceChain, linkGenerator); case 'customobject': - return objectMetadataToRenderable(type as ObjectMetadata, config); + return objectMetadataToRenderable(type as CustomObjectMetadata, config); } } @@ -247,7 +247,7 @@ function singleGroup( } function objectMetadataToRenderable( - objectMetadata: ObjectMetadata, + objectMetadata: CustomObjectMetadata, config: MarkdownGeneratorConfig, ): RenderableCustomObject { return { diff --git a/src/core/markdown/generate-docs.ts b/src/core/markdown/generate-docs.ts index b67dc094..28443d2d 100644 --- a/src/core/markdown/generate-docs.ts +++ b/src/core/markdown/generate-docs.ts @@ -34,7 +34,7 @@ import { isSkip } from '../shared/utils'; import { parsedFilesToReferenceGuide } from './adapters/reference-guide'; import { removeExcludedTags } from '../reflection/apex/remove-excluded-tags'; import { HookError } from '../errors/errors'; -import { ObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; @@ -68,9 +68,9 @@ export function generateDocs(unparsedBundles: UnparsedSourceBundle[], config: Ma ); } - function filterOutCustomFields(parsedFiles: ParsedFile[]): ParsedFile[] { + function filterOutCustomFields(parsedFiles: ParsedFile[]): ParsedFile[] { return parsedFiles.filter( - (parsedFile): parsedFile is ParsedFile => parsedFile.source.type !== 'customfield', + (parsedFile): parsedFile is ParsedFile => parsedFile.source.type !== 'customfield', ); } diff --git a/src/core/reflection/sobject/reflect-custom-object-sources.ts b/src/core/reflection/sobject/reflect-custom-object-sources.ts index 133e000e..698cf9c1 100644 --- a/src/core/reflection/sobject/reflect-custom-object-sources.ts +++ b/src/core/reflection/sobject/reflect-custom-object-sources.ts @@ -9,7 +9,7 @@ import * as A from 'fp-ts/Array'; import * as E from 'fp-ts/Either'; import { CustomFieldMetadata } from './reflect-custom-field-source'; -export type ObjectMetadata = { +export type CustomObjectMetadata = { type_name: 'customobject'; deploymentStatus: string; visibility: string; @@ -21,7 +21,7 @@ export type ObjectMetadata = { export function reflectCustomObjectSources( objectBundles: UnparsedCustomObjectBundle[], -): TE.TaskEither[]> { +): TE.TaskEither[]> { const semiGroupReflectionError: Semigroup = { concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]), }; @@ -32,7 +32,7 @@ export function reflectCustomObjectSources( function reflectCustomObjectSource( objectSource: UnparsedCustomObjectBundle, -): TE.TaskEither> { +): TE.TaskEither> { return pipe( E.tryCatch(() => new XMLParser().parse(objectSource.content), E.toError), E.flatMap(validate), @@ -73,31 +73,31 @@ function validate(parseResult: unknown): E.Either[], }; - return { ...defaultValues, ...parserResult.CustomObject } as ObjectMetadata; + return { ...defaultValues, ...parserResult.CustomObject } as CustomObjectMetadata; } -function addName(objectMetadata: ObjectMetadata, name: string): ObjectMetadata { +function addName(objectMetadata: CustomObjectMetadata, name: string): CustomObjectMetadata { return { ...objectMetadata, name, }; } -function addTypeName(objectMetadata: ObjectMetadata): ObjectMetadata { +function addTypeName(objectMetadata: CustomObjectMetadata): CustomObjectMetadata { return { ...objectMetadata, type_name: 'customobject', }; } -function toParsedFile(filePath: string, typeMirror: ObjectMetadata): ParsedFile { +function toParsedFile(filePath: string, typeMirror: CustomObjectMetadata): ParsedFile { return { source: { filePath: filePath, diff --git a/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts b/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts index 2ae64810..2ec1a336 100644 --- a/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts +++ b/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts @@ -1,16 +1,18 @@ import { ParsedFile, UnparsedCustomFieldBundle, UnparsedCustomObjectBundle } from '../../shared/types'; -import { ObjectMetadata, reflectCustomObjectSources } from './reflect-custom-object-sources'; +import { CustomObjectMetadata, reflectCustomObjectSources } from './reflect-custom-object-sources'; import * as TE from 'fp-ts/TaskEither'; import { ReflectionErrors } from '../../errors/errors'; import { CustomFieldMetadata, reflectCustomFieldSources } from './reflect-custom-field-source'; import { pipe } from 'fp-ts/function'; -export function reflectCustomFieldsAndObjects(objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[]) { - function filterNonPublished(parsedFiles: ParsedFile[]): ParsedFile[] { +export function reflectCustomFieldsAndObjects( + objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[], +) { + function filterNonPublished(parsedFiles: ParsedFile[]): ParsedFile[] { return parsedFiles.filter((parsedFile) => parsedFile.type.deploymentStatus === 'Deployed'); } - function filterNonPublic(parsedFiles: ParsedFile[]): ParsedFile[] { + function filterNonPublic(parsedFiles: ParsedFile[]): ParsedFile[] { return parsedFiles.filter((parsedFile) => parsedFile.type.visibility === 'Public'); } @@ -45,7 +47,7 @@ export function reflectCustomFieldsAndObjects(objectBundles: (UnparsedCustomObje ...object.type, fields: objectFields, }, - } as ParsedFile; + } as ParsedFile; }); }), ); diff --git a/src/core/reflection/sort-types-and-members.ts b/src/core/reflection/sort-types-and-members.ts index fde5f41e..da5b5b9e 100644 --- a/src/core/reflection/sort-types-and-members.ts +++ b/src/core/reflection/sort-types-and-members.ts @@ -1,15 +1,15 @@ import { ClassMirror, EnumMirror, InterfaceMirror, Type } from '@cparra/apex-reflection'; import { ParsedFile } from '../shared/types'; import { isApexType } from '../shared/utils'; -import { ObjectMetadata } from './sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from './sobject/reflect-custom-object-sources'; import { CustomFieldMetadata } from './sobject/reflect-custom-field-source'; type Named = { name: string }; export function sortTypesAndMembers( shouldSort: boolean, - parsedFiles: ParsedFile[], -): ParsedFile[] { + parsedFiles: ParsedFile[], +): ParsedFile[] { return parsedFiles .map((parsedFile) => ({ ...parsedFile, @@ -42,7 +42,7 @@ function sortTypeMember(type: Type, shouldSort: boolean): Type { } } -function sortCustomObjectFields(type: ObjectMetadata, shouldSort: boolean): ObjectMetadata { +function sortCustomObjectFields(type: CustomObjectMetadata, shouldSort: boolean): CustomObjectMetadata { return { ...type, fields: sortFields(type.fields, shouldSort), diff --git a/src/core/shared/types.d.ts b/src/core/shared/types.d.ts index a3c5cac1..7be70803 100644 --- a/src/core/shared/types.d.ts +++ b/src/core/shared/types.d.ts @@ -1,6 +1,6 @@ import { Type } from '@cparra/apex-reflection'; import { ChangeLogPageData } from '../changelog/generate-change-log'; -import { ObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; export type Generators = 'markdown' | 'openapi' | 'changelog'; @@ -89,7 +89,7 @@ export type SourceFileMetadata = { }; export type ParsedFile< - T extends Type | ObjectMetadata | CustomFieldMetadata = Type | ObjectMetadata | CustomFieldMetadata, + T extends Type | CustomObjectMetadata | CustomFieldMetadata = Type | CustomObjectMetadata | CustomFieldMetadata, > = { source: SourceFileMetadata; type: T; diff --git a/src/core/shared/utils.ts b/src/core/shared/utils.ts index 12982c74..3d11cebc 100644 --- a/src/core/shared/utils.ts +++ b/src/core/shared/utils.ts @@ -1,6 +1,6 @@ import { Skip } from './types'; import { Type } from '@cparra/apex-reflection'; -import { ObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; +import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { MarkdownGeneratorConfig } from '../markdown/generate-docs'; import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; @@ -17,15 +17,15 @@ export function isSkip(value: unknown): value is Skip { return Object.prototype.hasOwnProperty.call(value, '_tag') && (value as Skip)._tag === 'Skip'; } -export function isObjectType(type: Type | ObjectMetadata | CustomFieldMetadata): type is ObjectMetadata { - return (type as ObjectMetadata).type_name === 'customobject'; +export function isObjectType(type: Type | CustomObjectMetadata | CustomFieldMetadata): type is CustomObjectMetadata { + return (type as CustomObjectMetadata).type_name === 'customobject'; } -export function isApexType(type: Type | ObjectMetadata | CustomFieldMetadata): type is Type { +export function isApexType(type: Type | CustomObjectMetadata | CustomFieldMetadata): type is Type { return !isObjectType(type); } -export function getTypeGroup(type: Type | ObjectMetadata, config: MarkdownGeneratorConfig): string { +export function getTypeGroup(type: Type | CustomObjectMetadata, config: MarkdownGeneratorConfig): string { function getGroup(type: Type, config: MarkdownGeneratorConfig): string { const groupAnnotation = type.docComment?.annotations.find( (annotation) => annotation.name.toLowerCase() === 'group', From 63eafb123c314d7484908aaf1bdbef3c4e22f6aa Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 08:04:51 -0400 Subject: [PATCH 03/16] Has no new objects when both the old and new versions are the same --- .../__test__/generating-change-log.spec.ts | 2 + .../__test__/processing-changelog.spec.ts | 821 +++++++++--------- src/core/changelog/process-changelog.ts | 18 +- src/core/changelog/renderable-changelog.ts | 10 +- 4 files changed, 449 insertions(+), 402 deletions(-) diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index 28e9512e..d15d448d 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -13,6 +13,8 @@ const config = { skipIfNoChanges: false, }; +// TODO: new integration tests here + describe('when generating a changelog', () => { it('should not skip when skipIfNoChanges, even if there are no changes', async () => { const result = await generateChangeLog([], [], { ...config })(); diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 9162f06a..ab88a6c1 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -1,7 +1,8 @@ import { processChangelog } from '../process-changelog'; import { reflect, Type } from '@cparra/apex-reflection'; +import { CustomObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; -function typeFromRawString(raw: string): Type { +function apexTypeFromRawString(raw: string): Type { const result = reflect(raw); if (result.error) { throw new Error(result.error.message); @@ -10,6 +11,20 @@ function typeFromRawString(raw: string): Type { return result.typeMirror!; } +class CustomObjectMetadataBuilder { + build(): CustomObjectMetadata { + return { + type_name: 'customobject', + deploymentStatus: 'Deployed', + visibility: 'Public', + label: 'MyObject', + name: 'MyObject', + description: null, + fields: [], + }; + } +} + describe('when generating a changelog', () => { it('has no new types when both the old and new versions are empty', () => { const oldVersion = { types: [] }; @@ -17,7 +32,7 @@ describe('when generating a changelog', () => { const changeLog = processChangelog(oldVersion, newVersion); - expect(changeLog.newTypes).toEqual([]); + expect(changeLog.newApexTypes).toEqual([]); }); it('has no removed types when the old and new versions are empty', () => { @@ -26,414 +41,440 @@ describe('when generating a changelog', () => { const changeLog = processChangelog(oldVersion, newVersion); - expect(changeLog.removedTypes).toEqual([]); - }); - - it('has no new types when both the old and new versions are the same', () => { - const anyClassBody = 'public class AnyClass {}'; - const anyClass = typeFromRawString(anyClassBody); - const oldVersion = { types: [anyClass] }; - const newVersion = { types: [anyClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newTypes).toEqual([]); - }); - - it('has no removed types when both the old and new versions are the same', () => { - const anyClassBody = 'public class AnyClass {}'; - const anyClass = typeFromRawString(anyClassBody); - const oldVersion = { types: [anyClass] }; - const newVersion = { types: [anyClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.removedTypes).toEqual([]); - }); - - it('lists all new types', () => { - const existingInBoth = 'public class ExistingInBoth {}'; - const existingClass = typeFromRawString(existingInBoth); - const oldVersion = { types: [existingClass] }; - const newClassBody = 'public class NewClass {}'; - const newClass = typeFromRawString(newClassBody); - const newVersion = { types: [existingClass, newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newTypes).toEqual([newClass.name]); - }); - - it('lists all removed types', () => { - const existingInBoth = 'public class ExistingInBoth {}'; - const existingClass = typeFromRawString(existingInBoth); - const existingOnlyInOld = 'public class ExistingOnlyInOld {}'; - const existingOnlyInOldClass = typeFromRawString(existingOnlyInOld); - const oldVersion = { types: [existingClass, existingOnlyInOldClass] }; - const newVersion = { types: [existingClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.removedTypes).toEqual([existingOnlyInOldClass.name]); - }); - - it('lists all new values of a modified enum', () => { - const enumBefore = 'public enum MyEnum { VALUE1 }'; - const oldEnum = typeFromRawString(enumBefore); - const enumAfter = 'public enum MyEnum { VALUE1, VALUE2 }'; - const newEnum = typeFromRawString(enumAfter); - - const oldVersion = { types: [oldEnum] }; - const newVersion = { types: [newEnum] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyEnum', - modifications: [ - { - __typename: 'NewEnumValue', - name: 'VALUE2', - }, - ], - }, - ]); - }); - - it('list all removed values of a modified enum', () => { - const enumBefore = 'public enum MyEnum { VALUE1, VALUE2 }'; - const oldEnum = typeFromRawString(enumBefore); - const enumAfter = 'public enum MyEnum { VALUE1 }'; - const newEnum = typeFromRawString(enumAfter); - - const oldVersion = { types: [oldEnum] }; - const newVersion = { types: [newEnum] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyEnum', - modifications: [ - { - __typename: 'RemovedEnumValue', - name: 'VALUE2', - }, - ], - }, - ]); - }); - - it('lists all new methods of an interface', () => { - const interfaceBefore = 'public interface MyInterface {}'; - const oldInterface = typeFromRawString(interfaceBefore); - const interfaceAfter = 'public interface MyInterface { void newMethod(); }'; - const newInterface = typeFromRawString(interfaceAfter); - - const oldVersion = { types: [oldInterface] }; - const newVersion = { types: [newInterface] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyInterface', - modifications: [ - { - __typename: 'NewMethod', - name: 'newMethod', - }, - ], - }, - ]); - }); - - it('lists all new methods of a class', () => { - const classBefore = 'public class MyClass { }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { void newMethod() {} }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'NewMethod', - name: 'newMethod', - }, - ], - }, - ]); - }); - - it('lists all removed methods of an interface', () => { - const interfaceBefore = 'public interface MyInterface { void oldMethod(); }'; - const oldInterface = typeFromRawString(interfaceBefore); - const interfaceAfter = 'public interface MyInterface {}'; - const newInterface = typeFromRawString(interfaceAfter); - - const oldVersion = { types: [oldInterface] }; - const newVersion = { types: [newInterface] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyInterface', - modifications: [ - { - __typename: 'RemovedMethod', - name: 'oldMethod', - }, - ], - }, - ]); - }); - - it('lists all new properties of a class', () => { - const classBefore = 'public class MyClass { }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { String newProperty { get; set; } }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'NewProperty', - name: 'newProperty', - }, - ], - }, - ]); - }); - - it('lists all removed properties of a class', () => { - const classBefore = 'public class MyClass { String oldProperty { get; set; } }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'RemovedProperty', - name: 'oldProperty', - }, - ], - }, - ]); - }); - - it('lists all new fields of a class', () => { - const classBefore = 'public class MyClass { }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { String newField; }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'NewField', - name: 'newField', - }, - ], - }, - ]); + expect(changeLog.removedApexTypes).toEqual([]); }); - it('lists all removed fields of a class', () => { - const classBefore = 'public class MyClass { String oldField; }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { }'; - const newClass = typeFromRawString(classAfter); + describe('with apex code', () => { + it('has no new types when both the old and new versions are the same', () => { + const anyClassBody = 'public class AnyClass {}'; + const anyClass = apexTypeFromRawString(anyClassBody); + const oldVersion = { types: [anyClass] }; + const newVersion = { types: [anyClass] }; - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; + const changeLog = processChangelog(oldVersion, newVersion); - const changeLog = processChangelog(oldVersion, newVersion); + expect(changeLog.newApexTypes).toEqual([]); + }); - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'RemovedField', - name: 'oldField', - }, - ], - }, - ]); - }); + it('has no removed types when both the old and new versions are the same', () => { + const anyClassBody = 'public class AnyClass {}'; + const anyClass = apexTypeFromRawString(anyClassBody); + const oldVersion = { types: [anyClass] }; + const newVersion = { types: [anyClass] }; - it('lists new inner classes of a class', () => { - const classBefore = 'public class MyClass { }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { class NewInnerClass { } }'; - const newClass = typeFromRawString(classAfter); + const changeLog = processChangelog(oldVersion, newVersion); - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; + expect(changeLog.removedApexTypes).toEqual([]); + }); - const changeLog = processChangelog(oldVersion, newVersion); + it('lists all new types', () => { + const existingInBoth = 'public class ExistingInBoth {}'; + const existingClass = apexTypeFromRawString(existingInBoth); + const oldVersion = { types: [existingClass] }; + const newClassBody = 'public class NewClass {}'; + const newClass = apexTypeFromRawString(newClassBody); + const newVersion = { types: [existingClass, newClass] }; - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'NewType', - name: 'NewInnerClass', - }, - ], - }, - ]); - }); + const changeLog = processChangelog(oldVersion, newVersion); - it('lists removed inner classes of a class', () => { - const classBefore = 'public class MyClass { class OldInnerClass { } }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { }'; - const newClass = typeFromRawString(classAfter); + expect(changeLog.newApexTypes).toEqual([newClass.name]); + }); - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; + it('lists all removed types', () => { + const existingInBoth = 'public class ExistingInBoth {}'; + const existingClass = apexTypeFromRawString(existingInBoth); + const existingOnlyInOld = 'public class ExistingOnlyInOld {}'; + const existingOnlyInOldClass = apexTypeFromRawString(existingOnlyInOld); + const oldVersion = { types: [existingClass, existingOnlyInOldClass] }; + const newVersion = { types: [existingClass] }; - const changeLog = processChangelog(oldVersion, newVersion); + const changeLog = processChangelog(oldVersion, newVersion); - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'RemovedType', - name: 'OldInnerClass', - }, - ], - }, - ]); + expect(changeLog.removedApexTypes).toEqual([existingOnlyInOldClass.name]); + }); }); - it('lists new inner interfaces of a class', () => { - const classBefore = 'public class MyClass { }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { interface NewInterface { } }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'NewType', - name: 'NewInterface', - }, - ], - }, - ]); + describe('with custom object code', () => { + it('has no new objects when both the old and new versions are the same', () => { + const oldVersion = { types: [new CustomObjectMetadataBuilder().build()] }; + const newVersion = { types: [new CustomObjectMetadataBuilder().build()] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newCustomObjects).toEqual([]); + }); + // [] - Has no removed objects when both the old and new versions are the same + // [] - Lists all new objects + // [] - Lists all removed objects + // [] - Lists changed object labels + // [] - Lists all new fields of an object + // [] - Lists all removed fields of an object + // [] - Lists changed field labels }); - it('lists removed inner interfaces of a class', () => { - const classBefore = 'public class MyClass { interface OldInterface { } }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'RemovedType', - name: 'OldInterface', - }, - ], - }, - ]); + describe('with enum code', () => { + it('lists all new values of a modified enum', () => { + const enumBefore = 'public enum MyEnum { VALUE1 }'; + const oldEnum = apexTypeFromRawString(enumBefore); + const enumAfter = 'public enum MyEnum { VALUE1, VALUE2 }'; + const newEnum = apexTypeFromRawString(enumAfter); + + const oldVersion = { types: [oldEnum] }; + const newVersion = { types: [newEnum] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyEnum', + modifications: [ + { + __typename: 'NewEnumValue', + name: 'VALUE2', + }, + ], + }, + ]); + }); + + it('list all removed values of a modified enum', () => { + const enumBefore = 'public enum MyEnum { VALUE1, VALUE2 }'; + const oldEnum = apexTypeFromRawString(enumBefore); + const enumAfter = 'public enum MyEnum { VALUE1 }'; + const newEnum = apexTypeFromRawString(enumAfter); + + const oldVersion = { types: [oldEnum] }; + const newVersion = { types: [newEnum] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyEnum', + modifications: [ + { + __typename: 'RemovedEnumValue', + name: 'VALUE2', + }, + ], + }, + ]); + }); }); - it('lists new inner enums of a class', () => { - const classBefore = 'public class MyClass { }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { enum NewEnum { } }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'NewType', - name: 'NewEnum', - }, - ], - }, - ]); + describe('with interface code', () => { + it('lists all new methods of an interface', () => { + const interfaceBefore = 'public interface MyInterface {}'; + const oldInterface = apexTypeFromRawString(interfaceBefore); + const interfaceAfter = 'public interface MyInterface { void newMethod(); }'; + const newInterface = apexTypeFromRawString(interfaceAfter); + + const oldVersion = { types: [oldInterface] }; + const newVersion = { types: [newInterface] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyInterface', + modifications: [ + { + __typename: 'NewMethod', + name: 'newMethod', + }, + ], + }, + ]); + }); + + it('lists all removed methods of an interface', () => { + const interfaceBefore = 'public interface MyInterface { void oldMethod(); }'; + const oldInterface = apexTypeFromRawString(interfaceBefore); + const interfaceAfter = 'public interface MyInterface {}'; + const newInterface = apexTypeFromRawString(interfaceAfter); + + const oldVersion = { types: [oldInterface] }; + const newVersion = { types: [newInterface] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyInterface', + modifications: [ + { + __typename: 'RemovedMethod', + name: 'oldMethod', + }, + ], + }, + ]); + }); }); - it('lists removed inner enums of a class', () => { - const classBefore = 'public class MyClass { interface OldEnum { } }'; - const oldClass = typeFromRawString(classBefore); - const classAfter = 'public class MyClass { }'; - const newClass = typeFromRawString(classAfter); - - const oldVersion = { types: [oldClass] }; - const newVersion = { types: [newClass] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - expect(changeLog.newOrModifiedMembers).toEqual([ - { - typeName: 'MyClass', - modifications: [ - { - __typename: 'RemovedType', - name: 'OldEnum', - }, - ], - }, - ]); + describe('with class code', () => { + it('lists all new methods of a class', () => { + const classBefore = 'public class MyClass { }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { void newMethod() {} }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'NewMethod', + name: 'newMethod', + }, + ], + }, + ]); + }); + + it('lists all new properties of a class', () => { + const classBefore = 'public class MyClass { }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { String newProperty { get; set; } }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'NewProperty', + name: 'newProperty', + }, + ], + }, + ]); + }); + + it('lists all removed properties of a class', () => { + const classBefore = 'public class MyClass { String oldProperty { get; set; } }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'RemovedProperty', + name: 'oldProperty', + }, + ], + }, + ]); + }); + + it('lists all new fields of a class', () => { + const classBefore = 'public class MyClass { }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { String newField; }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'NewField', + name: 'newField', + }, + ], + }, + ]); + }); + + it('lists all removed fields of a class', () => { + const classBefore = 'public class MyClass { String oldField; }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'RemovedField', + name: 'oldField', + }, + ], + }, + ]); + }); + + it('lists new inner classes of a class', () => { + const classBefore = 'public class MyClass { }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { class NewInnerClass { } }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'NewType', + name: 'NewInnerClass', + }, + ], + }, + ]); + }); + + it('lists removed inner classes of a class', () => { + const classBefore = 'public class MyClass { class OldInnerClass { } }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'RemovedType', + name: 'OldInnerClass', + }, + ], + }, + ]); + }); + + it('lists new inner interfaces of a class', () => { + const classBefore = 'public class MyClass { }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { interface NewInterface { } }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'NewType', + name: 'NewInterface', + }, + ], + }, + ]); + }); + + it('lists removed inner interfaces of a class', () => { + const classBefore = 'public class MyClass { interface OldInterface { } }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'RemovedType', + name: 'OldInterface', + }, + ], + }, + ]); + }); + + it('lists new inner enums of a class', () => { + const classBefore = 'public class MyClass { }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { enum NewEnum { } }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'NewType', + name: 'NewEnum', + }, + ], + }, + ]); + }); + + it('lists removed inner enums of a class', () => { + const classBefore = 'public class MyClass { interface OldEnum { } }'; + const oldClass = apexTypeFromRawString(classBefore); + const classAfter = 'public class MyClass { }'; + const newClass = apexTypeFromRawString(classAfter); + + const oldVersion = { types: [oldClass] }; + const newVersion = { types: [newClass] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newOrModifiedApexMembers).toEqual([ + { + typeName: 'MyClass', + modifications: [ + { + __typename: 'RemovedType', + name: 'OldEnum', + }, + ], + }, + ]); + }); }); }); diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index 5763644e..d156fc99 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -30,22 +30,26 @@ export type NewOrModifiedMember = { }; export type Changelog = { - newTypes: string[]; - removedTypes: string[]; - newOrModifiedMembers: NewOrModifiedMember[]; + newApexTypes: string[]; + removedApexTypes: string[]; + newOrModifiedApexMembers: NewOrModifiedMember[]; + newCustomObjects: string[]; }; export function hasChanges(changelog: Changelog): boolean { return ( - changelog.newTypes.length > 0 || changelog.removedTypes.length > 0 || changelog.newOrModifiedMembers.length > 0 + changelog.newApexTypes.length > 0 || + changelog.removedApexTypes.length > 0 || + changelog.newOrModifiedApexMembers.length > 0 ); } export function processChangelog(oldVersion: VersionManifest, newVersion: VersionManifest): Changelog { return { - newTypes: getNewTypes(oldVersion, newVersion), - removedTypes: getRemovedTypes(oldVersion, newVersion), - newOrModifiedMembers: getNewOrModifiedMembers(oldVersion, newVersion), + newApexTypes: getNewTypes(oldVersion, newVersion), + removedApexTypes: getRemovedTypes(oldVersion, newVersion), + newOrModifiedApexMembers: getNewOrModifiedMembers(oldVersion, newVersion), + newCustomObjects: [], }; } diff --git a/src/core/changelog/renderable-changelog.ts b/src/core/changelog/renderable-changelog.ts index 2901088b..d96292af 100644 --- a/src/core/changelog/renderable-changelog.ts +++ b/src/core/changelog/renderable-changelog.ts @@ -45,7 +45,7 @@ export function convertToRenderableChangelog( changelog: Changelog, newManifest: (Type | CustomObjectMetadata)[], ): RenderableChangelog { - const allNewTypes = changelog.newTypes.map( + const allNewTypes = changelog.newApexTypes.map( (newType) => newManifest.find((type) => type.name.toLowerCase() === newType.toLowerCase())!, ); @@ -82,15 +82,15 @@ export function convertToRenderableChangelog( } : null, removedTypes: - changelog.removedTypes.length > 0 - ? { heading: 'Removed Types', description: 'These types have been removed.', types: changelog.removedTypes } + changelog.removedApexTypes.length > 0 + ? { heading: 'Removed Types', description: 'These types have been removed.', types: changelog.removedApexTypes } : null, newOrModifiedMembers: - changelog.newOrModifiedMembers.length > 0 + changelog.newOrModifiedApexMembers.length > 0 ? { heading: 'New or Modified Members in Existing Types', description: 'These members have been added or modified.', - modifications: changelog.newOrModifiedMembers.map(toRenderableModification), + modifications: changelog.newOrModifiedApexMembers.map(toRenderableModification), } : null, }; From e3e4ef76a8e41191e7245bb4e90080ae55883fdd Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 08:11:05 -0400 Subject: [PATCH 04/16] Has no removed objects when both the old and new versions are the same --- .../changelog/__test__/processing-changelog.spec.ts | 10 +++++++++- src/core/changelog/process-changelog.ts | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index ab88a6c1..6cf793c0 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -103,7 +103,15 @@ describe('when generating a changelog', () => { expect(changeLog.newCustomObjects).toEqual([]); }); - // [] - Has no removed objects when both the old and new versions are the same + + it('has no removed objects when both the old and new versions are the same', () => { + const oldVersion = { types: [new CustomObjectMetadataBuilder().build()] }; + const newVersion = { types: [new CustomObjectMetadataBuilder().build()] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.removedCustomObjects).toEqual([]); + }); // [] - Lists all new objects // [] - Lists all removed objects // [] - Lists changed object labels diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index d156fc99..8bec9651 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -34,6 +34,7 @@ export type Changelog = { removedApexTypes: string[]; newOrModifiedApexMembers: NewOrModifiedMember[]; newCustomObjects: string[]; + removedCustomObjects: string[]; }; export function hasChanges(changelog: Changelog): boolean { @@ -50,6 +51,7 @@ export function processChangelog(oldVersion: VersionManifest, newVersion: Versio removedApexTypes: getRemovedTypes(oldVersion, newVersion), newOrModifiedApexMembers: getNewOrModifiedMembers(oldVersion, newVersion), newCustomObjects: [], + removedCustomObjects: [], }; } From 6208ed3dc7c55dae92e05a1be6fe6a707a997afd Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 12:01:23 -0400 Subject: [PATCH 05/16] Lists all new custom objects. --- .../__test__/processing-changelog.spec.ts | 12 +++++++- src/core/changelog/process-changelog.ts | 28 +++++++++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 6cf793c0..3ae6bda8 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -112,7 +112,17 @@ describe('when generating a changelog', () => { expect(changeLog.removedCustomObjects).toEqual([]); }); - // [] - Lists all new objects + + it('lists all new custom objects', () => { + const oldVersion = { types: [] }; + const newObject = new CustomObjectMetadataBuilder().build(); + const newVersion = { types: [newObject] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.newCustomObjects).toEqual([newObject.name]); + }); + // [] - Lists all removed objects // [] - Lists changed object labels // [] - Lists all new fields of an object diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index 8bec9651..a649130e 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -47,29 +47,31 @@ export function hasChanges(changelog: Changelog): boolean { export function processChangelog(oldVersion: VersionManifest, newVersion: VersionManifest): Changelog { return { - newApexTypes: getNewTypes(oldVersion, newVersion), - removedApexTypes: getRemovedTypes(oldVersion, newVersion), - newOrModifiedApexMembers: getNewOrModifiedMembers(oldVersion, newVersion), - newCustomObjects: [], + newApexTypes: getNewApexTypes(oldVersion, newVersion), + removedApexTypes: getRemovedApexTypes(oldVersion, newVersion), + newOrModifiedApexMembers: getNewOrModifiedApexMembers(oldVersion, newVersion), + newCustomObjects: getNewCustomObjects(oldVersion, newVersion), removedCustomObjects: [], }; } -function getNewTypes(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { +function getNewApexTypes(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { return newVersion.types + .filter((newType): newType is Type => newType.type_name !== 'customobject') .filter((newType) => !oldVersion.types.some((oldType) => oldType.name.toLowerCase() === newType.name.toLowerCase())) .map((type) => type.name); } -function getRemovedTypes(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { +function getRemovedApexTypes(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { return oldVersion.types + .filter((newType): newType is Type => newType.type_name !== 'customobject') .filter((oldType) => !newVersion.types.some((newType) => newType.name.toLowerCase() === oldType.name.toLowerCase())) .map((type) => type.name); } -function getNewOrModifiedMembers(oldVersion: VersionManifest, newVersion: VersionManifest): NewOrModifiedMember[] { +function getNewOrModifiedApexMembers(oldVersion: VersionManifest, newVersion: VersionManifest): NewOrModifiedMember[] { return pipe( - getTypesInBothVersions(oldVersion, newVersion), + getApexTypesInBothVersions(oldVersion, newVersion), (typesInBoth) => [ ...getNewOrModifiedEnumValues(typesInBoth), ...getNewOrModifiedMethods(typesInBoth), @@ -97,6 +99,13 @@ function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModifiedMem ); } +function getNewCustomObjects(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { + return newVersion.types + .filter((newType): newType is CustomObjectMetadata => newType.type_name === 'customobject') + .filter((newType) => !oldVersion.types.some((oldType) => oldType.name.toLowerCase() === newType.name.toLowerCase())) + .map((type) => type.name); +} + function getNewOrModifiedMethods(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( typesInBoth.filter( @@ -162,8 +171,9 @@ type TypeInBoth = { newType: Type; }; -function getTypesInBothVersions(oldVersion: VersionManifest, newVersion: VersionManifest): TypeInBoth[] { +function getApexTypesInBothVersions(oldVersion: VersionManifest, newVersion: VersionManifest): TypeInBoth[] { return oldVersion.types + .filter((newType): newType is Type => newType.type_name !== 'customobject') .map((oldType) => ({ oldType, newType: newVersion.types.find((newType) => newType.name.toLowerCase() === oldType.name.toLowerCase()), From 97cdded61d43590b0550acc01e066ad1229140ec Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 12:02:52 -0400 Subject: [PATCH 06/16] Lists all removed custom objects --- .../__test__/processing-changelog.spec.ts | 11 ++++++++- src/core/changelog/process-changelog.ts | 23 ++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 3ae6bda8..5e611384 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -123,7 +123,16 @@ describe('when generating a changelog', () => { expect(changeLog.newCustomObjects).toEqual([newObject.name]); }); - // [] - Lists all removed objects + it('lists all removed custom objects', () => { + const oldObject = new CustomObjectMetadataBuilder().build(); + const oldVersion = { types: [oldObject] }; + const newVersion = { types: [] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.removedCustomObjects).toEqual([oldObject.name]); + }); + // [] - Lists changed object labels // [] - Lists all new fields of an object // [] - Lists all removed fields of an object diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index a649130e..2af4ac04 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -51,7 +51,7 @@ export function processChangelog(oldVersion: VersionManifest, newVersion: Versio removedApexTypes: getRemovedApexTypes(oldVersion, newVersion), newOrModifiedApexMembers: getNewOrModifiedApexMembers(oldVersion, newVersion), newCustomObjects: getNewCustomObjects(oldVersion, newVersion), - removedCustomObjects: [], + removedCustomObjects: getRemovedCustomObjects(oldVersion, newVersion), }; } @@ -69,6 +69,20 @@ function getRemovedApexTypes(oldVersion: VersionManifest, newVersion: VersionMan .map((type) => type.name); } +function getNewCustomObjects(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { + return newVersion.types + .filter((newType): newType is CustomObjectMetadata => newType.type_name === 'customobject') + .filter((newType) => !oldVersion.types.some((oldType) => oldType.name.toLowerCase() === newType.name.toLowerCase())) + .map((type) => type.name); +} + +function getRemovedCustomObjects(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { + return oldVersion.types + .filter((newType): newType is CustomObjectMetadata => newType.type_name === 'customobject') + .filter((oldType) => !newVersion.types.some((newType) => newType.name.toLowerCase() === oldType.name.toLowerCase())) + .map((type) => type.name); +} + function getNewOrModifiedApexMembers(oldVersion: VersionManifest, newVersion: VersionManifest): NewOrModifiedMember[] { return pipe( getApexTypesInBothVersions(oldVersion, newVersion), @@ -99,13 +113,6 @@ function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModifiedMem ); } -function getNewCustomObjects(oldVersion: VersionManifest, newVersion: VersionManifest): string[] { - return newVersion.types - .filter((newType): newType is CustomObjectMetadata => newType.type_name === 'customobject') - .filter((newType) => !oldVersion.types.some((oldType) => oldType.name.toLowerCase() === newType.name.toLowerCase())) - .map((type) => type.name); -} - function getNewOrModifiedMethods(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( typesInBoth.filter( From 34361ce77d490b3bbf7d867e073db2a81fba3b3d Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 12:14:34 -0400 Subject: [PATCH 07/16] Lists changed custom object labels. --- .../__test__/processing-changelog.spec.ts | 31 +++++++++++- src/core/changelog/process-changelog.ts | 49 +++++++++++++++---- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 5e611384..bcc36a06 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -12,12 +12,19 @@ function apexTypeFromRawString(raw: string): Type { } class CustomObjectMetadataBuilder { + label: string = 'MyObject'; + + withLabel(label: string): CustomObjectMetadataBuilder { + this.label = label; + return this; + } + build(): CustomObjectMetadata { return { type_name: 'customobject', deploymentStatus: 'Deployed', visibility: 'Public', - label: 'MyObject', + label: this.label, name: 'MyObject', description: null, fields: [], @@ -133,7 +140,27 @@ describe('when generating a changelog', () => { expect(changeLog.removedCustomObjects).toEqual([oldObject.name]); }); - // [] - Lists changed object labels + it('lists changed custom object labels', () => { + const oldObject = new CustomObjectMetadataBuilder().withLabel('OldLabel').build(); + const newObject = new CustomObjectMetadataBuilder().withLabel('NewLabel').build(); + const oldVersion = { types: [oldObject] }; + const newVersion = { types: [newObject] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.customObjectModifications).toEqual([ + { + typeName: oldObject.name, + modifications: [ + { + __typename: 'LabelChanged', + name: 'NewLabel', + }, + ], + }, + ]); + }); + // [] - Lists all new fields of an object // [] - Lists all removed fields of an object // [] - Lists changed field labels diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index 2af4ac04..6f0eedc8 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -17,7 +17,8 @@ type ModificationTypes = | 'NewProperty' | 'RemovedProperty' | 'NewField' - | 'RemovedField'; + | 'RemovedField' + | 'LabelChanged'; export type MemberModificationType = { __typename: ModificationTypes; @@ -35,6 +36,7 @@ export type Changelog = { newOrModifiedApexMembers: NewOrModifiedMember[]; newCustomObjects: string[]; removedCustomObjects: string[]; + customObjectModifications: NewOrModifiedMember[]; }; export function hasChanges(changelog: Changelog): boolean { @@ -52,6 +54,7 @@ export function processChangelog(oldVersion: VersionManifest, newVersion: Versio newOrModifiedApexMembers: getNewOrModifiedApexMembers(oldVersion, newVersion), newCustomObjects: getNewCustomObjects(oldVersion, newVersion), removedCustomObjects: getRemovedCustomObjects(oldVersion, newVersion), + customObjectModifications: getModifiedCustomObjectLabels(oldVersion, newVersion), }; } @@ -95,7 +98,21 @@ function getNewOrModifiedApexMembers(oldVersion: VersionManifest, newVersion: Ve ); } -function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { +function getModifiedCustomObjectLabels( + oldVersion: VersionManifest, + newVersion: VersionManifest, +): NewOrModifiedMember[] { + return pipe(getCustomObjectsInBothVersions(oldVersion, newVersion), (customObjectsInBoth) => + customObjectsInBoth + .filter(({ oldType, newType }) => oldType.label.toLowerCase() !== newType.label.toLowerCase()) + .map(({ newType }) => ({ + typeName: newType.name, + modifications: [{ __typename: 'LabelChanged', name: newType.label }], + })), + ); +} + +function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( typesInBoth.filter((typeInBoth) => typeInBoth.oldType.type_name === 'enum'), (enumsInBoth) => @@ -113,7 +130,7 @@ function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModifiedMem ); } -function getNewOrModifiedMethods(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { +function getNewOrModifiedMethods(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( typesInBoth.filter( (typeInBoth) => typeInBoth.oldType.type_name === 'class' || typeInBoth.oldType.type_name === 'interface', @@ -146,7 +163,7 @@ function getNewOrModifiedMethods(typesInBoth: TypeInBoth[]): NewOrModifiedMember ); } -function getNewOrModifiedClassMembers(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { +function getNewOrModifiedClassMembers(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( typesInBoth.filter((typeInBoth) => typeInBoth.oldType.type_name === 'class'), (classesInBoth) => @@ -173,19 +190,32 @@ function getNewOrModifiedClassMembers(typesInBoth: TypeInBoth[]): NewOrModifiedM ); } -type TypeInBoth = { - oldType: Type; - newType: Type; +type TypeInBoth = { + oldType: T; + newType: T; }; -function getApexTypesInBothVersions(oldVersion: VersionManifest, newVersion: VersionManifest): TypeInBoth[] { +function getApexTypesInBothVersions(oldVersion: VersionManifest, newVersion: VersionManifest): TypeInBoth[] { return oldVersion.types .filter((newType): newType is Type => newType.type_name !== 'customobject') .map((oldType) => ({ oldType, newType: newVersion.types.find((newType) => newType.name.toLowerCase() === oldType.name.toLowerCase()), })) - .filter((type) => type.newType !== undefined) as TypeInBoth[]; + .filter((type): type is TypeInBoth => type.newType !== undefined); +} + +function getCustomObjectsInBothVersions( + oldVersion: VersionManifest, + newVersion: VersionManifest, +): TypeInBoth[] { + return oldVersion.types + .filter((newType): newType is CustomObjectMetadata => newType.type_name === 'customobject') + .map((oldType) => ({ + oldType, + newType: newVersion.types.find((newType) => newType.name.toLowerCase() === oldType.name.toLowerCase()), + })) + .filter((type): type is TypeInBoth => type.newType !== undefined); } type NameAware = { @@ -193,6 +223,7 @@ type NameAware = { }; type AreEqualFn = (oldValue: T, newValue: T) => boolean; + function areEqualByName(oldValue: T, newValue: T): boolean { return oldValue.name.toLowerCase() === newValue.name.toLowerCase(); } From db5c1fb8cac0a51da61bff43d626e06d30e804a9 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 12:43:38 -0400 Subject: [PATCH 08/16] lists all new fields of a custom object --- .../__test__/processing-changelog.spec.ts | 45 +++++++++++- src/core/changelog/process-changelog.ts | 72 ++++++++++++------- src/core/changelog/renderable-changelog.ts | 2 + .../markdown/adapters/type-to-renderable.ts | 2 +- .../sobject/reflect-custom-object-sources.ts | 4 +- .../sobject/reflectCustomFieldsAndObjects.ts | 7 +- src/core/reflection/sort-types-and-members.ts | 7 +- 7 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index bcc36a06..6d5445dc 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -1,6 +1,7 @@ import { processChangelog } from '../process-changelog'; import { reflect, Type } from '@cparra/apex-reflection'; import { CustomObjectMetadata } from '../../reflection/sobject/reflect-custom-object-sources'; +import { CustomFieldMetadata } from '../../reflection/sobject/reflect-custom-field-source'; function apexTypeFromRawString(raw: string): Type { const result = reflect(raw); @@ -11,14 +12,33 @@ function apexTypeFromRawString(raw: string): Type { return result.typeMirror!; } +class CustomFieldMetadataBuilder { + build(): CustomFieldMetadata { + return { + type: 'Text', + type_name: 'customfield', + label: 'My Field', + name: 'MyField', + description: null, + parentName: 'MyObject', + }; + } +} + class CustomObjectMetadataBuilder { label: string = 'MyObject'; + fields: CustomFieldMetadata[] = []; withLabel(label: string): CustomObjectMetadataBuilder { this.label = label; return this; } + withField(field: CustomFieldMetadata): CustomObjectMetadataBuilder { + this.fields.push(field); + return this; + } + build(): CustomObjectMetadata { return { type_name: 'customobject', @@ -27,7 +47,7 @@ class CustomObjectMetadataBuilder { label: this.label, name: 'MyObject', description: null, - fields: [], + fields: this.fields, }; } } @@ -161,7 +181,28 @@ describe('when generating a changelog', () => { ]); }); - // [] - Lists all new fields of an object + it('lists all new fields of a custom object', () => { + const oldObject = new CustomObjectMetadataBuilder().build(); + const newField = new CustomFieldMetadataBuilder().build(); + const newObject = new CustomObjectMetadataBuilder().withField(newField).build(); + const oldVersion = { types: [oldObject] }; + const newVersion = { types: [newObject] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.customObjectModifications).toEqual([ + { + typeName: newObject.name, + modifications: [ + { + __typename: 'NewField', + name: newField.name, + }, + ], + }, + ]); + }); + // [] - Lists all removed fields of an object // [] - Lists changed field labels }); diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index 6f0eedc8..ea0aff16 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -1,4 +1,4 @@ -import { ClassMirror, EnumMirror, MethodMirror, Type } from '@cparra/apex-reflection'; +import { ClassMirror, EnumMirror, InterfaceMirror, MethodMirror, Type } from '@cparra/apex-reflection'; import { pipe } from 'fp-ts/function'; import { areMethodsEqual } from './method-changes-checker'; import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; @@ -54,7 +54,7 @@ export function processChangelog(oldVersion: VersionManifest, newVersion: Versio newOrModifiedApexMembers: getNewOrModifiedApexMembers(oldVersion, newVersion), newCustomObjects: getNewCustomObjects(oldVersion, newVersion), removedCustomObjects: getRemovedCustomObjects(oldVersion, newVersion), - customObjectModifications: getModifiedCustomObjectLabels(oldVersion, newVersion), + customObjectModifications: getCustomObjectModifications(oldVersion, newVersion), }; } @@ -98,27 +98,48 @@ function getNewOrModifiedApexMembers(oldVersion: VersionManifest, newVersion: Ve ); } -function getModifiedCustomObjectLabels( - oldVersion: VersionManifest, - newVersion: VersionManifest, -): NewOrModifiedMember[] { - return pipe(getCustomObjectsInBothVersions(oldVersion, newVersion), (customObjectsInBoth) => - customObjectsInBoth - .filter(({ oldType, newType }) => oldType.label.toLowerCase() !== newType.label.toLowerCase()) - .map(({ newType }) => ({ - typeName: newType.name, - modifications: [{ __typename: 'LabelChanged', name: newType.label }], - })), +function getCustomObjectModifications(oldVersion: VersionManifest, newVersion: VersionManifest): NewOrModifiedMember[] { + return pipe( + getCustomObjectsInBothVersions(oldVersion, newVersion), + (customObjectsInBoth) => [ + ...getModifiedCustomObjectLabels(customObjectsInBoth), + ...getNewOrRemovedCustomFields(customObjectsInBoth), + ], + (customObjectModifications) => customObjectModifications.filter((member) => member.modifications.length > 0), ); } +function getModifiedCustomObjectLabels(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { + return typesInBoth + .filter(({ oldType, newType }) => oldType.label.toLowerCase() !== newType.label.toLowerCase()) + .map(({ newType }) => ({ + typeName: newType.name, + modifications: [{ __typename: 'LabelChanged', name: newType.label }], + })); +} + +function getNewOrRemovedCustomFields(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { + return typesInBoth.map(({ oldType, newType }) => { + const oldCustomObject = oldType; + const newCustomObject = newType; + + return { + typeName: newType.name, + modifications: [ + ...getNewValues(oldCustomObject, newCustomObject, 'fields', 'NewField'), + ...getRemovedValues(oldCustomObject, newCustomObject, 'fields', 'RemovedField'), + ], + }; + }); +} + function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( - typesInBoth.filter((typeInBoth) => typeInBoth.oldType.type_name === 'enum'), + typesInBoth.filter((typeInBoth): typeInBoth is TypeInBoth => typeInBoth.oldType.type_name === 'enum'), (enumsInBoth) => enumsInBoth.map(({ oldType, newType }) => { - const oldEnum = oldType as EnumMirror; - const newEnum = newType as EnumMirror; + const oldEnum = oldType; + const newEnum = newType; return { typeName: newType.name, modifications: [ @@ -133,24 +154,25 @@ function getNewOrModifiedEnumValues(typesInBoth: TypeInBoth[]): NewOrModif function getNewOrModifiedMethods(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return pipe( typesInBoth.filter( - (typeInBoth) => typeInBoth.oldType.type_name === 'class' || typeInBoth.oldType.type_name === 'interface', + (typeInBoth): typeInBoth is TypeInBoth => + typeInBoth.oldType.type_name === 'class' || typeInBoth.oldType.type_name === 'interface', ), (typesInBoth) => typesInBoth.map(({ oldType, newType }) => { - const oldMethodAware = oldType as MethodAware; - const newMethodAware = newType as MethodAware; + const oldMethodAware = oldType; + const newMethodAware = newType; return { typeName: newType.name, modifications: [ - ...getNewValues( + ...getNewValues( oldMethodAware, newMethodAware, 'methods', 'NewMethod', areMethodsEqual, ), - ...getRemovedValues( + ...getRemovedValues( oldMethodAware, newMethodAware, 'methods', @@ -228,12 +250,12 @@ function areEqualByName(oldValue: T, newValue: T): boolean return oldValue.name.toLowerCase() === newValue.name.toLowerCase(); } -function getNewValues, K extends keyof T>( +function getNewValues, K extends keyof T>( oldPlaceToSearch: T, newPlaceToSearch: T, keyToSearch: K, typeName: ModificationTypes, - areEqualFn: AreEqualFn = areEqualByName, + areEqualFn: AreEqualFn = areEqualByName, ): MemberModificationType[] { return newPlaceToSearch[keyToSearch] .filter((newValue) => !oldPlaceToSearch[keyToSearch].some((oldValue) => areEqualFn(oldValue, newValue))) @@ -253,7 +275,3 @@ function getRemovedValues, .map((value) => value.name) .map((name) => ({ __typename: typeName, name })); } - -type MethodAware = { - methods: MethodMirror[]; -}; diff --git a/src/core/changelog/renderable-changelog.ts b/src/core/changelog/renderable-changelog.ts index d96292af..189021c7 100644 --- a/src/core/changelog/renderable-changelog.ts +++ b/src/core/changelog/renderable-changelog.ts @@ -137,5 +137,7 @@ function toRenderableModificationDescription(memberModificationType: MemberModif return `New Type: ${memberModificationType.name}`; case 'RemovedType': return `Removed Type: ${memberModificationType.name}`; + case 'LabelChanged': + return `Label Changed to ${memberModificationType.name}`; } } diff --git a/src/core/markdown/adapters/type-to-renderable.ts b/src/core/markdown/adapters/type-to-renderable.ts index 78b24c3d..66b1d34a 100644 --- a/src/core/markdown/adapters/type-to-renderable.ts +++ b/src/core/markdown/adapters/type-to-renderable.ts @@ -264,7 +264,7 @@ function objectMetadataToRenderable( fields: { headingLevel: 2, heading: 'Fields', - value: objectMetadata.fields.map((field) => fieldMetadataToRenderable(field.type, config, 3)), + value: objectMetadata.fields.map((field) => fieldMetadataToRenderable(field, config, 3)), }, }; } diff --git a/src/core/reflection/sobject/reflect-custom-object-sources.ts b/src/core/reflection/sobject/reflect-custom-object-sources.ts index 698cf9c1..a11a8d7b 100644 --- a/src/core/reflection/sobject/reflect-custom-object-sources.ts +++ b/src/core/reflection/sobject/reflect-custom-object-sources.ts @@ -16,7 +16,7 @@ export type CustomObjectMetadata = { label: string; name: string; description: string | null; - fields: ParsedFile[]; + fields: CustomFieldMetadata[]; }; export function reflectCustomObjectSources( @@ -78,7 +78,7 @@ function toObjectMetadata(parserResult: { CustomObject: object }): CustomObjectM deploymentStatus: 'Deployed', visibility: 'Public', description: null, - fields: [] as ParsedFile[], + fields: [] as CustomFieldMetadata[], }; return { ...defaultValues, ...parserResult.CustomObject } as CustomObjectMetadata; } diff --git a/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts b/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts index 2ec1a336..9cfcec6c 100644 --- a/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts +++ b/src/core/reflection/sobject/reflectCustomFieldsAndObjects.ts @@ -4,10 +4,11 @@ import * as TE from 'fp-ts/TaskEither'; import { ReflectionErrors } from '../../errors/errors'; import { CustomFieldMetadata, reflectCustomFieldSources } from './reflect-custom-field-source'; import { pipe } from 'fp-ts/function'; +import { TaskEither } from 'fp-ts/TaskEither'; export function reflectCustomFieldsAndObjects( objectBundles: (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[], -) { +): TaskEither[]> { function filterNonPublished(parsedFiles: ParsedFile[]): ParsedFile[] { return parsedFiles.filter((parsedFile) => parsedFile.type.deploymentStatus === 'Deployed'); } @@ -45,9 +46,9 @@ export function reflectCustomFieldsAndObjects( ...object, type: { ...object.type, - fields: objectFields, + fields: objectFields.map((field) => field.type), }, - } as ParsedFile; + }; }); }), ); diff --git a/src/core/reflection/sort-types-and-members.ts b/src/core/reflection/sort-types-and-members.ts index da5b5b9e..df185177 100644 --- a/src/core/reflection/sort-types-and-members.ts +++ b/src/core/reflection/sort-types-and-members.ts @@ -2,7 +2,6 @@ import { ClassMirror, EnumMirror, InterfaceMirror, Type } from '@cparra/apex-ref import { ParsedFile } from '../shared/types'; import { isApexType } from '../shared/utils'; import { CustomObjectMetadata } from './sobject/reflect-custom-object-sources'; -import { CustomFieldMetadata } from './sobject/reflect-custom-field-source'; type Named = { name: string }; @@ -45,14 +44,10 @@ function sortTypeMember(type: Type, shouldSort: boolean): Type { function sortCustomObjectFields(type: CustomObjectMetadata, shouldSort: boolean): CustomObjectMetadata { return { ...type, - fields: sortFields(type.fields, shouldSort), + fields: sortNamed(shouldSort, type.fields), }; } -function sortFields(fields: ParsedFile[], shouldSort: boolean): ParsedFile[] { - return fields.sort((a, b) => sortByNames(shouldSort, a.type, b.type)); -} - function sortEnumValues(shouldSort: boolean, enumType: EnumMirror): EnumMirror { return { ...enumType, From d1eb39cfef3b90206c1d6e056bc211acbc93daeb Mon Sep 17 00:00:00 2001 From: cesarParra Date: Wed, 23 Oct 2024 09:19:03 -0400 Subject: [PATCH 09/16] Lists all removed fields of a custom object --- .../__test__/processing-changelog.spec.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 6d5445dc..b94148be 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -203,7 +203,27 @@ describe('when generating a changelog', () => { ]); }); - // [] - Lists all removed fields of an object + it('lists all removed fields of a custom object', () => { + const oldField = new CustomFieldMetadataBuilder().build(); + const oldObject = new CustomObjectMetadataBuilder().withField(oldField).build(); + const newObject = new CustomObjectMetadataBuilder().build(); + const oldVersion = { types: [oldObject] }; + const newVersion = { types: [newObject] }; + + const changeLog = processChangelog(oldVersion, newVersion); + + expect(changeLog.customObjectModifications).toEqual([ + { + typeName: oldObject.name, + modifications: [ + { + __typename: 'RemovedField', + name: oldField.name, + }, + ], + }, + ]); + }); // [] - Lists changed field labels }); From 6a8a702bfdcc7ab7287210eba8650da567193e48 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Wed, 23 Oct 2024 09:52:32 -0400 Subject: [PATCH 10/16] including new custom objects section --- .../__test__/generating-change-log.spec.ts | 31 +++++++++++++++++-- .../__test__/processing-changelog.spec.ts | 5 ++- src/core/changelog/process-changelog.ts | 4 +-- src/core/changelog/renderable-changelog.ts | 24 ++++++++++++-- .../changelog/templates/changelog-template.ts | 12 +++++++ 5 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index d15d448d..fff05a4a 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -1,4 +1,4 @@ -import { UnparsedApexBundle } from '../../shared/types'; +import { UnparsedApexBundle, UnparsedCustomObjectBundle } from '../../shared/types'; import { ChangeLogPageData, generateChangeLog } from '../generate-change-log'; import { assertEither } from '../../test-helpers/assert-either'; import { isSkip } from '../../shared/utils'; @@ -13,7 +13,19 @@ const config = { skipIfNoChanges: false, }; -// TODO: new integration tests here +export function customObjectGenerator( + config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, +) { + return ` + + + ${config.deploymentStatus} + test object for testing + + MyFirstObjects + ${config.visibility} + `; +} describe('when generating a changelog', () => { it('should not skip when skipIfNoChanges, even if there are no changes', async () => { @@ -185,6 +197,21 @@ describe('when generating a changelog', () => { }); }); + describe('that include new custom objects', () => { + it('should include a section for new custom objects', async () => { + const newObjectSource = customObjectGenerator(); + + const oldBundle: UnparsedCustomObjectBundle[] = []; + const newBundle: UnparsedCustomObjectBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, + ]; + + const result = await generateChangeLog(oldBundle, newBundle, config)(); + + assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('## New Custom Objects')); + }); + }); + describe('that includes new types out of scope', () => { it('should not include them', async () => { const newClassSource = 'class Test {}'; diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index b94148be..1decde2a 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -17,7 +17,7 @@ class CustomFieldMetadataBuilder { return { type: 'Text', type_name: 'customfield', - label: 'My Field', + label: 'MyField', name: 'MyField', description: null, parentName: 'MyObject', @@ -173,7 +173,7 @@ describe('when generating a changelog', () => { typeName: oldObject.name, modifications: [ { - __typename: 'LabelChanged', + __typename: 'CustomObjectLabelChanged', name: 'NewLabel', }, ], @@ -224,7 +224,6 @@ describe('when generating a changelog', () => { }, ]); }); - // [] - Lists changed field labels }); describe('with enum code', () => { diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index ea0aff16..d2012c51 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -18,7 +18,7 @@ type ModificationTypes = | 'RemovedProperty' | 'NewField' | 'RemovedField' - | 'LabelChanged'; + | 'CustomObjectLabelChanged'; export type MemberModificationType = { __typename: ModificationTypes; @@ -114,7 +114,7 @@ function getModifiedCustomObjectLabels(typesInBoth: TypeInBoth oldType.label.toLowerCase() !== newType.label.toLowerCase()) .map(({ newType }) => ({ typeName: newType.name, - modifications: [{ __typename: 'LabelChanged', name: newType.label }], + modifications: [{ __typename: 'CustomObjectLabelChanged', name: newType.label }], })); } diff --git a/src/core/changelog/renderable-changelog.ts b/src/core/changelog/renderable-changelog.ts index 189021c7..42c7f41b 100644 --- a/src/core/changelog/renderable-changelog.ts +++ b/src/core/changelog/renderable-changelog.ts @@ -9,7 +9,7 @@ type NewTypeRenderable = { description?: RenderableContent[]; }; -type NewTypeSection = { +type NewTypeSection = { __type: T; heading: string; description: string; @@ -39,19 +39,25 @@ export type RenderableChangelog = { newEnums: NewTypeSection<'enum'> | null; removedTypes: RemovedTypeSection | null; newOrModifiedMembers: NewOrModifiedMembersSection | null; + newCustomObjects: NewTypeSection<'customobject'> | null; + // todo: removed custom objects + // todo: changed custom objects }; export function convertToRenderableChangelog( changelog: Changelog, newManifest: (Type | CustomObjectMetadata)[], ): RenderableChangelog { - const allNewTypes = changelog.newApexTypes.map( + const allNewTypes = [...changelog.newApexTypes, ...changelog.newCustomObjects].map( (newType) => newManifest.find((type) => type.name.toLowerCase() === newType.toLowerCase())!, ); const newClasses = allNewTypes.filter((type): type is ClassMirror => type.type_name === 'class'); const newInterfaces = allNewTypes.filter((type): type is InterfaceMirror => type.type_name === 'interface'); const newEnums = allNewTypes.filter((type): type is EnumMirror => type.type_name === 'enum'); + const newCustomObjects = allNewTypes.filter( + (type): type is CustomObjectMetadata => type.type_name === 'customobject', + ); return { newClasses: @@ -93,6 +99,18 @@ export function convertToRenderableChangelog( modifications: changelog.newOrModifiedApexMembers.map(toRenderableModification), } : null, + newCustomObjects: + newCustomObjects.length > 0 + ? { + __type: 'customobject', + heading: 'New Custom Objects', + description: 'These custom objects are new.', + types: newCustomObjects.map((type) => ({ + name: type.name, + description: type.description ? [type.description] : undefined, + })), + } + : null, }; } @@ -137,7 +155,7 @@ function toRenderableModificationDescription(memberModificationType: MemberModif return `New Type: ${memberModificationType.name}`; case 'RemovedType': return `Removed Type: ${memberModificationType.name}`; - case 'LabelChanged': + case 'CustomObjectLabelChanged': return `Label Changed to ${memberModificationType.name}`; } } diff --git a/src/core/changelog/templates/changelog-template.ts b/src/core/changelog/templates/changelog-template.ts index 3d982da2..bf251722 100644 --- a/src/core/changelog/templates/changelog-template.ts +++ b/src/core/changelog/templates/changelog-template.ts @@ -37,6 +37,18 @@ export const changelogTemplate = ` {{/each}} {{/if}} +{{#if newCustomObjects}} +## {{newCustomObjects.heading}} + +{{newCustomObjects.description}} + +{{#each newCustomObjects.types}} +### {{this.name}} + +{{{renderContent this.description}}} +{{/each}} +{{/if}} + {{#if removedTypes}} ## Removed Types From 00435fdb853fea90f4c6f64d12d014771a507ff1 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 29 Oct 2024 07:01:29 -0400 Subject: [PATCH 11/16] UT fix --- src/core/changelog/__test__/processing-changelog.spec.ts | 5 ++++- src/core/changelog/process-changelog.ts | 4 ++-- src/core/reflection/sobject/reflect-custom-object-sources.ts | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 1decde2a..792108be 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -168,13 +168,16 @@ describe('when generating a changelog', () => { const changeLog = processChangelog(oldVersion, newVersion); + // TODO: The changelog should display the old label and the new label + // TODO: Same deal with fields + expect(changeLog.customObjectModifications).toEqual([ { typeName: oldObject.name, modifications: [ { __typename: 'CustomObjectLabelChanged', - name: 'NewLabel', + name: newObject.name, }, ], }, diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index d2012c51..f2bdaa69 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -111,10 +111,10 @@ function getCustomObjectModifications(oldVersion: VersionManifest, newVersion: V function getModifiedCustomObjectLabels(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return typesInBoth - .filter(({ oldType, newType }) => oldType.label.toLowerCase() !== newType.label.toLowerCase()) + .filter(({ oldType, newType }) => oldType.label?.toLowerCase() !== newType.label?.toLowerCase()) .map(({ newType }) => ({ typeName: newType.name, - modifications: [{ __typename: 'CustomObjectLabelChanged', name: newType.label }], + modifications: [{ __typename: 'CustomObjectLabelChanged', name: newType.name }], })); } diff --git a/src/core/reflection/sobject/reflect-custom-object-sources.ts b/src/core/reflection/sobject/reflect-custom-object-sources.ts index b13a8df9..f48ed6e9 100644 --- a/src/core/reflection/sobject/reflect-custom-object-sources.ts +++ b/src/core/reflection/sobject/reflect-custom-object-sources.ts @@ -59,7 +59,7 @@ function validate(parseResult: unknown): E.Either Date: Tue, 29 Oct 2024 07:25:24 -0400 Subject: [PATCH 12/16] Includes the removed custom object section --- .../Contact/fields/PhotoUrl__c.field-meta.xml | 9 + .../objects/Event__c/Event__c.object-meta.xml | 167 +++++++++++++++++ .../fields/Description__c.field-meta.xml | 10 ++ .../fields/End_Date__c.field-meta.xml | 9 + .../fields/Location__c.field-meta.xml | 11 ++ .../fields/Start_Date__c.field-meta.xml | 9 + .../fields/Tag_Line__c.field-meta.xml | 11 ++ .../Price_Component__c.object-meta.xml | 169 +++++++++++++++++ .../fields/Description__c.field-meta.xml | 11 ++ .../fields/Expression__c.field-meta.xml | 12 ++ .../fields/Percent__c.field-meta.xml | 13 ++ .../fields/Price__c.field-meta.xml | 13 ++ .../fields/Type__c.field-meta.xml | 30 ++++ ...Product_Price_Component__c.object-meta.xml | 166 +++++++++++++++++ .../fields/Price_Component__c.field-meta.xml | 14 ++ .../fields/Product__c.field-meta.xml | 14 ++ .../Product__c/Product__c.object-meta.xml | 169 +++++++++++++++++ .../fields/Description__c.field-meta.xml | 11 ++ .../Product__c/fields/Event__c.field-meta.xml | 12 ++ .../fields/Features__c.field-meta.xml | 10 ++ .../Sales_Order_Line__c.object-meta.xml | 167 +++++++++++++++++ .../fields/Amount__c.field-meta.xml | 11 ++ .../fields/Product__c.field-meta.xml | 13 ++ .../fields/Sales_Order__c.field-meta.xml | 14 ++ .../Source_Price_Component__c.field-meta.xml | 13 ++ .../fields/Type__c.field-meta.xml | 26 +++ .../Sales_Order__c.object-meta.xml | 170 ++++++++++++++++++ .../Speaker__c/Speaker__c.object-meta.xml | 167 +++++++++++++++++ .../Speaker__c/fields/About__c.field-meta.xml | 10 ++ .../Speaker__c/fields/Event__c.field-meta.xml | 14 ++ .../fields/Person__c.field-meta.xml | 14 ++ examples/changelog/docs/changelog.md | 14 ++ .../{ => classes}/OldImplementation.cls | 0 .../OldImplementation.cls-meta.xml | 0 .../previous/{ => classes}/SolidService.cls | 0 .../{ => classes}/SolidService.cls-meta.xml | 0 .../objects/Event__c/Event__c.object-meta.xml | 167 +++++++++++++++++ .../fields/End_Date__c.field-meta.xml | 9 + .../fields/Location__c.field-meta.xml | 11 ++ .../fields/Start_Date__c.field-meta.xml | 9 + .../Price_Component__c.object-meta.xml | 169 +++++++++++++++++ .../fields/Expression__c.field-meta.xml | 12 ++ .../fields/Percent__c.field-meta.xml | 13 ++ .../fields/Price__c.field-meta.xml | 13 ++ .../fields/Type__c.field-meta.xml | 30 ++++ ...Product_Price_Component__c.object-meta.xml | 166 +++++++++++++++++ .../fields/Price_Component__c.field-meta.xml | 14 ++ .../fields/Product__c.field-meta.xml | 14 ++ .../Product__c/Product__c.object-meta.xml | 169 +++++++++++++++++ .../Product__c/fields/Event__c.field-meta.xml | 12 ++ .../fields/Features__c.field-meta.xml | 10 ++ examples/vitepress/docs/changelog.md | 26 ++- .../__test__/generating-change-log.spec.ts | 87 ++++++++- src/core/changelog/renderable-changelog.ts | 13 +- .../changelog/templates/changelog-template.ts | 12 +- 55 files changed, 2455 insertions(+), 4 deletions(-) create mode 100644 examples/changelog/current/objects/Contact/fields/PhotoUrl__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Event__c/Event__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Event__c/fields/Description__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Event__c/fields/End_Date__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Event__c/fields/Location__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Event__c/fields/Start_Date__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Event__c/fields/Tag_Line__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Price_Component__c/Price_Component__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Price_Component__c/fields/Description__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Price_Component__c/fields/Expression__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Price_Component__c/fields/Percent__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Price_Component__c/fields/Price__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Price_Component__c/fields/Type__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Product__c/Product__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Product__c/fields/Description__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Product__c/fields/Event__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Product__c/fields/Features__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Sales_Order__c/Sales_Order__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Speaker__c/Speaker__c.object-meta.xml create mode 100644 examples/changelog/current/objects/Speaker__c/fields/About__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Speaker__c/fields/Event__c.field-meta.xml create mode 100644 examples/changelog/current/objects/Speaker__c/fields/Person__c.field-meta.xml rename examples/changelog/previous/{ => classes}/OldImplementation.cls (100%) rename examples/changelog/previous/{ => classes}/OldImplementation.cls-meta.xml (100%) rename examples/changelog/previous/{ => classes}/SolidService.cls (100%) rename examples/changelog/previous/{ => classes}/SolidService.cls-meta.xml (100%) create mode 100644 examples/changelog/previous/objects/Event__c/Event__c.object-meta.xml create mode 100644 examples/changelog/previous/objects/Event__c/fields/End_Date__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Event__c/fields/Location__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Event__c/fields/Start_Date__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Price_Component__c/Price_Component__c.object-meta.xml create mode 100644 examples/changelog/previous/objects/Price_Component__c/fields/Expression__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Price_Component__c/fields/Percent__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Price_Component__c/fields/Price__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Price_Component__c/fields/Type__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml create mode 100644 examples/changelog/previous/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Product__c/Product__c.object-meta.xml create mode 100644 examples/changelog/previous/objects/Product__c/fields/Event__c.field-meta.xml create mode 100644 examples/changelog/previous/objects/Product__c/fields/Features__c.field-meta.xml diff --git a/examples/changelog/current/objects/Contact/fields/PhotoUrl__c.field-meta.xml b/examples/changelog/current/objects/Contact/fields/PhotoUrl__c.field-meta.xml new file mode 100644 index 00000000..a9117781 --- /dev/null +++ b/examples/changelog/current/objects/Contact/fields/PhotoUrl__c.field-meta.xml @@ -0,0 +1,9 @@ + + + PhotoUrl__c + false + + false + false + Url + diff --git a/examples/changelog/current/objects/Event__c/Event__c.object-meta.xml b/examples/changelog/current/objects/Event__c/Event__c.object-meta.xml new file mode 100644 index 00000000..d30dde5c --- /dev/null +++ b/examples/changelog/current/objects/Event__c/Event__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + + + Text + + Events + Represents an event that people can register for. + + ReadWrite + Vowel + Public + diff --git a/examples/changelog/current/objects/Event__c/fields/Description__c.field-meta.xml b/examples/changelog/current/objects/Event__c/fields/Description__c.field-meta.xml new file mode 100644 index 00000000..c1b682a4 --- /dev/null +++ b/examples/changelog/current/objects/Event__c/fields/Description__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Description__c + false + + 32768 + false + LongTextArea + 10 + diff --git a/examples/changelog/current/objects/Event__c/fields/End_Date__c.field-meta.xml b/examples/changelog/current/objects/Event__c/fields/End_Date__c.field-meta.xml new file mode 100644 index 00000000..422a0003 --- /dev/null +++ b/examples/changelog/current/objects/Event__c/fields/End_Date__c.field-meta.xml @@ -0,0 +1,9 @@ + + + End_Date__c + false + + true + false + Date + diff --git a/examples/changelog/current/objects/Event__c/fields/Location__c.field-meta.xml b/examples/changelog/current/objects/Event__c/fields/Location__c.field-meta.xml new file mode 100644 index 00000000..b8f32121 --- /dev/null +++ b/examples/changelog/current/objects/Event__c/fields/Location__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Location__c + false + false + + true + 3 + false + Location + diff --git a/examples/changelog/current/objects/Event__c/fields/Start_Date__c.field-meta.xml b/examples/changelog/current/objects/Event__c/fields/Start_Date__c.field-meta.xml new file mode 100644 index 00000000..81fb3f6d --- /dev/null +++ b/examples/changelog/current/objects/Event__c/fields/Start_Date__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Start_Date__c + false + + true + false + Date + diff --git a/examples/changelog/current/objects/Event__c/fields/Tag_Line__c.field-meta.xml b/examples/changelog/current/objects/Event__c/fields/Tag_Line__c.field-meta.xml new file mode 100644 index 00000000..652ee2e0 --- /dev/null +++ b/examples/changelog/current/objects/Event__c/fields/Tag_Line__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Tag_Line__c + false + + 255 + false + false + Text + false + diff --git a/examples/changelog/current/objects/Price_Component__c/Price_Component__c.object-meta.xml b/examples/changelog/current/objects/Price_Component__c/Price_Component__c.object-meta.xml new file mode 100644 index 00000000..ae72fd0c --- /dev/null +++ b/examples/changelog/current/objects/Price_Component__c/Price_Component__c.object-meta.xml @@ -0,0 +1,169 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Price_Component_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + + PC-{0000} + + AutoNumber + + Price Components + + ReadWrite + Public + diff --git a/examples/changelog/current/objects/Price_Component__c/fields/Description__c.field-meta.xml b/examples/changelog/current/objects/Price_Component__c/fields/Description__c.field-meta.xml new file mode 100644 index 00000000..69050ca6 --- /dev/null +++ b/examples/changelog/current/objects/Price_Component__c/fields/Description__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Description__c + false + + 255 + false + false + Text + false + diff --git a/examples/changelog/current/objects/Price_Component__c/fields/Expression__c.field-meta.xml b/examples/changelog/current/objects/Price_Component__c/fields/Expression__c.field-meta.xml new file mode 100644 index 00000000..c0bf4e45 --- /dev/null +++ b/examples/changelog/current/objects/Price_Component__c/fields/Expression__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Expression__c + The Expression that determines if this price should take effect or not. + false + The Expression that determines if this price should take effect or not. + + 131072 + false + LongTextArea + 20 + diff --git a/examples/changelog/current/objects/Price_Component__c/fields/Percent__c.field-meta.xml b/examples/changelog/current/objects/Price_Component__c/fields/Percent__c.field-meta.xml new file mode 100644 index 00000000..9c303bc4 --- /dev/null +++ b/examples/changelog/current/objects/Price_Component__c/fields/Percent__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Percent__c + Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + false + Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + + 18 + false + 0 + false + Percent + diff --git a/examples/changelog/current/objects/Price_Component__c/fields/Price__c.field-meta.xml b/examples/changelog/current/objects/Price_Component__c/fields/Price__c.field-meta.xml new file mode 100644 index 00000000..84136dec --- /dev/null +++ b/examples/changelog/current/objects/Price_Component__c/fields/Price__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Price__c + Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + false + Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + + 18 + false + 2 + false + Currency + diff --git a/examples/changelog/current/objects/Price_Component__c/fields/Type__c.field-meta.xml b/examples/changelog/current/objects/Price_Component__c/fields/Type__c.field-meta.xml new file mode 100644 index 00000000..c430b305 --- /dev/null +++ b/examples/changelog/current/objects/Price_Component__c/fields/Type__c.field-meta.xml @@ -0,0 +1,30 @@ + + + Type__c + false + + true + false + Picklist + + true + + false + + List Price + false + + + + Surcharge + false + + + + Discount + false + + + + + diff --git a/examples/changelog/current/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml b/examples/changelog/current/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml new file mode 100644 index 00000000..8a9a6348 --- /dev/null +++ b/examples/changelog/current/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml @@ -0,0 +1,166 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + + PPC-{0000} + + AutoNumber + + Product Price Components + + ControlledByParent + Public + diff --git a/examples/changelog/current/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml b/examples/changelog/current/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml new file mode 100644 index 00000000..f152ecb6 --- /dev/null +++ b/examples/changelog/current/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Price_Component__c + false + + Price_Component__c + Product Price Components + Product_Price_Components + 1 + false + false + MasterDetail + false + diff --git a/examples/changelog/current/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml b/examples/changelog/current/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml new file mode 100644 index 00000000..16ec5b33 --- /dev/null +++ b/examples/changelog/current/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Product__c + false + + Product__c + Product Price Components + Product_Price_Components + 0 + false + false + MasterDetail + false + diff --git a/examples/changelog/current/objects/Product__c/Product__c.object-meta.xml b/examples/changelog/current/objects/Product__c/Product__c.object-meta.xml new file mode 100644 index 00000000..cdeb52a9 --- /dev/null +++ b/examples/changelog/current/objects/Product__c/Product__c.object-meta.xml @@ -0,0 +1,169 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Product_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + Product that is sold or available for sale. + + + Text + + Products + + ReadWrite + Public + diff --git a/examples/changelog/current/objects/Product__c/fields/Description__c.field-meta.xml b/examples/changelog/current/objects/Product__c/fields/Description__c.field-meta.xml new file mode 100644 index 00000000..69050ca6 --- /dev/null +++ b/examples/changelog/current/objects/Product__c/fields/Description__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Description__c + false + + 255 + false + false + Text + false + diff --git a/examples/changelog/current/objects/Product__c/fields/Event__c.field-meta.xml b/examples/changelog/current/objects/Product__c/fields/Event__c.field-meta.xml new file mode 100644 index 00000000..82947d0b --- /dev/null +++ b/examples/changelog/current/objects/Product__c/fields/Event__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Event__c + Restrict + false + + Event__c + Products + true + false + Lookup + diff --git a/examples/changelog/current/objects/Product__c/fields/Features__c.field-meta.xml b/examples/changelog/current/objects/Product__c/fields/Features__c.field-meta.xml new file mode 100644 index 00000000..6b67a859 --- /dev/null +++ b/examples/changelog/current/objects/Product__c/fields/Features__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Features__c + false + + 32768 + false + LongTextArea + 10 + diff --git a/examples/changelog/current/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml b/examples/changelog/current/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml new file mode 100644 index 00000000..36e9348d --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order_Line__c/Sales_Order_Line__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + Represents a line item on a sales order. + + SOL-{0000} + + AutoNumber + + Sales Order Lines + + ControlledByParent + Public + diff --git a/examples/changelog/current/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml new file mode 100644 index 00000000..3a464e2d --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Amount__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Amount__c + false + + 18 + true + 2 + false + Currency + diff --git a/examples/changelog/current/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml new file mode 100644 index 00000000..b6b5369f --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Product__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Product__c + Restrict + false + + Product__c + Sales Order Lines + Sales_Order_Lines + true + false + Lookup + diff --git a/examples/changelog/current/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml new file mode 100644 index 00000000..c1d881c8 --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Sales_Order__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Sales_Order__c + false + + Sales_Order__c + Sales Order Lines + Sales_Order_Lines + 0 + false + false + MasterDetail + false + diff --git a/examples/changelog/current/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml new file mode 100644 index 00000000..69817d96 --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Source_Price_Component__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Source_Price_Component__c + SetNull + false + + Price_Component__c + Sales Order Lines + Sales_Order_Lines + false + false + Lookup + diff --git a/examples/changelog/current/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml new file mode 100644 index 00000000..328b5529 --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order_Line__c/fields/Type__c.field-meta.xml @@ -0,0 +1,26 @@ + + + Type__c + "Charge" + false + + true + false + Picklist + + true + + false + + Charge + false + + + + Discount + false + + + + + diff --git a/examples/changelog/current/objects/Sales_Order__c/Sales_Order__c.object-meta.xml b/examples/changelog/current/objects/Sales_Order__c/Sales_Order__c.object-meta.xml new file mode 100644 index 00000000..2225e4f9 --- /dev/null +++ b/examples/changelog/current/objects/Sales_Order__c/Sales_Order__c.object-meta.xml @@ -0,0 +1,170 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Sales_Order_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + Custom object for tracking sales orders. + + SO-{0000} + + AutoNumber + + Sales Orders + + ReadWrite + Public + diff --git a/examples/changelog/current/objects/Speaker__c/Speaker__c.object-meta.xml b/examples/changelog/current/objects/Speaker__c/Speaker__c.object-meta.xml new file mode 100644 index 00000000..6bdf2199 --- /dev/null +++ b/examples/changelog/current/objects/Speaker__c/Speaker__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + Represents a speaker at an event. + + SPEAK-{0000} + + AutoNumber + + Speakers + + ControlledByParent + Public + diff --git a/examples/changelog/current/objects/Speaker__c/fields/About__c.field-meta.xml b/examples/changelog/current/objects/Speaker__c/fields/About__c.field-meta.xml new file mode 100644 index 00000000..2fc71d94 --- /dev/null +++ b/examples/changelog/current/objects/Speaker__c/fields/About__c.field-meta.xml @@ -0,0 +1,10 @@ + + + About__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/examples/changelog/current/objects/Speaker__c/fields/Event__c.field-meta.xml b/examples/changelog/current/objects/Speaker__c/fields/Event__c.field-meta.xml new file mode 100644 index 00000000..cf6bfc63 --- /dev/null +++ b/examples/changelog/current/objects/Speaker__c/fields/Event__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Event__c + false + + Event__c + Speakers + Speakers + 0 + false + false + MasterDetail + false + diff --git a/examples/changelog/current/objects/Speaker__c/fields/Person__c.field-meta.xml b/examples/changelog/current/objects/Speaker__c/fields/Person__c.field-meta.xml new file mode 100644 index 00000000..b7ac07b1 --- /dev/null +++ b/examples/changelog/current/objects/Speaker__c/fields/Person__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Person__c + false + + Contact + Speakers + Speakers + 1 + false + false + MasterDetail + false + diff --git a/examples/changelog/docs/changelog.md b/examples/changelog/docs/changelog.md index f25caaf8..82370e53 100644 --- a/examples/changelog/docs/changelog.md +++ b/examples/changelog/docs/changelog.md @@ -22,6 +22,20 @@ These enums are new. ### PossibleValues +## New Custom Objects + +These custom objects are new. + +### Sales_Order_Line__c + +Represents a line item on a sales order. +### Sales_Order__c + +Custom object for tracking sales orders. +### Speaker__c + +Represents a speaker at an event. + ## Removed Types These types have been removed. diff --git a/examples/changelog/previous/OldImplementation.cls b/examples/changelog/previous/classes/OldImplementation.cls similarity index 100% rename from examples/changelog/previous/OldImplementation.cls rename to examples/changelog/previous/classes/OldImplementation.cls diff --git a/examples/changelog/previous/OldImplementation.cls-meta.xml b/examples/changelog/previous/classes/OldImplementation.cls-meta.xml similarity index 100% rename from examples/changelog/previous/OldImplementation.cls-meta.xml rename to examples/changelog/previous/classes/OldImplementation.cls-meta.xml diff --git a/examples/changelog/previous/SolidService.cls b/examples/changelog/previous/classes/SolidService.cls similarity index 100% rename from examples/changelog/previous/SolidService.cls rename to examples/changelog/previous/classes/SolidService.cls diff --git a/examples/changelog/previous/SolidService.cls-meta.xml b/examples/changelog/previous/classes/SolidService.cls-meta.xml similarity index 100% rename from examples/changelog/previous/SolidService.cls-meta.xml rename to examples/changelog/previous/classes/SolidService.cls-meta.xml diff --git a/examples/changelog/previous/objects/Event__c/Event__c.object-meta.xml b/examples/changelog/previous/objects/Event__c/Event__c.object-meta.xml new file mode 100644 index 00000000..d30dde5c --- /dev/null +++ b/examples/changelog/previous/objects/Event__c/Event__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + + + Text + + Events + Represents an event that people can register for. + + ReadWrite + Vowel + Public + diff --git a/examples/changelog/previous/objects/Event__c/fields/End_Date__c.field-meta.xml b/examples/changelog/previous/objects/Event__c/fields/End_Date__c.field-meta.xml new file mode 100644 index 00000000..422a0003 --- /dev/null +++ b/examples/changelog/previous/objects/Event__c/fields/End_Date__c.field-meta.xml @@ -0,0 +1,9 @@ + + + End_Date__c + false + + true + false + Date + diff --git a/examples/changelog/previous/objects/Event__c/fields/Location__c.field-meta.xml b/examples/changelog/previous/objects/Event__c/fields/Location__c.field-meta.xml new file mode 100644 index 00000000..b8f32121 --- /dev/null +++ b/examples/changelog/previous/objects/Event__c/fields/Location__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Location__c + false + false + + true + 3 + false + Location + diff --git a/examples/changelog/previous/objects/Event__c/fields/Start_Date__c.field-meta.xml b/examples/changelog/previous/objects/Event__c/fields/Start_Date__c.field-meta.xml new file mode 100644 index 00000000..81fb3f6d --- /dev/null +++ b/examples/changelog/previous/objects/Event__c/fields/Start_Date__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Start_Date__c + false + + true + false + Date + diff --git a/examples/changelog/previous/objects/Price_Component__c/Price_Component__c.object-meta.xml b/examples/changelog/previous/objects/Price_Component__c/Price_Component__c.object-meta.xml new file mode 100644 index 00000000..ae72fd0c --- /dev/null +++ b/examples/changelog/previous/objects/Price_Component__c/Price_Component__c.object-meta.xml @@ -0,0 +1,169 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Price_Component_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + + PC-{0000} + + AutoNumber + + Price Components + + ReadWrite + Public + diff --git a/examples/changelog/previous/objects/Price_Component__c/fields/Expression__c.field-meta.xml b/examples/changelog/previous/objects/Price_Component__c/fields/Expression__c.field-meta.xml new file mode 100644 index 00000000..c0bf4e45 --- /dev/null +++ b/examples/changelog/previous/objects/Price_Component__c/fields/Expression__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Expression__c + The Expression that determines if this price should take effect or not. + false + The Expression that determines if this price should take effect or not. + + 131072 + false + LongTextArea + 20 + diff --git a/examples/changelog/previous/objects/Price_Component__c/fields/Percent__c.field-meta.xml b/examples/changelog/previous/objects/Price_Component__c/fields/Percent__c.field-meta.xml new file mode 100644 index 00000000..9c303bc4 --- /dev/null +++ b/examples/changelog/previous/objects/Price_Component__c/fields/Percent__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Percent__c + Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + false + Use this field to calculate the price based on the list price's percentage instead of providing a flat price. + + 18 + false + 0 + false + Percent + diff --git a/examples/changelog/previous/objects/Price_Component__c/fields/Price__c.field-meta.xml b/examples/changelog/previous/objects/Price_Component__c/fields/Price__c.field-meta.xml new file mode 100644 index 00000000..84136dec --- /dev/null +++ b/examples/changelog/previous/objects/Price_Component__c/fields/Price__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Price__c + Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + false + Use this when the Price Component represents a Flat Price. To represent a Percentage use the Percent field. + + 18 + false + 2 + false + Currency + diff --git a/examples/changelog/previous/objects/Price_Component__c/fields/Type__c.field-meta.xml b/examples/changelog/previous/objects/Price_Component__c/fields/Type__c.field-meta.xml new file mode 100644 index 00000000..c430b305 --- /dev/null +++ b/examples/changelog/previous/objects/Price_Component__c/fields/Type__c.field-meta.xml @@ -0,0 +1,30 @@ + + + Type__c + false + + true + false + Picklist + + true + + false + + List Price + false + + + + Surcharge + false + + + + Discount + false + + + + + diff --git a/examples/changelog/previous/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml b/examples/changelog/previous/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml new file mode 100644 index 00000000..8a9a6348 --- /dev/null +++ b/examples/changelog/previous/objects/Product_Price_Component__c/Product_Price_Component__c.object-meta.xml @@ -0,0 +1,166 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + ControlledByParent + + + PPC-{0000} + + AutoNumber + + Product Price Components + + ControlledByParent + Public + diff --git a/examples/changelog/previous/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml b/examples/changelog/previous/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml new file mode 100644 index 00000000..f152ecb6 --- /dev/null +++ b/examples/changelog/previous/objects/Product_Price_Component__c/fields/Price_Component__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Price_Component__c + false + + Price_Component__c + Product Price Components + Product_Price_Components + 1 + false + false + MasterDetail + false + diff --git a/examples/changelog/previous/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml b/examples/changelog/previous/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml new file mode 100644 index 00000000..16ec5b33 --- /dev/null +++ b/examples/changelog/previous/objects/Product_Price_Component__c/fields/Product__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Product__c + false + + Product__c + Product Price Components + Product_Price_Components + 0 + false + false + MasterDetail + false + diff --git a/examples/changelog/previous/objects/Product__c/Product__c.object-meta.xml b/examples/changelog/previous/objects/Product__c/Product__c.object-meta.xml new file mode 100644 index 00000000..cdeb52a9 --- /dev/null +++ b/examples/changelog/previous/objects/Product__c/Product__c.object-meta.xml @@ -0,0 +1,169 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Action override created by Lightning App Builder during activation. + Product_Record_Page + Large + false + Flexipage + + + View + Default + + + View + Small + Default + + false + SYSTEM + Deployed + false + true + false + false + false + false + false + true + true + Private + + Product that is sold or available for sale. + + + Text + + Products + + ReadWrite + Public + diff --git a/examples/changelog/previous/objects/Product__c/fields/Event__c.field-meta.xml b/examples/changelog/previous/objects/Product__c/fields/Event__c.field-meta.xml new file mode 100644 index 00000000..82947d0b --- /dev/null +++ b/examples/changelog/previous/objects/Product__c/fields/Event__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Event__c + Restrict + false + + Event__c + Products + true + false + Lookup + diff --git a/examples/changelog/previous/objects/Product__c/fields/Features__c.field-meta.xml b/examples/changelog/previous/objects/Product__c/fields/Features__c.field-meta.xml new file mode 100644 index 00000000..6b67a859 --- /dev/null +++ b/examples/changelog/previous/objects/Product__c/fields/Features__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Features__c + false + + 32768 + false + LongTextArea + 10 + diff --git a/examples/vitepress/docs/changelog.md b/examples/vitepress/docs/changelog.md index 59bf6f89..fedd3cc0 100644 --- a/examples/vitepress/docs/changelog.md +++ b/examples/vitepress/docs/changelog.md @@ -40,4 +40,28 @@ These enums are new. This is a sample enum. This references ReferencedEnum . -This description has several lines \ No newline at end of file +This description has several lines + +## New Custom Objects + +These custom objects are new. + +### Event__c + +Represents an event that people can register for. +### Price_Component__c + +### Product_Price_Component__c + +### Product__c + +Product that is sold or available for sale. +### Sales_Order_Line__c + +Represents a line item on a sales order. +### Sales_Order__c + +Custom object for tracking sales orders. +### Speaker__c + +Represents a speaker at an event. \ No newline at end of file diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index fff05a4a..b7539385 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -13,7 +13,7 @@ const config = { skipIfNoChanges: false, }; -export function customObjectGenerator( +function customObjectGenerator( config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, ) { return ` @@ -27,6 +27,32 @@ export function customObjectGenerator( `; } +// export const customField = ` +// +// +// PhotoUrl__c +// false +// +// false +// false +// Url +// A URL that points to a photo +// `; +// +// function unparsedFieldBundleFromRawString(meta: { +// rawContent: string; +// filePath: string; +// parentName: string; +// }): UnparsedCustomFieldBundle { +// return { +// type: 'customfield', +// name: 'TestField__c', +// filePath: meta.filePath, +// content: meta.rawContent, +// parentName: meta.parentName, +// }; +// } + describe('when generating a changelog', () => { it('should not skip when skipIfNoChanges, even if there are no changes', async () => { const result = await generateChangeLog([], [], { ...config })(); @@ -255,6 +281,36 @@ describe('when generating a changelog', () => { }); }); + describe('that includes removed custom objects', () => { + it('should include a section for removed custom objects', async () => { + const oldObjectSource = customObjectGenerator(); + + const oldBundle: UnparsedCustomObjectBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, + ]; + const newBundle: UnparsedCustomObjectBundle[] = []; + + const result = await generateChangeLog(oldBundle, newBundle, config)(); + + assertEither(result, (data) => + expect((data as ChangeLogPageData).content).toContain('## Removed Custom Objects'), + ); + }); + + it('should include the removed custom object name', async () => { + const oldObjectSource = customObjectGenerator(); + + const oldBundle: UnparsedCustomObjectBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, + ]; + const newBundle: UnparsedCustomObjectBundle[] = []; + + const result = await generateChangeLog(oldBundle, newBundle, config)(); + + assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('- MyTestObject')); + }); + }); + describe('that includes modifications to existing members', () => { it('should include a section for new or modified members', async () => { const oldClassSource = 'class Test {}'; @@ -309,4 +365,33 @@ describe('when generating a changelog', () => { assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('myMethod')); }); }); + + // describe('that includes modifications to custom fields', () => { + // it('should include a section for new or modified custom fields', async () => { + // const oldObjectSource = customObjectGenerator(); + // const newObjectSource = customObjectGenerator(); + // + // const oldBundle: UnparsedSourceBundle[] = [ + // { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, + // ]; + // const newBundle: UnparsedSourceBundle[] = [ + // { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, + // unparsedFieldBundleFromRawString({ + // rawContent: customField, + // filePath: 'MyTestObject__c.field-meta.xml', + // parentName: 'MyTestObject', + // }), + // ]; + // + // const result = await generateChangeLog(oldBundle, newBundle, config)(); + // + // assertEither(result, (data) => + // expect((data as ChangeLogPageData).content).toContain('## New or Modified Fields in Existing Objects'), + // ); + // }); + // + // // TODO: should list new custom fields + // // TODO: Should list removed custom fields + // // TODO: Should mention custom field label modification + // }); }); diff --git a/src/core/changelog/renderable-changelog.ts b/src/core/changelog/renderable-changelog.ts index 42c7f41b..9894ca6c 100644 --- a/src/core/changelog/renderable-changelog.ts +++ b/src/core/changelog/renderable-changelog.ts @@ -40,8 +40,11 @@ export type RenderableChangelog = { removedTypes: RemovedTypeSection | null; newOrModifiedMembers: NewOrModifiedMembersSection | null; newCustomObjects: NewTypeSection<'customobject'> | null; - // todo: removed custom objects + removedCustomObjects: RemovedTypeSection | null; // todo: changed custom objects + // TODO: new fields + // TODO: removed fields + // TODO: changed fields }; export function convertToRenderableChangelog( @@ -111,6 +114,14 @@ export function convertToRenderableChangelog( })), } : null, + removedCustomObjects: + changelog.removedCustomObjects.length > 0 + ? { + heading: 'Removed Custom Objects', + description: 'These custom objects have been removed.', + types: changelog.removedCustomObjects, + } + : null, }; } diff --git a/src/core/changelog/templates/changelog-template.ts b/src/core/changelog/templates/changelog-template.ts index bf251722..af7a335b 100644 --- a/src/core/changelog/templates/changelog-template.ts +++ b/src/core/changelog/templates/changelog-template.ts @@ -50,7 +50,7 @@ export const changelogTemplate = ` {{/if}} {{#if removedTypes}} -## Removed Types +## {{removedTypes.heading}} {{removedTypes.description}} @@ -59,6 +59,16 @@ export const changelogTemplate = ` {{/each}} {{/if}} +{{#if removedCustomObjects}} +## {{removedCustomObjects.heading}} + +{{removedCustomObjects.description}} + +{{#each removedCustomObjects.types}} +- {{this}} +{{/each}} +{{/if}} + {{#if newOrModifiedMembers}} ## {{newOrModifiedMembers.heading}} From 2723bd8769e5ecb4168c24a5496946363247ecbd Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 29 Oct 2024 08:48:10 -0400 Subject: [PATCH 13/16] Includes new and removed custom fields. --- .../__test__/generating-change-log.spec.ts | 153 +++++++++++------- .../__test__/processing-changelog.spec.ts | 24 --- src/core/changelog/process-changelog.ts | 17 +- src/core/changelog/renderable-changelog.ts | 15 +- .../changelog/templates/changelog-template.ts | 14 ++ 5 files changed, 124 insertions(+), 99 deletions(-) diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index b7539385..7ed51af0 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -1,4 +1,9 @@ -import { UnparsedApexBundle, UnparsedCustomObjectBundle } from '../../shared/types'; +import { + UnparsedApexBundle, + UnparsedCustomFieldBundle, + UnparsedCustomObjectBundle, + UnparsedSourceBundle, +} from '../../shared/types'; import { ChangeLogPageData, generateChangeLog } from '../generate-change-log'; import { assertEither } from '../../test-helpers/assert-either'; import { isSkip } from '../../shared/utils'; @@ -27,31 +32,31 @@ function customObjectGenerator( `; } -// export const customField = ` -// -// -// PhotoUrl__c -// false -// -// false -// false -// Url -// A URL that points to a photo -// `; -// -// function unparsedFieldBundleFromRawString(meta: { -// rawContent: string; -// filePath: string; -// parentName: string; -// }): UnparsedCustomFieldBundle { -// return { -// type: 'customfield', -// name: 'TestField__c', -// filePath: meta.filePath, -// content: meta.rawContent, -// parentName: meta.parentName, -// }; -// } +export const customField = ` + + + PhotoUrl__c + false + + false + false + Url + A URL that points to a photo +`; + +function unparsedFieldBundleFromRawString(meta: { + rawContent: string; + filePath: string; + parentName: string; +}): UnparsedCustomFieldBundle { + return { + type: 'customfield', + name: 'TestField__c', + filePath: meta.filePath, + content: meta.rawContent, + parentName: meta.parentName, + }; +} describe('when generating a changelog', () => { it('should not skip when skipIfNoChanges, even if there are no changes', async () => { @@ -366,32 +371,72 @@ describe('when generating a changelog', () => { }); }); - // describe('that includes modifications to custom fields', () => { - // it('should include a section for new or modified custom fields', async () => { - // const oldObjectSource = customObjectGenerator(); - // const newObjectSource = customObjectGenerator(); - // - // const oldBundle: UnparsedSourceBundle[] = [ - // { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, - // ]; - // const newBundle: UnparsedSourceBundle[] = [ - // { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, - // unparsedFieldBundleFromRawString({ - // rawContent: customField, - // filePath: 'MyTestObject__c.field-meta.xml', - // parentName: 'MyTestObject', - // }), - // ]; - // - // const result = await generateChangeLog(oldBundle, newBundle, config)(); - // - // assertEither(result, (data) => - // expect((data as ChangeLogPageData).content).toContain('## New or Modified Fields in Existing Objects'), - // ); - // }); - // - // // TODO: should list new custom fields - // // TODO: Should list removed custom fields - // // TODO: Should mention custom field label modification - // }); + describe('that includes modifications to custom fields', () => { + it('should include a section for new or removed custom fields', async () => { + const oldObjectSource = customObjectGenerator(); + const newObjectSource = customObjectGenerator(); + + const oldBundle: UnparsedSourceBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, + ]; + const newBundle: UnparsedSourceBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, + unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'MyTestObject__c.field-meta.xml', + parentName: 'MyTestObject', + }), + ]; + + const result = await generateChangeLog(oldBundle, newBundle, config)(); + + assertEither(result, (data) => + expect((data as ChangeLogPageData).content).toContain('## New or Removed Fields in Existing Objects'), + ); + }); + + it('should include new custom field names', async () => { + const oldObjectSource = customObjectGenerator(); + const newObjectSource = customObjectGenerator(); + + const oldBundle: UnparsedSourceBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, + ]; + const newBundle: UnparsedSourceBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, + unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'MyTestObject__c.field-meta.xml', + parentName: 'MyTestObject', + }), + ]; + + const result = await generateChangeLog(oldBundle, newBundle, config)(); + + assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('New Field: TestField__c')); + }); + + it('should include removed custom field names', async () => { + const oldObjectSource = customObjectGenerator(); + const newObjectSource = customObjectGenerator(); + + const oldBundle: UnparsedSourceBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, + unparsedFieldBundleFromRawString({ + rawContent: customField, + filePath: 'MyTestObject__c.field-meta.xml', + parentName: 'MyTestObject', + }), + ]; + const newBundle: UnparsedSourceBundle[] = [ + { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, + ]; + + const result = await generateChangeLog(oldBundle, newBundle, config)(); + + assertEither(result, (data) => + expect((data as ChangeLogPageData).content).toContain('Removed Field: TestField__c'), + ); + }); + }); }); diff --git a/src/core/changelog/__test__/processing-changelog.spec.ts b/src/core/changelog/__test__/processing-changelog.spec.ts index 792108be..9bb8a964 100644 --- a/src/core/changelog/__test__/processing-changelog.spec.ts +++ b/src/core/changelog/__test__/processing-changelog.spec.ts @@ -160,30 +160,6 @@ describe('when generating a changelog', () => { expect(changeLog.removedCustomObjects).toEqual([oldObject.name]); }); - it('lists changed custom object labels', () => { - const oldObject = new CustomObjectMetadataBuilder().withLabel('OldLabel').build(); - const newObject = new CustomObjectMetadataBuilder().withLabel('NewLabel').build(); - const oldVersion = { types: [oldObject] }; - const newVersion = { types: [newObject] }; - - const changeLog = processChangelog(oldVersion, newVersion); - - // TODO: The changelog should display the old label and the new label - // TODO: Same deal with fields - - expect(changeLog.customObjectModifications).toEqual([ - { - typeName: oldObject.name, - modifications: [ - { - __typename: 'CustomObjectLabelChanged', - name: newObject.name, - }, - ], - }, - ]); - }); - it('lists all new fields of a custom object', () => { const oldObject = new CustomObjectMetadataBuilder().build(); const newField = new CustomFieldMetadataBuilder().build(); diff --git a/src/core/changelog/process-changelog.ts b/src/core/changelog/process-changelog.ts index f2bdaa69..b9d8695d 100644 --- a/src/core/changelog/process-changelog.ts +++ b/src/core/changelog/process-changelog.ts @@ -17,8 +17,7 @@ type ModificationTypes = | 'NewProperty' | 'RemovedProperty' | 'NewField' - | 'RemovedField' - | 'CustomObjectLabelChanged'; + | 'RemovedField'; export type MemberModificationType = { __typename: ModificationTypes; @@ -101,23 +100,11 @@ function getNewOrModifiedApexMembers(oldVersion: VersionManifest, newVersion: Ve function getCustomObjectModifications(oldVersion: VersionManifest, newVersion: VersionManifest): NewOrModifiedMember[] { return pipe( getCustomObjectsInBothVersions(oldVersion, newVersion), - (customObjectsInBoth) => [ - ...getModifiedCustomObjectLabels(customObjectsInBoth), - ...getNewOrRemovedCustomFields(customObjectsInBoth), - ], + (customObjectsInBoth) => getNewOrRemovedCustomFields(customObjectsInBoth), (customObjectModifications) => customObjectModifications.filter((member) => member.modifications.length > 0), ); } -function getModifiedCustomObjectLabels(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { - return typesInBoth - .filter(({ oldType, newType }) => oldType.label?.toLowerCase() !== newType.label?.toLowerCase()) - .map(({ newType }) => ({ - typeName: newType.name, - modifications: [{ __typename: 'CustomObjectLabelChanged', name: newType.name }], - })); -} - function getNewOrRemovedCustomFields(typesInBoth: TypeInBoth[]): NewOrModifiedMember[] { return typesInBoth.map(({ oldType, newType }) => { const oldCustomObject = oldType; diff --git a/src/core/changelog/renderable-changelog.ts b/src/core/changelog/renderable-changelog.ts index 9894ca6c..dead2477 100644 --- a/src/core/changelog/renderable-changelog.ts +++ b/src/core/changelog/renderable-changelog.ts @@ -41,10 +41,7 @@ export type RenderableChangelog = { newOrModifiedMembers: NewOrModifiedMembersSection | null; newCustomObjects: NewTypeSection<'customobject'> | null; removedCustomObjects: RemovedTypeSection | null; - // todo: changed custom objects - // TODO: new fields - // TODO: removed fields - // TODO: changed fields + newOrRemovedCustomFields: NewOrModifiedMembersSection | null; }; export function convertToRenderableChangelog( @@ -122,6 +119,14 @@ export function convertToRenderableChangelog( types: changelog.removedCustomObjects, } : null, + newOrRemovedCustomFields: + changelog.customObjectModifications.length > 0 + ? { + heading: 'New or Removed Fields in Existing Objects', + description: 'These custom fields have been added or removed.', + modifications: changelog.customObjectModifications.map(toRenderableModification), + } + : null, }; } @@ -166,7 +171,5 @@ function toRenderableModificationDescription(memberModificationType: MemberModif return `New Type: ${memberModificationType.name}`; case 'RemovedType': return `Removed Type: ${memberModificationType.name}`; - case 'CustomObjectLabelChanged': - return `Label Changed to ${memberModificationType.name}`; } } diff --git a/src/core/changelog/templates/changelog-template.ts b/src/core/changelog/templates/changelog-template.ts index af7a335b..3ab71e93 100644 --- a/src/core/changelog/templates/changelog-template.ts +++ b/src/core/changelog/templates/changelog-template.ts @@ -77,6 +77,20 @@ export const changelogTemplate = ` {{#each newOrModifiedMembers.modifications}} ### {{this.typeName}} +{{#each this.modifications}} +- {{this}} +{{/each}} +{{/each}} +{{/if}} + +{{#if newOrRemovedCustomFields}} +## {{newOrRemovedCustomFields.heading}} + +{{newOrRemovedCustomFields.description}} + +{{#each newOrRemovedCustomFields.modifications}} +### {{this.typeName}} + {{#each this.modifications}} - {{this}} {{/each}} From c42fae28d112d9401af1ff4120a4b47b8a9669ba Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 29 Oct 2024 09:00:42 -0400 Subject: [PATCH 14/16] UT refactoring --- examples/changelog/docs/changelog.md | 19 ++++++++++++++++++- .../__test__/generating-change-log.spec.ts | 15 +-------------- .../changelog/templates/changelog-template.ts | 1 + .../generating-custom-object-docs.spec.ts | 2 +- .../markdown/__test__/generating-docs.spec.ts | 8 ++------ .../generating-reference-guide.spec.ts | 8 ++------ src/core/markdown/__test__/test-helpers.ts | 14 -------------- src/core/test-helpers/test-data-builders.ts | 13 +++++++++++++ 8 files changed, 38 insertions(+), 42 deletions(-) create mode 100644 src/core/test-helpers/test-data-builders.ts diff --git a/examples/changelog/docs/changelog.md b/examples/changelog/docs/changelog.md index 82370e53..b567be19 100644 --- a/examples/changelog/docs/changelog.md +++ b/examples/changelog/docs/changelog.md @@ -49,4 +49,21 @@ These members have been added or modified. ### SolidService - New Method: newMethod -- Removed Method: deprecatedMethod \ No newline at end of file +- Removed Method: deprecatedMethod + +## New or Removed Fields in Existing Objects + +These custom fields have been added or removed. + +### Event__c + +- New Field: Description__c +- New Field: Tag_Line__c + +### Price_Component__c + +- New Field: Description__c + +### Product__c + +- New Field: Description__c \ No newline at end of file diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index 7ed51af0..34017344 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -7,6 +7,7 @@ import { import { ChangeLogPageData, generateChangeLog } from '../generate-change-log'; import { assertEither } from '../../test-helpers/assert-either'; import { isSkip } from '../../shared/utils'; +import { customObjectGenerator } from '../../test-helpers/test-data-builders'; const config = { fileName: 'changelog', @@ -18,20 +19,6 @@ const config = { skipIfNoChanges: false, }; -function customObjectGenerator( - config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, -) { - return ` - - - ${config.deploymentStatus} - test object for testing - - MyFirstObjects - ${config.visibility} - `; -} - export const customField = ` diff --git a/src/core/changelog/templates/changelog-template.ts b/src/core/changelog/templates/changelog-template.ts index 3ab71e93..eb6f431b 100644 --- a/src/core/changelog/templates/changelog-template.ts +++ b/src/core/changelog/templates/changelog-template.ts @@ -94,6 +94,7 @@ export const changelogTemplate = ` {{#each this.modifications}} - {{this}} {{/each}} + {{/each}} {{/if}} `.trim(); diff --git a/src/core/markdown/__test__/generating-custom-object-docs.spec.ts b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts index f08d0499..d0928dd6 100644 --- a/src/core/markdown/__test__/generating-custom-object-docs.spec.ts +++ b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts @@ -1,12 +1,12 @@ import { extendExpect } from './expect-extensions'; import { customField, - customObjectGenerator, generateDocs, unparsedFieldBundleFromRawString, unparsedObjectBundleFromRawString, } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; +import { customObjectGenerator } from '../../test-helpers/test-data-builders'; describe('Generates Custom Object documentation', () => { beforeAll(() => { diff --git a/src/core/markdown/__test__/generating-docs.spec.ts b/src/core/markdown/__test__/generating-docs.spec.ts index e8f6fb32..fd22690a 100644 --- a/src/core/markdown/__test__/generating-docs.spec.ts +++ b/src/core/markdown/__test__/generating-docs.spec.ts @@ -1,12 +1,8 @@ import { DocPageData, PostHookDocumentationBundle } from '../../shared/types'; import { extendExpect } from './expect-extensions'; -import { - unparsedApexBundleFromRawString, - generateDocs, - unparsedObjectBundleFromRawString, - customObjectGenerator, -} from './test-helpers'; +import { unparsedApexBundleFromRawString, generateDocs, unparsedObjectBundleFromRawString } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; +import { customObjectGenerator } from '../../test-helpers/test-data-builders'; function aSingleDoc(result: PostHookDocumentationBundle): DocPageData { expect(result.docs).toHaveLength(1); diff --git a/src/core/markdown/__test__/generating-reference-guide.spec.ts b/src/core/markdown/__test__/generating-reference-guide.spec.ts index 510e9458..6650c71d 100644 --- a/src/core/markdown/__test__/generating-reference-guide.spec.ts +++ b/src/core/markdown/__test__/generating-reference-guide.spec.ts @@ -1,14 +1,10 @@ import { extendExpect } from './expect-extensions'; import { pipe } from 'fp-ts/function'; import * as E from 'fp-ts/Either'; -import { - unparsedApexBundleFromRawString, - generateDocs, - customObjectGenerator, - unparsedObjectBundleFromRawString, -} from './test-helpers'; +import { unparsedApexBundleFromRawString, generateDocs, unparsedObjectBundleFromRawString } from './test-helpers'; import { ReferenceGuidePageData } from '../../shared/types'; import { assertEither } from '../../test-helpers/assert-either'; +import { customObjectGenerator } from '../../test-helpers/test-data-builders'; describe('When generating the Reference Guide', () => { beforeAll(() => { diff --git a/src/core/markdown/__test__/test-helpers.ts b/src/core/markdown/__test__/test-helpers.ts index d884958f..829ca897 100644 --- a/src/core/markdown/__test__/test-helpers.ts +++ b/src/core/markdown/__test__/test-helpers.ts @@ -59,20 +59,6 @@ export function generateDocs(apexBundles: UnparsedSourceBundle[], config?: Parti }); } -export function customObjectGenerator( - config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, -) { - return ` - - - ${config.deploymentStatus} - test object for testing - - MyFirstObjects - ${config.visibility} - `; -} - export const customField = ` diff --git a/src/core/test-helpers/test-data-builders.ts b/src/core/test-helpers/test-data-builders.ts new file mode 100644 index 00000000..2a81662d --- /dev/null +++ b/src/core/test-helpers/test-data-builders.ts @@ -0,0 +1,13 @@ +export function customObjectGenerator( + config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, +) { + return ` + + + ${config.deploymentStatus} + test object for testing + + MyFirstObjects + ${config.visibility} + `; +} From 43cace668a56d5dbb1a8ebc0eb66685949609426 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 29 Oct 2024 09:04:50 -0400 Subject: [PATCH 15/16] UT refactoring --- .../__test__/generating-change-log.spec.ts | 38 +------------------ .../generating-custom-object-docs.spec.ts | 13 +------ src/core/markdown/__test__/test-helpers.ts | 33 +--------------- src/core/test-helpers/test-data-builders.ts | 28 ++++++++++++++ 4 files changed, 33 insertions(+), 79 deletions(-) diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index 34017344..fb97c418 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -1,13 +1,8 @@ -import { - UnparsedApexBundle, - UnparsedCustomFieldBundle, - UnparsedCustomObjectBundle, - UnparsedSourceBundle, -} from '../../shared/types'; +import { UnparsedApexBundle, UnparsedCustomObjectBundle, UnparsedSourceBundle } from '../../shared/types'; import { ChangeLogPageData, generateChangeLog } from '../generate-change-log'; import { assertEither } from '../../test-helpers/assert-either'; import { isSkip } from '../../shared/utils'; -import { customObjectGenerator } from '../../test-helpers/test-data-builders'; +import { customObjectGenerator, unparsedFieldBundleFromRawString } from '../../test-helpers/test-data-builders'; const config = { fileName: 'changelog', @@ -19,32 +14,6 @@ const config = { skipIfNoChanges: false, }; -export const customField = ` - - - PhotoUrl__c - false - - false - false - Url - A URL that points to a photo -`; - -function unparsedFieldBundleFromRawString(meta: { - rawContent: string; - filePath: string; - parentName: string; -}): UnparsedCustomFieldBundle { - return { - type: 'customfield', - name: 'TestField__c', - filePath: meta.filePath, - content: meta.rawContent, - parentName: meta.parentName, - }; -} - describe('when generating a changelog', () => { it('should not skip when skipIfNoChanges, even if there are no changes', async () => { const result = await generateChangeLog([], [], { ...config })(); @@ -369,7 +338,6 @@ describe('when generating a changelog', () => { const newBundle: UnparsedSourceBundle[] = [ { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'MyTestObject__c.field-meta.xml', parentName: 'MyTestObject', }), @@ -392,7 +360,6 @@ describe('when generating a changelog', () => { const newBundle: UnparsedSourceBundle[] = [ { type: 'customobject', name: 'MyTestObject', content: newObjectSource, filePath: 'MyTestObject.object' }, unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'MyTestObject__c.field-meta.xml', parentName: 'MyTestObject', }), @@ -410,7 +377,6 @@ describe('when generating a changelog', () => { const oldBundle: UnparsedSourceBundle[] = [ { type: 'customobject', name: 'MyTestObject', content: oldObjectSource, filePath: 'MyTestObject.object' }, unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'MyTestObject__c.field-meta.xml', parentName: 'MyTestObject', }), diff --git a/src/core/markdown/__test__/generating-custom-object-docs.spec.ts b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts index d0928dd6..faf3fd59 100644 --- a/src/core/markdown/__test__/generating-custom-object-docs.spec.ts +++ b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts @@ -1,12 +1,7 @@ import { extendExpect } from './expect-extensions'; -import { - customField, - generateDocs, - unparsedFieldBundleFromRawString, - unparsedObjectBundleFromRawString, -} from './test-helpers'; +import { generateDocs, unparsedObjectBundleFromRawString } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; -import { customObjectGenerator } from '../../test-helpers/test-data-builders'; +import { customObjectGenerator, unparsedFieldBundleFromRawString } from '../../test-helpers/test-data-builders'; describe('Generates Custom Object documentation', () => { beforeAll(() => { @@ -54,7 +49,6 @@ describe('Generates Custom Object documentation', () => { }); const customFieldBundle = unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'src/object/TestField__c.field-meta.xml', parentName: 'TestObject__c', }); @@ -82,7 +76,6 @@ describe('Generates Custom Object documentation', () => { }); const customFieldBundle = unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'src/object/TestField__c.field-meta.xml', parentName: 'TestObject__c', }); @@ -99,7 +92,6 @@ describe('Generates Custom Object documentation', () => { }); const customFieldBundle = unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'src/object/TestField__c.field-meta.xml', parentName: 'TestObject__c', }); @@ -116,7 +108,6 @@ describe('Generates Custom Object documentation', () => { }); const customFieldBundle = unparsedFieldBundleFromRawString({ - rawContent: customField, filePath: 'src/object/TestField__c.field-meta.xml', parentName: 'TestObject__c', }); diff --git a/src/core/markdown/__test__/test-helpers.ts b/src/core/markdown/__test__/test-helpers.ts index 829ca897..eb7e8fb3 100644 --- a/src/core/markdown/__test__/test-helpers.ts +++ b/src/core/markdown/__test__/test-helpers.ts @@ -1,9 +1,4 @@ -import { - UnparsedApexBundle, - UnparsedCustomFieldBundle, - UnparsedCustomObjectBundle, - UnparsedSourceBundle, -} from '../../shared/types'; +import { UnparsedApexBundle, UnparsedCustomObjectBundle, UnparsedSourceBundle } from '../../shared/types'; import { generateDocs as gen, MarkdownGeneratorConfig } from '../generate-docs'; import { referenceGuideTemplate } from '../templates/reference-guide'; @@ -29,20 +24,6 @@ export function unparsedObjectBundleFromRawString(meta: { }; } -export function unparsedFieldBundleFromRawString(meta: { - rawContent: string; - filePath: string; - parentName: string; -}): UnparsedCustomFieldBundle { - return { - type: 'customfield', - name: 'TestField__c', - filePath: meta.filePath, - content: meta.rawContent, - parentName: meta.parentName, - }; -} - export function generateDocs(apexBundles: UnparsedSourceBundle[], config?: Partial) { return gen(apexBundles, { targetDir: 'target', @@ -58,15 +39,3 @@ export function generateDocs(apexBundles: UnparsedSourceBundle[], config?: Parti ...config, }); } - -export const customField = ` - - - PhotoUrl__c - false - - false - false - Url - A URL that points to a photo -`; diff --git a/src/core/test-helpers/test-data-builders.ts b/src/core/test-helpers/test-data-builders.ts index 2a81662d..be47b271 100644 --- a/src/core/test-helpers/test-data-builders.ts +++ b/src/core/test-helpers/test-data-builders.ts @@ -1,3 +1,5 @@ +import { UnparsedCustomFieldBundle } from '../shared/types'; + export function customObjectGenerator( config: { deploymentStatus: string; visibility: string } = { deploymentStatus: 'Deployed', visibility: 'Public' }, ) { @@ -11,3 +13,29 @@ export function customObjectGenerator( ${config.visibility} `; } + +export const customField = ` + + + PhotoUrl__c + false + + false + false + Url + A URL that points to a photo +`; + +export function unparsedFieldBundleFromRawString(meta: { + rawContent?: string; + filePath: string; + parentName: string; +}): UnparsedCustomFieldBundle { + return { + type: 'customfield', + name: 'TestField__c', + filePath: meta.filePath, + content: meta.rawContent ?? customField, + parentName: meta.parentName, + }; +} From c91e75f4c47432b85d7e5f8bee37bf705e3ac71c Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 29 Oct 2024 09:08:59 -0400 Subject: [PATCH 16/16] Resolving TODOs --- src/core/changelog/generate-change-log.ts | 18 +----------------- src/core/markdown/generate-docs.ts | 16 +--------------- src/util/source-bundle-utils.ts | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 32 deletions(-) create mode 100644 src/util/source-bundle-utils.ts diff --git a/src/core/changelog/generate-change-log.ts b/src/core/changelog/generate-change-log.ts index d5e2ad63..4071b6df 100644 --- a/src/core/changelog/generate-change-log.ts +++ b/src/core/changelog/generate-change-log.ts @@ -2,8 +2,6 @@ import { ParsedFile, Skip, UnparsedApexBundle, - UnparsedCustomFieldBundle, - UnparsedCustomObjectBundle, UnparsedSourceBundle, UserDefinedChangelogConfig, } from '../shared/types'; @@ -21,6 +19,7 @@ import { skip } from '../shared/utils'; import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; +import { filterApexSourceFiles, filterCustomObjectsAndFields } from '#utils/source-bundle-utils'; export type ChangeLogPageData = { content: string; @@ -55,27 +54,12 @@ export function generateChangeLog( } function reflect(bundles: UnparsedSourceBundle[], config: Omit) { - // TODO: Move to utility and reuse - function filterApexSourceFiles(sourceFiles: UnparsedSourceBundle[]): UnparsedApexBundle[] { - return sourceFiles.filter((sourceFile): sourceFile is UnparsedApexBundle => sourceFile.type === 'apex'); - } - const filterOutOfScopeApex = apply(filterScope, config.scope); function reflectApexFiles(sourceFiles: UnparsedApexBundle[]) { return pipe(reflectApexSource(sourceFiles), TE.map(filterOutOfScopeApex)); } - // TODO: Move to utility and reuse - function filterCustomObjectsAndFields( - sourceFiles: UnparsedSourceBundle[], - ): (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[] { - return sourceFiles.filter( - (sourceFile): sourceFile is UnparsedCustomObjectBundle => - sourceFile.type === 'customobject' || sourceFile.type === 'customfield', - ); - } - return pipe( reflectApexFiles(filterApexSourceFiles(bundles)), TE.chain((parsedApexFiles) => { diff --git a/src/core/markdown/generate-docs.ts b/src/core/markdown/generate-docs.ts index 28443d2d..a0464ac3 100644 --- a/src/core/markdown/generate-docs.ts +++ b/src/core/markdown/generate-docs.ts @@ -17,9 +17,7 @@ import { DocPageReference, TransformReference, ParsedFile, - UnparsedCustomObjectBundle, UnparsedSourceBundle, - UnparsedCustomFieldBundle, } from '../shared/types'; import { parsedFilesToRenderableBundle } from './adapters/renderable-bundle'; import { reflectApexSource } from '../reflection/apex/reflect-apex-source'; @@ -37,6 +35,7 @@ import { HookError } from '../errors/errors'; import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; +import { filterApexSourceFiles, filterCustomObjectsAndFields } from '#utils/source-bundle-utils'; export type MarkdownGeneratorConfig = Omit< UserDefinedMarkdownConfig, @@ -55,19 +54,6 @@ export function generateDocs(unparsedBundles: UnparsedSourceBundle[], config: Ma ); const sort = apply(sortTypesAndMembers, config.sortAlphabetically); - function filterApexSourceFiles(sourceFiles: UnparsedSourceBundle[]): UnparsedApexBundle[] { - return sourceFiles.filter((sourceFile): sourceFile is UnparsedApexBundle => sourceFile.type === 'apex'); - } - - function filterCustomObjectsAndFields( - sourceFiles: UnparsedSourceBundle[], - ): (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[] { - return sourceFiles.filter( - (sourceFile): sourceFile is UnparsedCustomObjectBundle => - sourceFile.type === 'customobject' || sourceFile.type === 'customfield', - ); - } - function filterOutCustomFields(parsedFiles: ParsedFile[]): ParsedFile[] { return parsedFiles.filter( (parsedFile): parsedFile is ParsedFile => parsedFile.source.type !== 'customfield', diff --git a/src/util/source-bundle-utils.ts b/src/util/source-bundle-utils.ts new file mode 100644 index 00000000..39fcb650 --- /dev/null +++ b/src/util/source-bundle-utils.ts @@ -0,0 +1,19 @@ +import { + UnparsedApexBundle, + UnparsedCustomFieldBundle, + UnparsedCustomObjectBundle, + UnparsedSourceBundle, +} from '../core/shared/types'; + +export function filterApexSourceFiles(sourceFiles: UnparsedSourceBundle[]): UnparsedApexBundle[] { + return sourceFiles.filter((sourceFile): sourceFile is UnparsedApexBundle => sourceFile.type === 'apex'); +} + +export function filterCustomObjectsAndFields( + sourceFiles: UnparsedSourceBundle[], +): (UnparsedCustomObjectBundle | UnparsedCustomFieldBundle)[] { + return sourceFiles.filter( + (sourceFile): sourceFile is UnparsedCustomObjectBundle => + sourceFile.type === 'customobject' || sourceFile.type === 'customfield', + ); +}