Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce recursive types #570

Merged
merged 13 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
4 changes: 3 additions & 1 deletion provider/cmd/pulumi-resource-datadog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ package main

import (
"context"

_ "embed"

datadog "github.com/pulumi/pulumi-datadog/provider/v4"
"github.com/pulumi/pulumi-terraform-bridge/pf/tfbridge"

datadog "github.com/pulumi/pulumi-datadog/provider/v4"
)

//go:embed schema-embed.json
Expand Down
46,345 changes: 2,016 additions & 44,329 deletions provider/cmd/pulumi-resource-datadog/schema.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion provider/cmd/pulumi-tfgen-datadog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
package main

import (
datadog "github.com/pulumi/pulumi-datadog/provider/v4"
"github.com/pulumi/pulumi-terraform-bridge/pf/tfgen"

datadog "github.com/pulumi/pulumi-datadog/provider/v4"
)

func main() {
Expand Down
256 changes: 256 additions & 0 deletions provider/dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// 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 datadog

import (
"encoding/json"
"fmt"
"strings"

"golang.org/x/text/cases"
"golang.org/x/text/language"

"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"
)

type recType struct{ prefix, suffix string }

func (r recType) has(typ tokens.Type) bool {
name := typ.Name().String()
return strings.HasPrefix(name, r.prefix) &&
strings.HasSuffix(name, r.suffix) &&
len(name) > len(r.prefix)+len(r.suffix)
}

func (r recType) canonical() string {
return fmt.Sprintf("%s:%s/%s:%[3]s", datadogPkg, mainMod, r.prefix+r.suffix)
}

func rerollRecursiveDashboardWidget(spec *schema.PackageSpec) {
widget, ok := spec.Types["datadog:index/DashboardWidget:DashboardWidget"]
contract.Assertf(ok, "must be able to find top level widget")

var defs []recType

for prop := range widget.Properties {
if !strings.HasSuffix(prop, "Definition") {
continue
}
defs = append(defs, recType{
prefix: "DashboardWidget",
suffix: cases.Title(language.English, cases.NoLower).String(prop),
})
}

rerollRecursiveTypes(spec, defs)

var copyTypeByName func(to, from string)
copyTypeByName = func(to, from string) {
src, ok := spec.Types[fmt.Sprintf("%s:%s/%s:%[3]s", datadogPkg, mainMod, from)]
contract.Assertf(ok, "could not find type to copy: %q", from)

// Deep copy
b, err := json.Marshal(src)
contract.AssertNoErrorf(err, "deep copy failed")
src = schema.ComplexTypeSpec{}
err = json.Unmarshal(b, &src)
contract.AssertNoErrorf(err, "deep copy failed")

// Move any subtypes
traverseTypes(&src, func(t *schema.TypeSpec) {
const prefix = "#/types/"
tokStr, ok := strings.CutPrefix(t.Ref, prefix)
if !ok {
return
}

parts := strings.Split(tokStr, tokens.TokenDelimiter)
if rest, ok := strings.CutPrefix(parts[2], from); ok {
name := to + rest
copyTypeByName(name, parts[2])

modParts := strings.Split(parts[1], "/")
parts[1] = modParts[0] + "/" + name
parts[2] = name

t.Ref = "#/types/" + strings.Join(parts, tokens.TokenDelimiter)
}
})

spec.Types[fmt.Sprintf("%s:%s/%s:%[3]s", datadogPkg, mainMod, to)] = src
}

mkRec := func(prefix, postfix, example string) recType {
copyTypeByName(prefix+postfix, example)
return recType{prefix, postfix}
}

rerollRecursiveTypes(spec, []recType{
mkRec("DashboardWidget", "RumQuery", "DashboardWidgetToplistDefinitionRequestRumQuery"),
mkRec("DashboardWidget", "SecurityQuery", "DashboardWidgetToplistDefinitionRequestSecurityQuery"),
mkRec("DashboardWidget", "ApmQuery", "DashboardWidgetQueryTableDefinitionRequestApmQuery"),
mkRec("DashboardWidget", "GroupBy", "DashboardWidgetDistributionDefinitionRequestSecurityQueryGroupBy"),
mkRec("DashboardWidget", "LogQuery", "DashboardWidgetHostmapDefinitionRequestFillLogQuery"),
})
}

func rerollRecursiveTypes(spec *schema.PackageSpec, defs []recType) {
for _, def := range defs {
typ := def.canonical()
_, ok := spec.Types[typ]
if !ok {
contract.Failf("Could not find root type for %T: %q", def, typ)
}
}

// We'll collect all referenced types that we replace with the root
// type, so that we could remove all of elided types and their children.
var elidedRefs []tokens.Type

typ:
for tok := range spec.Types {
tok, err := tokens.ParseTypeToken(tok)
contract.AssertNoErrorf(err, "invalid type token")

for _, def := range defs {
if def.has(tok) {
elidedRefs = append(elidedRefs, tok)
continue typ
}
}
}

var deleteTypeSpec func(*schema.TypeSpec)
var deleteType func(tokens.Type)
deleteTypeSpec = func(typ *schema.TypeSpec) {
if typ == nil {
return
}
deleteTypeSpec(typ.AdditionalProperties)
deleteTypeSpec(typ.Items)

if propRef, ok := strings.CutPrefix(typ.Ref, "#/types/"); ok {
propRefTok, err := tokens.ParseTypeToken(propRef)
contract.AssertNoErrorf(err, "invalid type token")
deleteType(propRefTok)
}
}

deleteType = func(tok tokens.Type) {
t, ok := spec.Types[string(tok)]
if !ok {
// This type may have already been deleted, so we return early.
return
}

for _, prop := range t.ObjectTypeSpec.Properties {
prop := prop
deleteTypeSpec(&prop.TypeSpec)
}

delete(spec.Types, string(tok))
}

for _, elided := range elidedRefs {
deleteType(elided)
}

// We have now deleted all the types we don't want, but that has left dangling
// type references.

fixup := func(t *schema.TypeSpec) {
const prefix = "#/types/"

tokStr, ok := strings.CutPrefix(t.Ref, prefix)
if !ok {
return
}
tok, err := tokens.ParseTypeToken(tokStr)
contract.AssertNoErrorf(err, "invalid type token")

for _, def := range defs {
if def.has(tok) {
t.Ref = prefix + def.canonical()
return
}
}
}

for k, r := range spec.Resources {
r := r
traverseResourceTypes(&r, fixup)
spec.Resources[k] = r

}
for k, d := range spec.Functions {
d := d
traverseFunctionTypes(&d, fixup)
spec.Functions[k] = d
}
for k, typ := range spec.Types {
typ := typ
traverseTypes(&typ, fixup)
spec.Types[k] = typ
}
}

func traverseResourceTypes(r *schema.ResourceSpec, f func(*schema.TypeSpec)) {
for k, v := range r.InputProperties {
v := v
traversePropertyTypes(&v, f)
r.InputProperties[k] = v
}
if r.StateInputs != nil {
traverseObjectTypes(r.StateInputs, f)
}
traverseObjectTypes(&r.ObjectTypeSpec, f)
}

func traverseObjectTypes(o *schema.ObjectTypeSpec, f func(*schema.TypeSpec)) {
for k, v := range o.Properties {
v := v
traversePropertyTypes(&v, f)
o.Properties[k] = v
}
}

func traversePropertyTypes(o *schema.PropertySpec, f func(*schema.TypeSpec)) {
f(&o.TypeSpec)
if o.Items != nil {
f(o.Items)
}
if o.AdditionalProperties != nil {
f(o.AdditionalProperties)
}
}

func traverseTypes(t *schema.ComplexTypeSpec, f func(*schema.TypeSpec)) {
traverseObjectTypes(&t.ObjectTypeSpec, f)
}

func traverseFunctionTypes(t *schema.FunctionSpec, f func(*schema.TypeSpec)) {
if t.Inputs != nil {
traverseObjectTypes(t.Inputs, f)
}
if t.Outputs != nil {
traverseObjectTypes(t.Outputs, f)
}
if t.ReturnType != nil {
traverseObjectTypes(t.ReturnType.ObjectTypeSpec, f)
f(t.ReturnType.TypeSpec)
}
}
53 changes: 35 additions & 18 deletions provider/resources.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
// 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 datadog

import (
// Allow embedding files
"bytes"
"context"
_ "embed"
"fmt"
"path"
"strings"
"unicode"

"github.com/pulumi/pulumi-datadog/provider/v4/pkg/version"
_ "embed" // Allow embedding files

"github.com/terraform-providers/terraform-provider-datadog/datadog"
"github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider"

pfbridge "github.com/pulumi/pulumi-terraform-bridge/pf/tfbridge"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
tks "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/tokens"
shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"github.com/terraform-providers/terraform-provider-datadog/datadog"
"github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider"

"github.com/pulumi/pulumi-datadog/provider/v4/pkg/version"
)

const (
Expand Down Expand Up @@ -71,19 +87,20 @@ func Provider() tfbridge.ProviderInfo {
fwprovider.New(),
)
prov := tfbridge.ProviderInfo{
P: p,
Name: "datadog",
Description: "A Pulumi package for creating and managing Datadog resources.",
Keywords: []string{"pulumi", "datadog"},
License: "Apache-2.0",
Homepage: "https://pulumi.io",
Repository: "https://github.com/pulumi/pulumi-datadog",
Config: map[string]*tfbridge.SchemaInfo{},
MetadataInfo: tfbridge.NewProviderMetadata(metadata),
Version: version.Version,
UpstreamRepoPath: "./upstream",
GitHubOrg: "DataDog",
DocRules: &tfbridge.DocRuleInfo{EditRules: docEditRules},
P: p,
Name: "datadog",
Description: "A Pulumi package for creating and managing Datadog resources.",
Keywords: []string{"pulumi", "datadog"},
License: "Apache-2.0",
Homepage: "https://pulumi.io",
Repository: "https://github.com/pulumi/pulumi-datadog",
Config: map[string]*tfbridge.SchemaInfo{},
MetadataInfo: tfbridge.NewProviderMetadata(metadata),
Version: version.Version,
UpstreamRepoPath: "./upstream",
GitHubOrg: "DataDog",
DocRules: &tfbridge.DocRuleInfo{EditRules: docEditRules},
SchemaPostProcessor: rerollRecursiveDashboardWidget,
Resources: map[string]*tfbridge.ResourceInfo{
"datadog_logs_index": {
Fields: map[string]*tfbridge.SchemaInfo{
Expand Down
Loading
Loading