From 207a7735e1519efe630235bba69348e7cab31845 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 22 Oct 2024 07:38:12 -0400 Subject: [PATCH 01/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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', + ); +} From a28249815d9ee696ae039aa3d175294577bce3da Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 29 Oct 2024 09:18:04 -0400 Subject: [PATCH 17/19] Doc updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41e9c7c6..1a394375 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ create a documentation site that fits your needs, hosted in any static web hosti ApexDocs generates Markdown files, which can be integrated into any Static Site Generation (SSG) engine, (e.g. Jekyll, Vitepress, Hugo, Docosaurus, etc.) to create a documentation site that fits your needs. -This gives you the flexibility to create beautiful leveraging your preferred SSG engine, which +This gives you the flexibility to create beautiful sites by leveraging your preferred SSG engine, which usually provides a wide range of themes, dark mode support, and other features out of the box.
From 4300e53f3a32de5d7b01560b7116291c177c2b49 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 5 Nov 2024 07:51:09 -0400 Subject: [PATCH 18/19] Merging master --- jest.config.js | 2 +- .../generating-custom-object-docs.spec.ts | 2 +- src/core/markdown/__test__/test-helpers.ts | 33 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index 81c35d21..8a93fadd 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - modulePathIgnorePatterns: ['/dist/'], + modulePathIgnorePatterns: ['/dist/', '/examples/'], moduleNameMapper: { '^chalk$': '/__mocks__/chalk.js', '^log-update$': '/__mocks__/log-update.js', 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 aac7584a..b5768434 100644 --- a/src/core/markdown/__test__/generating-custom-object-docs.spec.ts +++ b/src/core/markdown/__test__/generating-custom-object-docs.spec.ts @@ -1,5 +1,5 @@ import { extendExpect } from './expect-extensions'; -import { generateDocs, unparsedObjectBundleFromRawString } from './test-helpers'; +import { customFieldPickListValues, generateDocs, unparsedObjectBundleFromRawString } from './test-helpers'; import { assertEither } from '../../test-helpers/assert-either'; import { customObjectGenerator, unparsedFieldBundleFromRawString } from '../../test-helpers/test-data-builders'; diff --git a/src/core/markdown/__test__/test-helpers.ts b/src/core/markdown/__test__/test-helpers.ts index eb7e8fb3..2f126de6 100644 --- a/src/core/markdown/__test__/test-helpers.ts +++ b/src/core/markdown/__test__/test-helpers.ts @@ -39,3 +39,36 @@ export function generateDocs(apexBundles: UnparsedSourceBundle[], config?: Parti ...config, }); } + +export const customFieldPickListValues = ` + + + Status__c + false + + true + false + Status + Picklist + + true + + false + + Staging + false + + + + Active + false + + + + Inactive + false + + + + +`; From c75f43915fe61e499f06d5c0d9c7aedc07338a35 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Tue, 5 Nov 2024 07:59:05 -0400 Subject: [PATCH 19/19] Releasing version 3.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c269126..8fa6595b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cparra/apexdocs", - "version": "3.4.2", + "version": "3.5.0", "description": "Library with CLI capabilities to generate documentation for Salesforce Apex classes.", "keywords": [ "apex",