diff --git a/pkg/tfgen/unrec/comparer.go b/pkg/tfgen/unrec/comparer.go new file mode 100644 index 000000000..a260ef0bd --- /dev/null +++ b/pkg/tfgen/unrec/comparer.go @@ -0,0 +1,319 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "bytes" + "reflect" + + pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" +) + +type comparer struct { + // Local type-ref comparisons are scoped to a package schema. + schema *pschema.PackageSpec + + // Set of implicit type rewrites to consider when comparing. + rewrites map[tokens.Type]tokens.Type +} + +func (cmp *comparer) WithRewrites(rewrites map[tokens.Type]tokens.Type) *comparer { + return &comparer{ + schema: cmp.schema, + rewrites: rewrites, + } +} + +func (cmp *comparer) EqualTypeRefs(a, b tokens.Type) bool { + g := &generalizedComparer{schema: cmp.schema, rewrites: cmp.rewrites} + g.EqualXPropertyMaps = g.strictlyEqualXPropertyMaps + return g.EqualTypeRefs(a, b) +} + +func (cmp *comparer) LessThanTypeRefs(a, b tokens.Type) bool { + return cmp.LessThanOrEqualTypeRefs(a, b) && !cmp.EqualTypeRefs(a, b) +} + +// A type will be considered "less than" another type if both are locally defined object types and A defines a subset of +// B's properties. This is useful to deal with property dropout during recursive type expansions. +func (cmp *comparer) LessThanOrEqualTypeRefs(a, b tokens.Type) (eq bool) { + g := &generalizedComparer{schema: cmp.schema, rewrites: cmp.rewrites} + g.EqualXPropertyMaps = g.lessThanOrEqualXPropertyMaps + return g.EqualTypeRefs(a, b) +} + +// Generalizing structural comparisons to specialize for A=B and A<=B separately. +type generalizedComparer struct { + schema *pschema.PackageSpec + rewrites map[tokens.Type]tokens.Type + EqualXPropertyMaps func(xPropertyMap, xPropertyMap) bool +} + +func (cmp *generalizedComparer) lessThanOrEqualXPropertyMaps(a, b xPropertyMap) bool { + // Empty objects are treated specially and are never {}<=X. + if len(a) == 0 || len(b) == 0 { + return len(a) == len(b) + } + for aK, aP := range a { + bP, ok := b[aK] + // Every key in A should also be a key in B. + if !ok { + return false + } + if !cmp.EqualXProperties(aP, bP) { + return false + } + } + return true +} + +func (cmp *generalizedComparer) strictlyEqualXPropertyMaps(a, b xPropertyMap) bool { + return len(a) == len(b) && cmp.lessThanOrEqualXPropertyMaps(a, b) +} + +func (cmp *generalizedComparer) rewrite(a tokens.Type) tokens.Type { + if cmp.rewrites == nil { + return a + } + if x, ok := cmp.rewrites[a]; ok { + return x + } + return a +} + +func (cmp *generalizedComparer) EqualTypeRefs(a, b tokens.Type) bool { + a, b = cmp.rewrite(a), cmp.rewrite(b) + if a == b { + return true + } + aT, gotA := cmp.schema.Types[string(a)] + bT, gotB := cmp.schema.Types[string(b)] + if gotA && gotB { + return cmp.EqualComplexTypeSpecs(&aT, &bT) + } + return false +} + +func (cmp *generalizedComparer) EqualRawRefs(a, b string) bool { + if a == b { + return true + } + aT, ok1 := parseLocalRef(a) + bT, ok2 := parseLocalRef(b) + if ok1 && ok2 { + return cmp.EqualTypeRefs(aT, bT) + } + return false +} + +func (cmp *generalizedComparer) EqualXProperties(a, b xProperty) bool { + if a.IsRequired != b.IsRequired { + return false + } + if a.IsPlain != b.IsPlain { + return false + } + if !cmp.EqualPropertySpecs(&a.PropertySpec, &b.PropertySpec) { + return false + } + return true +} + +func (cmp *generalizedComparer) EqualObjectTypeSpecs(a, b pschema.ObjectTypeSpec) bool { + if a.Type != b.Type { + return false + } + if a.IsOverlay != b.IsOverlay { + return false + } + if !cmp.EqualLanguageMaps(a.Language, b.Language) { + return false + } + if a.Description != b.Description { + return false + } + if !cmp.EqualXPropertyMaps(newXPropertyMap(a), newXPropertyMap(b)) { + return false + } + return true +} + +func (cmp *generalizedComparer) EqualComplexTypeSpecs(a, b *pschema.ComplexTypeSpec) bool { + // Do not identify enum equality yet. + if a.Enum != nil || b.Enum != nil { + return false + } + if !cmp.EqualObjectTypeSpecs(a.ObjectTypeSpec, b.ObjectTypeSpec) { + return false + } + return true +} + +func (cmp *generalizedComparer) EqualLanguageMaps(a, b map[string]pschema.RawMessage) bool { + if a == nil || b == nil { + return a == nil && b == nil + } + if len(a) != len(b) { + return false + } + for k, av := range a { + bv, ok := b[k] + if !ok { + return false + } + if !bytes.Equal(av, bv) { + return false + } + } + return true +} + +func (cmp *generalizedComparer) EqualTypeSpecLists(a, b []pschema.TypeSpec) bool { + if a == nil || b == nil { + return a == nil && b == nil + } + if len(a) != len(b) { + return false + } + for i := range a { + if !cmp.EqualTypeSpecs(&a[i], &b[i]) { + return false + } + } + return true +} + +func (cmp *generalizedComparer) EqualStringSlices(a, b []string) bool { + if a == nil || b == nil { + return a == nil && b == nil + } + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} + +func (cmp *generalizedComparer) EqualStringMaps(a, b map[string]string) bool { + if a == nil || b == nil { + return a == nil && b == nil + } + if len(a) != len(b) { + return false + } + for k, av := range a { + bv, ok := b[k] + if !ok { + return false + } + if av != bv { + return false + } + } + return true +} + +func (cmp *generalizedComparer) EqualDiscriminatorSpecs(a, b *pschema.DiscriminatorSpec) bool { + if a == nil || b == nil { + return a == b + } + if a.PropertyName != b.PropertyName { + return false + } + if !cmp.EqualStringMaps(a.Mapping, b.Mapping) { + return false + } + return true +} + +func (cmp *generalizedComparer) EqualTypeSpecs(a, b *pschema.TypeSpec) bool { + if a == nil || b == nil { + return a == b + } + if a.Type != b.Type { + return false + } + if !cmp.EqualRawRefs(a.Ref, b.Ref) { + return false + } + if !cmp.EqualTypeSpecs(a.AdditionalProperties, b.AdditionalProperties) { + return false + } + if !cmp.EqualTypeSpecs(a.Items, b.Items) { + return false + } + if !cmp.EqualTypeSpecLists(a.OneOf, b.OneOf) { + return false + } + if !cmp.EqualDiscriminatorSpecs(a.Discriminator, b.Discriminator) { + return false + } + if a.Plain != b.Plain { + return false + } + return true +} + +func (cmp *generalizedComparer) EqualPropertySpecs(a, b *pschema.PropertySpec) bool { + if !cmp.EqualTypeSpecs(&a.TypeSpec, &b.TypeSpec) { + return false + } + if a.Description != b.Description { + return false + } + if !reflect.DeepEqual(a.Const, b.Const) { + return false + } + if !reflect.DeepEqual(a.Default, b.Default) { + return false + } + if !cmp.EqualDefaultSpecs(a.DefaultInfo, b.DefaultInfo) { + return false + } + if a.DeprecationMessage != b.DeprecationMessage { + return false + } + if !cmp.EqualLanguageMaps(a.Language, b.Language) { + return false + } + if a.Secret != b.Secret { + return false + } + if a.ReplaceOnChanges != b.ReplaceOnChanges { + return false + } + if a.WillReplaceOnChanges != b.WillReplaceOnChanges { + return false + } + return true +} + +func (cmp *generalizedComparer) EqualDefaultSpecs(a, b *pschema.DefaultSpec) bool { + if a == nil || b == nil { + return a == b + } + if !cmp.EqualLanguageMaps(a.Language, b.Language) { + return false + } + if !cmp.EqualStringSlices(a.Environment, b.Environment) { + return false + } + return true +} diff --git a/pkg/tfgen/unrec/comparer_test.go b/pkg/tfgen/unrec/comparer_test.go new file mode 100644 index 000000000..e2e9503d4 --- /dev/null +++ b/pkg/tfgen/unrec/comparer_test.go @@ -0,0 +1,101 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "testing" + + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" + "github.com/stretchr/testify/require" +) + +func TesComparerAndStatement(t *testing.T) { + //nolint:lll + t1 := "myprov:index/WebAclStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatement" + t2 := "myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement" + s := exampleSchema(t) + cmp := &comparer{s, nil} + require.Truef(t, cmp.LessThanTypeRefs(tokens.Type(t1), tokens.Type(t2)), "A 1 { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "statement": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "and_statement": statementSchema(level - 1), + "xss_match_statement": xssMatchStatementSchema(), + "sqli_match_statement": sqliMatchStatementSchema(), + "regex_match_statement": regexMatchStatementSchema(), + }, + }, + }, + }, + }, + } + } + + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "statement": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "xss_match_statement": xssMatchStatementSchema(), + "sqli_match_statement": sqliMatchStatementSchema(), + "regex_match_statement": regexMatchStatementSchema(), + }, + }, + }, + }, + }, + } +} + +func xssMatchStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_to_match": fieldToMatchSchema(), + }, + }, + } +} + +func fieldToMatchSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: fieldToMatchBaseSchema(), + } +} + +func sqliMatchStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_to_match": fieldToMatchSchema(), + "text_transformation": textTransformationSchema(), + }, + }, + } +} + +const ( + namesAttrPriority = "priority" + namesAttrType = "type" +) + +func textTransformationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + namesAttrPriority: { + Type: schema.TypeInt, + Required: true, + }, + namesAttrType: { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +var listOfEmptyObjectSchema *schema.Schema = &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{}, + }, +} + +func emptySchema() *schema.Schema { + return listOfEmptyObjectSchema +} + +func fieldToMatchBaseSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "method": emptySchema(), + }, + } +} diff --git a/pkg/tfgen/unrec/package.go b/pkg/tfgen/unrec/package.go new file mode 100644 index 000000000..ad1756a9e --- /dev/null +++ b/pkg/tfgen/unrec/package.go @@ -0,0 +1,10 @@ +// Package unrec implements recursion detection over [Pulumi Package Schema]. +// +// The implementation is generic to all providers but the intent is tailored for bridged providers specifically. TF +// cannot represent recursive types but uses unrolling up to level N, for example for Statement in waf2 web_acl in AWS +// it uses unrolling to level 3. This creates too many types in the Pulumi projection. +// +// This package is aimed at detecting the unrolled recursion and tying it back into recursive types. +// +// [Pulumi Package Schema]: https://www.pulumi.com/docs/guides/pulumi-packages/schema/ +package unrec diff --git a/pkg/tfgen/unrec/package_spec_util.go b/pkg/tfgen/unrec/package_spec_util.go new file mode 100644 index 000000000..c57ce0d1a --- /dev/null +++ b/pkg/tfgen/unrec/package_spec_util.go @@ -0,0 +1,226 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "encoding/json" + "fmt" + "strings" + + pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" + "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" +) + +func parseLocalRef(rawRef string) (tokens.Type, bool) { + if !strings.HasPrefix(rawRef, "#/types/") { + return "", false + } + cleanRef := strings.TrimPrefix(rawRef, "#/types/") + return tokens.Type(cleanRef), true +} + +type xProperty struct { + pschema.PropertySpec + IsRequired bool + IsPlain bool +} + +type xPropertyMap map[string]xProperty + +func newXPropertyMap(s pschema.ObjectTypeSpec) xPropertyMap { + m := make(xPropertyMap) + for _, prop := range s.Plain { + p := m[prop] + p.IsPlain = true + m[prop] = p + } + for _, prop := range s.Required { + p := m[prop] + p.IsRequired = true + m[prop] = p + } + for prop, spec := range s.Properties { + p := m[prop] + p.PropertySpec = spec + m[prop] = p + } + return m +} + +type typeRefs map[tokens.Type]struct{} + +func newTypeRefs(tok ...tokens.Type) typeRefs { + tr := make(typeRefs) + tr.Add(tok...) + return tr +} + +func (refs typeRefs) Contains(t tokens.Type) bool { + _, ok := refs[t] + return ok +} + +func (refs typeRefs) Add(tok ...tokens.Type) { + for _, t := range tok { + refs[t] = struct{}{} + } +} + +func (refs typeRefs) Slice() []tokens.Type { + x := []tokens.Type{} + for k := range refs { + x = append(x, k) + } + return x +} + +func (refs typeRefs) Best() tokens.Type { + var best tokens.Type + for k := range refs { + if best == "" || len(k) < len(best) { + best = k + } + } + contract.Assertf(best != "", "expected to find at least one element") + return best +} + +func findGenericTypeReferences(x any) (typeRefs, error) { + bytes, err := json.Marshal(x) + if err != nil { + return nil, err + } + + var s any + if err := json.Unmarshal(bytes, &s); err != nil { + return nil, err + } + tr := make(typeRefs, 0) + transformMaps(func(m map[string]any) { + ref, ok := detectRef(m) + if ok { + tr[ref] = struct{}{} + } + }, s) + return tr, nil +} + +func detectRef(m map[string]any) (tokens.Type, bool) { + ref, gotRef := m["$ref"] + if !gotRef { + return "", false + } + refs, ok := ref.(string) + if !ok { + return "", false + } + return parseLocalRef(refs) +} + +func transformMaps(transform func(map[string]any), value any) any { + switch value := value.(type) { + case map[string]any: + transform(value) + for k := range value { + value[k] = transformMaps(transform, value[k]) + } + return value + case []any: + for i := range value { + value[i] = transformMaps(transform, value[i]) + } + return value + default: + return value + } +} + +func rewriteTypeRefs(rewrites map[tokens.Type]tokens.Type, schema *pschema.PackageSpec) error { + bytes, err := json.Marshal(schema) + if err != nil { + return err + } + + var s any + if err := json.Unmarshal(bytes, &s); err != nil { + return err + } + + rewrite := func(m map[string]any) { + cleanRef, ok := detectRef(m) + if !ok { + return + } + if modifiedRef, ok := rewrites[cleanRef]; ok { + m["$ref"] = fmt.Sprintf("#/types/%s", modifiedRef) + } + } + + modifiedS := transformMaps(rewrite, s) + + modifiedBytes, err := json.Marshal(modifiedS) + if err != nil { + return err + } + + var modifiedSchema pschema.PackageSpec + if err := json.Unmarshal(modifiedBytes, &modifiedSchema); err != nil { + return err + } + + for deletedType := range rewrites { + delete(modifiedSchema.Types, string(deletedType)) + } + + *schema = modifiedSchema + + return nil +} + +func findResourceTypeReferences(r pschema.ResourceSpec) (typeRefs, error) { + return findGenericTypeReferences(r) +} + +func findFunctionTypeReferences(f pschema.FunctionSpec) (typeRefs, error) { + return findGenericTypeReferences(f) +} + +func findTypeReferenceTransitiveClosure(spec *pschema.PackageSpec, refs typeRefs) (typeRefs, error) { + queue := []tokens.Type{} + for r := range refs { + queue = append(queue, r) + } + seen := typeRefs{} + for len(queue) > 0 { + r := queue[0] + queue = queue[1:] + if _, ok := seen[r]; ok { + continue + } + t, ok := spec.Types[string(r)] + if ok { + moreRefs, err := findGenericTypeReferences(t) + if err != nil { + return nil, err + } + for m := range moreRefs { + queue = append(queue, m) + } + } + seen[r] = struct{}{} + } + return seen, nil +} diff --git a/pkg/tfgen/unrec/package_spec_util_test.go b/pkg/tfgen/unrec/package_spec_util_test.go new file mode 100644 index 000000000..0d769eb15 --- /dev/null +++ b/pkg/tfgen/unrec/package_spec_util_test.go @@ -0,0 +1,48 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "testing" + + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" + "github.com/stretchr/testify/require" +) + +func TestParseLocalRef(t *testing.T) { + type testCase struct { + ref string + expect tokens.Type + } + testCases := []testCase{ + { + "#/types/aws:opsworks/RailsAppLayerCloudwatchConfiguration:RailsAppLayerCloudwatchConfiguration", + "aws:opsworks/RailsAppLayerCloudwatchConfiguration:RailsAppLayerCloudwatchConfiguration", + }, + { + "pulumi.json#/Asset", + "", + }, + } + for _, tc := range testCases { + tok, ok := parseLocalRef(tc.ref) + if tc.expect != "" { + require.True(t, ok) + require.Equal(t, tc.expect, tok) + } else { + require.False(t, ok) + } + } +} diff --git a/pkg/tfgen/unrec/recursion_detector.go b/pkg/tfgen/unrec/recursion_detector.go new file mode 100644 index 000000000..796379329 --- /dev/null +++ b/pkg/tfgen/unrec/recursion_detector.go @@ -0,0 +1,161 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "slices" + "sort" + + pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" +) + +// Detects unrolled recursion. +// +// TF does not support recursive type definitions but Pulumi does. The idea here is to take a projection of TF recursion +// unrolling trick and fold it back. +// +// Suppose ancestor(T1, T2) if T2 describes a sub-property of T1. +// +// Suppose T1 <= T2 if T1 has the subset of properties of T2 with types that are themselves Tx <= Ty. +// +// The algo looks for this pattern to detect recursion: +// +// ancestor(T1, T2), ancestor(T2, T3), T3 <= T2 <= T1 +// +// Such that: +// +// set(props(T1)) = set(props(T2)) +// +// This needs to use an approximate and not strict equality because the leaf node of a recursively unrolled type will +// drop recursive properties and therefore not strictly match the ancestor. The prop-set condition prevents accidentally +// identifying non-recursive subset instances as recursive instances. +type recursionDetector struct { + schema *pschema.PackageSpec + detectedRecursiveTypes map[tokens.Type]struct{} + cmp *comparer +} + +func newRecursionDetector(schema *pschema.PackageSpec) *recursionDetector { + return &recursionDetector{ + schema: schema, + detectedRecursiveTypes: map[tokens.Type]struct{}{}, + cmp: &comparer{schema: schema}, + } +} + +// Starting from the set of starterTypes, detects recursion and reports it. The keys of the resulting map are the +// recursion roots, and the values are sets of recursive instances for each root. +func (rd *recursionDetector) Detect(starterTypes []tokens.Type) map[tokens.Type]map[tokens.Type]struct{} { + // First pass: detect recursion roots. + vis := &typeVisitor{Schema: rd.schema, Visit: rd.detectRootsVisitor} + vis.VisitTypes(starterTypes...) + + roots := rd.roots() + + detected := map[tokens.Type]map[tokens.Type]struct{}{} + for _, r := range roots { + detected[r] = map[tokens.Type]struct{}{} + } + + // Second pass: detect instances. + vis2 := &typeVisitor{Schema: rd.schema, Visit: func(_ []tokens.Type, current tokens.Type) bool { + for _, root := range roots { + if rd.cmp.LessThanOrEqualTypeRefs(current, root) && current != root { + detected[root][current] = struct{}{} + return true + } + } + return true + }} + + vis2.VisitTypes(starterTypes...) + return detected +} + +func (rd *recursionDetector) sorted(types []tokens.Type) []tokens.Type { + tokens := slices.Clone(types) + sort.Slice(tokens, func(i, j int) bool { + if len(tokens[i]) < len(tokens[j]) { + return true + } + return tokens[i] < tokens[j] + }) + return tokens +} + +func (rd *recursionDetector) unique(types []tokens.Type) []tokens.Type { + result := []tokens.Type{} + for _, t := range types { + seen := false + for _, s := range result { + if rd.cmp.EqualTypeRefs(s, t) { + seen = true + break + } + } + if !seen { + result = append(result, t) + } + } + return result +} + +func (rd *recursionDetector) roots() []tokens.Type { + tt := []tokens.Type{} + for t := range rd.detectedRecursiveTypes { + tt = append(tt, t) + } + return rd.unique(rd.sorted(tt)) +} + +func (rd *recursionDetector) detectRootsVisitor(ancestors []tokens.Type, current tokens.Type) bool { + for i, ai := range ancestors { + if _, visited := rd.detectedRecursiveTypes[ai]; visited { + continue + } + for _, aj := range ancestors[i+1:] { + if rd.detect(ai, aj, current) { + if rd.detectedRecursiveTypes == nil { + rd.detectedRecursiveTypes = map[tokens.Type]struct{}{} + } + rd.detectedRecursiveTypes[ai] = struct{}{} + return true + } + } + } + return true +} + +func (rd *recursionDetector) detect(t1, t2, t3 tokens.Type) bool { + return rd.cmp.LessThanTypeRefs(t3, t2) && rd.cmp.LessThanTypeRefs(t2, t1) && rd.sameProps(t1, t2) +} + +func (rd *recursionDetector) sameProps(t1, t2 tokens.Type) bool { + t1d, ok1 := rd.schema.Types[string(t1)] + t2d, ok2 := rd.schema.Types[string(t2)] + if !ok1 || !ok2 { + return false + } + if len(t1d.Properties) != len(t2d.Properties) { + return false + } + for k := range t1d.Properties { + if _, ok := t2d.Properties[k]; !ok { + return false + } + } + return true +} diff --git a/pkg/tfgen/unrec/recursion_detector_test.go b/pkg/tfgen/unrec/recursion_detector_test.go new file mode 100644 index 000000000..0cc15f4e1 --- /dev/null +++ b/pkg/tfgen/unrec/recursion_detector_test.go @@ -0,0 +1,38 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "testing" + + "github.com/hexops/autogold/v2" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" +) + +func TestRecursionDetector(t *testing.T) { + s := exampleSchema(t) + rd := newRecursionDetector(s) + + starterTypes := []tokens.Type{} + for _, r := range s.Resources { + for _, p := range r.Properties { + if ref, ok := parseLocalRef(p.TypeSpec.Ref); ok { + starterTypes = append(starterTypes, ref) + } + } + } + + autogold.ExpectFile(t, rd.Detect(starterTypes)) +} diff --git a/pkg/tfgen/unrec/simplify.go b/pkg/tfgen/unrec/simplify.go new file mode 100644 index 000000000..3bb55db1d --- /dev/null +++ b/pkg/tfgen/unrec/simplify.go @@ -0,0 +1,120 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "context" + + pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" +) + +func SimplifyRecursiveTypes(ctx context.Context, schema *pschema.PackageSpec) error { + rewriteRules, err := computeAllRewriteRules(schema) + if err != nil { + return err + } + return rewriteTypeRefs(rewriteRules, schema) +} + +func computeAllRewriteRules(schema *pschema.PackageSpec) (map[tokens.Type]tokens.Type, error) { + cmp := &comparer{schema: schema} + + rewriteRules := make(map[tokens.Type]tokens.Type) + + for _, r := range schema.Resources { + typeRefs, err := findResourceTypeReferences(r) + if err != nil { + return nil, err + } + if err := computeRewriteRules(cmp, schema, typeRefs, rewriteRules); err != nil { + return nil, err + } + } + + for _, f := range schema.Functions { + typeRefs, err := findFunctionTypeReferences(f) + if err != nil { + return nil, err + } + if err := computeRewriteRules(cmp, schema, typeRefs, rewriteRules); err != nil { + return nil, err + } + } + + return rewriteRules, nil +} + +// Starting from starterTypes, detect recursive type roots. Simplify all types reachable from the recursive type roots. +// Simplification rewrites type A with type B if A<=B according to [comparer.LessThanTypeRefs]. +// +// Although scoped to the progeny of recursive types, this may still end up over-eagerly rewriting logically distinct +// but structurally identical types and may require some refinement. +func computeRewriteRules( + cmp *comparer, + schema *pschema.PackageSpec, + starterTypes typeRefs, + rewriteRules map[tokens.Type]tokens.Type, +) error { + // Detect recursive type patterns first. + rd := newRecursionDetector(schema) + recursionGraph := rd.Detect(starterTypes.Slice()) + + // Rewrite every recursive type into its root. + for root, instances := range recursionGraph { + for instance := range instances { + rewriteRules[instance] = root + } + } + + // Find all types reachable from the recursive type references. + recursiveRefs := newTypeRefs() + for root, instances := range recursionGraph { + recursiveRefs.Add(root) + for instance := range instances { + recursiveRefs.Add(instance) + } + } + allRefs, err := findTypeReferenceTransitiveClosure(schema, recursiveRefs) + if err != nil { + return err + } + + // Rewrite identical types into "best" types minimizing token length. + for _, eqc := range typeEqualityClasses(cmp.WithRewrites(rewriteRules), allRefs) { + best := eqc.Best() + for _, typ := range eqc.Slice() { + if typ != best { + rewriteRules[typ] = best + } + } + } + + return nil +} + +func typeEqualityClasses(cmp *comparer, types typeRefs) []typeRefs { + eq := func(a, b tokens.Type) bool { + return cmp.EqualTypeRefs(a, b) + } + eClasses := equalityClasses(eq, types.Slice()) + var result []typeRefs + for _, c := range eClasses { + tr := make(typeRefs) + tr.Add(c...) + result = append(result, tr) + } + return result +} diff --git a/pkg/tfgen/unrec/simplify_test.go b/pkg/tfgen/unrec/simplify_test.go new file mode 100644 index 000000000..02fd81f04 --- /dev/null +++ b/pkg/tfgen/unrec/simplify_test.go @@ -0,0 +1,35 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "context" + "testing" + + "encoding/json" + "github.com/hexops/autogold/v2" + "github.com/stretchr/testify/require" +) + +func TestSimplify(t *testing.T) { + s := exampleSchema(t) + err := SimplifyRecursiveTypes(context.Background(), s) + require.NoError(t, err) + + bytes, err := json.MarshalIndent(s, "", " ") + require.NoError(t, err) + + autogold.ExpectFile(t, autogold.Raw(string(bytes))) +} diff --git a/pkg/tfgen/unrec/testdata/TestRecursionDetector.golden b/pkg/tfgen/unrec/testdata/TestRecursionDetector.golden new file mode 100644 index 000000000..518dda7a2 --- /dev/null +++ b/pkg/tfgen/unrec/testdata/TestRecursionDetector.golden @@ -0,0 +1,15 @@ +map[tokens.Type]map[tokens.Type]struct{}{ + tokens.Type("myprov:index/WebAclStatementAndStatement:WebAclStatementAndStatement"): { + tokens.Type("myprov:index/WebAclStatementAndStatementStatementAndStatement:WebAclStatementAndStatementStatementAndStatement"): {}, + tokens.Type("myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatement"): {}, + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatement"): {}, + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement"): {}, + }, + tokens.Type("myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement"): { + tokens.Type("myprov:index/WebAclStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatement"): {}, + tokens.Type("myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatement"): {}, + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatement:WebAclStatementRateBasedStatementScopeDownStatement"): {}, + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement"): {}, + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement"): {}, + }, +} diff --git a/pkg/tfgen/unrec/testdata/TestSimplify.golden b/pkg/tfgen/unrec/testdata/TestSimplify.golden new file mode 100644 index 000000000..648b1f970 --- /dev/null +++ b/pkg/tfgen/unrec/testdata/TestSimplify.golden @@ -0,0 +1,297 @@ +{ + "name": "myprov", + "attribution": "This Pulumi package is based on the [`myprov` Terraform Provider](https://github.com/terraform-providers/terraform-provider-myprov).", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true, + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-myprov)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-myprov` repo](/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-myprov` repo](https://github.com/terraform-providers/terraform-provider-myprov/issues)." + }, + "python": { + "compatibility": "tfbridge20", + "pyproject": {}, + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-myprov)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-myprov` repo](/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-myprov` repo](https://github.com/terraform-providers/terraform-provider-myprov/issues)." + } + }, + "config": {}, + "types": { + "myprov:index/WebAclStatement:WebAclStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatement:WebAclStatementAndStatement" + }, + "rateBasedStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatement:WebAclStatementRateBasedStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatement:WebAclStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatement:WebAclStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementXssMatchStatement:WebAclStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatement:WebAclStatementAndStatement": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement" + } + } + }, + "type": "object", + "required": [ + "statements" + ] + }, + "myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatement:WebAclStatementAndStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatement:WebAclStatementRateBasedStatement": { + "properties": { + "aggregateKeyType": { + "type": "string" + }, + "scopeDownStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRegexMatchStatement:WebAclStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatementFieldToMatch:WebAclStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatementTextTransformation:WebAclStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRegexMatchStatementFieldToMatch:WebAclStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRegexMatchStatementTextTransformation:WebAclStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementSqliMatchStatement:WebAclStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatementFieldToMatch:WebAclStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatementTextTransformation:WebAclStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementSqliMatchStatementFieldToMatch:WebAclStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementSqliMatchStatementTextTransformation:WebAclStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementXssMatchStatement:WebAclStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementXssMatchStatementFieldToMatch:WebAclStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementXssMatchStatementFieldToMatch:WebAclStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementXssMatchStatementFieldToMatchMethod:WebAclStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementXssMatchStatementFieldToMatchMethod:WebAclStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + } + }, + "provider": { + "description": "The provider type for the myprov package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n" + }, + "resources": { + "myprov:index:WebAcl": { + "properties": { + "statement": { + "$ref": "#/types/myprov:index/WebAclStatement:WebAclStatement" + } + }, + "required": [ + "statement" + ], + "inputProperties": { + "statement": { + "$ref": "#/types/myprov:index/WebAclStatement:WebAclStatement" + } + }, + "requiredInputs": [ + "statement" + ], + "stateInputs": { + "description": "Input properties used for looking up and filtering WebAcl resources.\n", + "properties": { + "statement": { + "$ref": "#/types/myprov:index/WebAclStatement:WebAclStatement" + } + }, + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/pkg/tfgen/unrec/testdata/TestTypeVisitor.golden b/pkg/tfgen/unrec/testdata/TestTypeVisitor.golden new file mode 100644 index 000000000..9ddfe4ae3 --- /dev/null +++ b/pkg/tfgen/unrec/testdata/TestTypeVisitor.golden @@ -0,0 +1,8 @@ +[]tokens.Type{ + tokens.Type("myprov:index/WebAclStatement:WebAclStatement"), + tokens.Type("myprov:index/WebAclStatementRateBasedStatement:WebAclStatementRateBasedStatement"), + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatement:WebAclStatementRateBasedStatementScopeDownStatement"), + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatement"), + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement"), + tokens.Type("myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement"), +} diff --git a/pkg/tfgen/unrec/testdata/test-schema.json b/pkg/tfgen/unrec/testdata/test-schema.json new file mode 100644 index 000000000..87a10f78f --- /dev/null +++ b/pkg/tfgen/unrec/testdata/test-schema.json @@ -0,0 +1,1014 @@ +{ + "name": "myprov", + "attribution": "This Pulumi package is based on the [`myprov` Terraform Provider](https://github.com/terraform-providers/terraform-provider-myprov).", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-myprov)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-myprov` repo](/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-myprov` repo](https://github.com/terraform-providers/terraform-provider-myprov/issues).", + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true + }, + "python": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-myprov)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-myprov` repo](/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-myprov` repo](https://github.com/terraform-providers/terraform-provider-myprov/issues).", + "compatibility": "tfbridge20", + "pyproject": {} + } + }, + "config": {}, + "types": { + "myprov:index/WebAclStatement:WebAclStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatement:WebAclStatementAndStatement" + }, + "rateBasedStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatement:WebAclStatementRateBasedStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatement:WebAclStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatement:WebAclStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementXssMatchStatement:WebAclStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatement:WebAclStatementAndStatement": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement" + } + } + }, + "type": "object", + "required": [ + "statements" + ] + }, + "myprov:index/WebAclStatementAndStatementStatement:WebAclStatementAndStatementStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatement:WebAclStatementAndStatementStatementAndStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatement:WebAclStatementAndStatementStatementAndStatement": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatement" + } + } + }, + "type": "object", + "required": [ + "statements" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatement": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatement" + } + } + }, + "type": "object", + "required": [ + "statements" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatement": { + "properties": { + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementRegexMatchStatement:WebAclStatementAndStatementStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementAndStatementStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementAndStatementStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementSqliMatchStatement:WebAclStatementAndStatementStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementAndStatementStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementAndStatementStatementXssMatchStatement:WebAclStatementAndStatementStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementAndStatementStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatement:WebAclStatementRateBasedStatement": { + "properties": { + "aggregateKeyType": { + "type": "string" + }, + "scopeDownStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatement:WebAclStatementRateBasedStatementScopeDownStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatement:WebAclStatementRateBasedStatementScopeDownStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatement": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement" + } + } + }, + "type": "object", + "required": [ + "statements" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatement": { + "properties": { + "andStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement" + }, + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatement": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement" + } + } + }, + "type": "object", + "required": [ + "statements" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement": { + "properties": { + "regexMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatement" + }, + "sqliMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatement" + }, + "xssMatchStatement": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatement" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementTextTransformation:WebAclStatementRateBasedStatementScopeDownStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatement:WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatch:WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatchMethod:WebAclStatementRateBasedStatementScopeDownStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRegexMatchStatement:WebAclStatementRegexMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatementFieldToMatch:WebAclStatementRegexMatchStatementFieldToMatch" + }, + "regexString": { + "type": "string" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatementTextTransformation:WebAclStatementRegexMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "regexString", + "textTransformations" + ] + }, + "myprov:index/WebAclStatementRegexMatchStatementFieldToMatch:WebAclStatementRegexMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRegexMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementRegexMatchStatementFieldToMatchMethod:WebAclStatementRegexMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementRegexMatchStatementTextTransformation:WebAclStatementRegexMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementSqliMatchStatement:WebAclStatementSqliMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatementFieldToMatch:WebAclStatementSqliMatchStatementFieldToMatch" + }, + "textTransformations": { + "type": "array", + "items": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatementTextTransformation:WebAclStatementSqliMatchStatementTextTransformation" + } + } + }, + "type": "object", + "required": [ + "textTransformations" + ] + }, + "myprov:index/WebAclStatementSqliMatchStatementFieldToMatch:WebAclStatementSqliMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementSqliMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementSqliMatchStatementFieldToMatchMethod:WebAclStatementSqliMatchStatementFieldToMatchMethod": { + "type": "object" + }, + "myprov:index/WebAclStatementSqliMatchStatementTextTransformation:WebAclStatementSqliMatchStatementTextTransformation": { + "properties": { + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "priority", + "type" + ] + }, + "myprov:index/WebAclStatementXssMatchStatement:WebAclStatementXssMatchStatement": { + "properties": { + "fieldToMatch": { + "$ref": "#/types/myprov:index/WebAclStatementXssMatchStatementFieldToMatch:WebAclStatementXssMatchStatementFieldToMatch" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementXssMatchStatementFieldToMatch:WebAclStatementXssMatchStatementFieldToMatch": { + "properties": { + "method": { + "$ref": "#/types/myprov:index/WebAclStatementXssMatchStatementFieldToMatchMethod:WebAclStatementXssMatchStatementFieldToMatchMethod" + } + }, + "type": "object" + }, + "myprov:index/WebAclStatementXssMatchStatementFieldToMatchMethod:WebAclStatementXssMatchStatementFieldToMatchMethod": { + "type": "object" + } + }, + "provider": { + "description": "The provider type for the myprov package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n" + }, + "resources": { + "myprov:index:WebAcl": { + "properties": { + "statement": { + "$ref": "#/types/myprov:index/WebAclStatement:WebAclStatement" + } + }, + "required": [ + "statement" + ], + "inputProperties": { + "statement": { + "$ref": "#/types/myprov:index/WebAclStatement:WebAclStatement" + } + }, + "requiredInputs": [ + "statement" + ], + "stateInputs": { + "description": "Input properties used for looking up and filtering WebAcl resources.\n", + "properties": { + "statement": { + "$ref": "#/types/myprov:index/WebAclStatement:WebAclStatement" + } + }, + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/pkg/tfgen/unrec/type_visitor.go b/pkg/tfgen/unrec/type_visitor.go new file mode 100644 index 000000000..e0ed952bf --- /dev/null +++ b/pkg/tfgen/unrec/type_visitor.go @@ -0,0 +1,107 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "slices" + + pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" +) + +type typeVisitor struct { + Schema *pschema.PackageSpec + Visit func(ancestors []tokens.Type, current tokens.Type) bool + + parent *typeVisitor // internal + visiting tokens.Type // internal + seen map[tokens.Type]struct{} // internal +} + +func (tv *typeVisitor) VisitTypes(types ...tokens.Type) { + for _, ty := range types { + tv.visitLocalType(ty) + } +} + +func (tv *typeVisitor) push(ty tokens.Type) *typeVisitor { + copy := *tv + copy.parent = tv + copy.visiting = ty + return © +} + +func (tv *typeVisitor) ancestors() []tokens.Type { + var result []tokens.Type + x := tv + for x != nil { + if x.visiting != "" { + result = append(result, x.visiting) + } + x = x.parent + } + slices.Reverse(result) + return result +} + +func (tv *typeVisitor) visitLocalType(ty tokens.Type) { + cts, found := tv.Schema.Types[string(ty)] + if !found { + return + } + if tv.seen == nil { + tv.seen = map[tokens.Type]struct{}{} + } + if _, seen := tv.seen[ty]; seen { + return + } + tv.seen[ty] = struct{}{} + if tv.Visit(tv.ancestors(), ty) { + tv.push(ty).visitComplexTypeSpec(cts) + } +} + +func (tv *typeVisitor) visitComplexTypeSpec(cts pschema.ComplexTypeSpec) { + if cts.Enum != nil { + return + } + tv.visitObjectTypeSpec(cts.ObjectTypeSpec) +} + +func (tv *typeVisitor) visitObjectTypeSpec(ots pschema.ObjectTypeSpec) { + for _, prop := range ots.Properties { + tv.visitPropertySpec(prop) + } +} + +func (tv *typeVisitor) visitPropertySpec(ots pschema.PropertySpec) { + tv.visitTypeSpec(ots.TypeSpec) +} + +func (tv *typeVisitor) visitTypeSpec(ots pschema.TypeSpec) { + if localType, ok := parseLocalRef(ots.Ref); ok { + tv.visitLocalType(localType) + return + } + if ots.Items != nil { + tv.visitTypeSpec(*ots.Items) + } + if ots.AdditionalProperties != nil { + tv.visitTypeSpec(*ots.AdditionalProperties) + } + for _, ty := range ots.OneOf { + tv.visitTypeSpec(ty) + } +} diff --git a/pkg/tfgen/unrec/type_visitor_test.go b/pkg/tfgen/unrec/type_visitor_test.go new file mode 100644 index 000000000..beac41256 --- /dev/null +++ b/pkg/tfgen/unrec/type_visitor_test.go @@ -0,0 +1,53 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unrec + +import ( + "testing" + + "github.com/hexops/autogold/v2" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" + "github.com/stretchr/testify/require" +) + +func TestTypeVisitor(t *testing.T) { + s := exampleSchema(t) + + starterTypes := []tokens.Type{} + for _, r := range s.Resources { + for _, p := range r.Properties { + if ref, ok := parseLocalRef(p.TypeSpec.Ref); ok { + starterTypes = append(starterTypes, ref) + } + } + } + + count := 0 + + visited := map[tokens.Type][]tokens.Type{} + + vis := &typeVisitor{Schema: s, Visit: func(ancestors []tokens.Type, current tokens.Type) bool { + visited[current] = ancestors + count++ + return true + }} + vis.VisitTypes(starterTypes...) + + require.Equal(t, 90, count) + + //nolint:lll + tok := "myprov:index/WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement:WebAclStatementRateBasedStatementScopeDownStatementAndStatementStatementAndStatementStatement" + autogold.ExpectFile(t, visited[tokens.Type(tok)]) +}