Recursively merge properties.
When merging inputs and defaults in order to construct the set of inputs for a call to `Create`, we must recursively merge each property value: the provided defaults may contain nested values that must be present in the merged result.
This commit is contained in:
parent
89839038a0
commit
f5b35561c6
4
examples/dynamic-provider/recursive-merge/Pulumi.yaml
Normal file
4
examples/dynamic-provider/recursive-merge/Pulumi.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name: recursive-merge
|
||||||
|
description: A test for recursive merging of defaults.
|
||||||
|
runtime: nodejs
|
||||||
|
|
29
examples/dynamic-provider/recursive-merge/index.ts
Normal file
29
examples/dynamic-provider/recursive-merge/index.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||||
|
|
||||||
|
import * as pulumi from "pulumi";
|
||||||
|
import * as dynamic from "pulumi/dynamic";
|
||||||
|
|
||||||
|
class TestProvider implements dynamic.ResourceProvider {
|
||||||
|
diff = (id: pulumi.ID, olds: any, news: any) => Promise.resolve(new dynamic.DiffResult([], []));
|
||||||
|
delete = (id: pulumi.ID, props: any) => Promise.resolve();
|
||||||
|
update = (id: string, olds: any, news: any) => Promise.resolve(new dynamic.UpdateResult({}));
|
||||||
|
|
||||||
|
check = (inputs: any) => Promise.resolve(new dynamic.CheckResult({prop: {def: true}, arr: [{def: true}], arr1: [], arr2: [{def:true}]}, []));
|
||||||
|
|
||||||
|
create = async (inputs: any) => {
|
||||||
|
if (!inputs.prop.def || !inputs.arr[0].def || !inputs.arr1[0].def || !inputs.arr2[0].def) {
|
||||||
|
throw new Error("expected defaults to be recursively merged");
|
||||||
|
}
|
||||||
|
return new dynamic.CreateResult("0", {ok: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestResource extends dynamic.Resource {
|
||||||
|
public readonly ok: pulumi.Computed<Boolean>;
|
||||||
|
|
||||||
|
constructor(name: string) {
|
||||||
|
super(new TestProvider(), name, {prop: {unused: ""}, arr: [{unused: ""}], arr1: [{def: true}], arr2: []}, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ok = new TestResource("test").ok;
|
11
examples/dynamic-provider/recursive-merge/package.json
Normal file
11
examples/dynamic-provider/recursive-merge/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "minimal",
|
||||||
|
"main": "bin/index.js",
|
||||||
|
"typings": "bin/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^2.5.3"
|
||||||
|
}
|
||||||
|
}
|
22
examples/dynamic-provider/recursive-merge/tsconfig.json
Normal file
22
examples/dynamic-provider/recursive-merge/tsconfig.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "bin",
|
||||||
|
"target": "es6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"pretty": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
7
examples/dynamic-provider/recursive-merge/yarn.lock
Normal file
7
examples/dynamic-provider/recursive-merge/yarn.lock
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
typescript@^2.5.3:
|
||||||
|
version "2.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
|
|
@ -141,6 +141,9 @@ func (m PropertyMap) Merge(other PropertyMap) PropertyMap {
|
||||||
new := m.Copy()
|
new := m.Copy()
|
||||||
for k, v := range other {
|
for k, v := range other {
|
||||||
if !v.IsNull() {
|
if !v.IsNull() {
|
||||||
|
if mv, ok := m[k]; ok {
|
||||||
|
v = mv.Merge(v)
|
||||||
|
}
|
||||||
new[k] = v
|
new[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,6 +457,42 @@ func (v PropertyValue) MapRepl(replk func(string) (string, bool),
|
||||||
return v.ObjectValue().MapRepl(replk, replv)
|
return v.ObjectValue().MapRepl(replk, replv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge simply merges the value of other into v. Merging proceeds as follows:
|
||||||
|
// - If other is null, v is returned.
|
||||||
|
// - If v and other are both arrays, the corresponding elements are recurively merged. Any unmerged elements in v or other are then
|
||||||
|
// appended to the result.
|
||||||
|
// - If v and other are both maps, the corresponding key-value pairs are recursively merged.
|
||||||
|
// - Otherwise, other is returned.
|
||||||
|
func (v PropertyValue) Merge(other PropertyValue) PropertyValue {
|
||||||
|
switch {
|
||||||
|
case other.IsNull():
|
||||||
|
return v
|
||||||
|
case v.IsArray() && other.IsArray():
|
||||||
|
left, right, merged := v.ArrayValue(), other.ArrayValue(), []PropertyValue{}
|
||||||
|
for len(left) > 0 && len(right) > 0 {
|
||||||
|
merged = append(merged, left[0], right[0])
|
||||||
|
left, right = left[1:], right[1:]
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case len(left) > 0:
|
||||||
|
contract.Assert(len(right) == 0)
|
||||||
|
for ; len(left) > 0; left = left[1:] {
|
||||||
|
merged = append(merged, left[0])
|
||||||
|
}
|
||||||
|
case len(right) > 0:
|
||||||
|
contract.Assert(len(left) == 0)
|
||||||
|
for ; len(right) > 0; right = right[1:] {
|
||||||
|
merged = append(merged, right[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewArrayProperty(merged)
|
||||||
|
case v.IsObject() && other.IsObject():
|
||||||
|
return NewObjectProperty(v.ObjectValue().Merge(other.ObjectValue()))
|
||||||
|
default:
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// String implements the fmt.Stringer interface to add slightly more information to the output.
|
// String implements the fmt.Stringer interface to add slightly more information to the output.
|
||||||
func (v PropertyValue) String() string {
|
func (v PropertyValue) String() string {
|
||||||
if v.IsComputed() || v.IsOutput() {
|
if v.IsComputed() || v.IsOutput() {
|
||||||
|
|
Loading…
Reference in a new issue