packages/@aws-cdk/cloudformation-diff/lib/diff/index.ts (72 lines of code) (raw):
import type { Resource } from '@aws-cdk/service-spec-types';
import * as types from './types';
import { deepEqual, diffKeyedEntities, loadResourceModel } from './util';
export function diffAttribute(oldValue: any, newValue: any): types.Difference<string> {
return new types.Difference<string>(_asString(oldValue), _asString(newValue));
}
export function diffCondition(oldValue: types.Condition, newValue: types.Condition): types.ConditionDifference {
return new types.ConditionDifference(oldValue, newValue);
}
export function diffMapping(oldValue: types.Mapping, newValue: types.Mapping): types.MappingDifference {
return new types.MappingDifference(oldValue, newValue);
}
export function diffMetadata(oldValue: types.Metadata, newValue: types.Metadata): types.MetadataDifference {
return new types.MetadataDifference(oldValue, newValue);
}
export function diffOutput(oldValue: types.Output, newValue: types.Output): types.OutputDifference {
return new types.OutputDifference(oldValue, newValue);
}
export function diffParameter(oldValue: types.Parameter, newValue: types.Parameter): types.ParameterDifference {
return new types.ParameterDifference(oldValue, newValue);
}
export function diffResource(oldValue?: types.Resource, newValue?: types.Resource): types.ResourceDifference {
const resourceType = {
oldType: oldValue && oldValue.Type,
newType: newValue && newValue.Type,
};
let propertyDiffs: { [key: string]: types.PropertyDifference<any> } = {};
let otherDiffs: { [key: string]: types.Difference<any> } = {};
if (resourceType.oldType !== undefined && resourceType.oldType === resourceType.newType) {
// Only makes sense to inspect deeper if the types stayed the same
const impl = loadResourceModel(resourceType.oldType);
propertyDiffs = diffKeyedEntities(oldValue!.Properties,
newValue!.Properties,
(oldVal, newVal, key) => _diffProperty(oldVal, newVal, key, impl));
otherDiffs = diffKeyedEntities(oldValue, newValue, _diffOther);
delete otherDiffs.Properties;
}
return new types.ResourceDifference(oldValue, newValue, {
resourceType, propertyDiffs, otherDiffs,
});
function _diffProperty(oldV: any, newV: any, key: string, resourceSpec?: Resource) {
let changeImpact = types.ResourceImpact.NO_CHANGE;
const spec = resourceSpec?.properties?.[key];
if (spec && !deepEqual(oldV, newV)) {
switch (spec.causesReplacement) {
case 'yes':
changeImpact = types.ResourceImpact.WILL_REPLACE;
break;
case 'maybe':
changeImpact = types.ResourceImpact.MAY_REPLACE;
break;
default:
// In those cases, whatever is the current value is what we should keep
changeImpact = types.ResourceImpact.WILL_UPDATE;
}
}
return new types.PropertyDifference(oldV, newV, { changeImpact });
}
function _diffOther(oldV: any, newV: any) {
return new types.Difference(oldV, newV);
}
}
export function diffUnknown(oldValue: any, newValue: any): types.Difference<any> {
return new types.Difference(oldValue, newValue);
}
/**
* Coerces a given value to +string | undefined+.
*
* @param value the value to be coerced.
*
* @returns +undefined+ if +value+ is +null+ or +undefined+,
* +value+ if it is a +string+,
* a compact JSON representation of +value+ otherwise.
*/
function _asString(value: any): string | undefined {
if (value == null) {
return undefined;
}
if (typeof value === 'string') {
return value as string;
}
return JSON.stringify(value);
}