tpgtools/id.go (94 lines of code) (raw):
// Copyright 2021 Google LLC. All Rights Reserved.
//
// 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 main
import (
"fmt"
"regexp"
"sort"
"strings"
"bitbucket.org/creachadair/stringset"
"github.com/nasa9084/go-openapi"
)
const PatternPart = "{{(\\w+)}}"
func idParts(id string) (parts []string) {
r := regexp.MustCompile(PatternPart)
// returns [["{{field}}", "field"] ...]
idTmplAndParts := r.FindAllStringSubmatch(id, -1)
for _, v := range idTmplAndParts {
parts = append(parts, v[1])
}
return parts
}
// PatternToRegex formats a pattern string into a Python-compatible regex.
func PatternToRegex(s string, allowForwardSlash bool) string {
re := regexp.MustCompile(PatternPart)
if allowForwardSlash {
return re.ReplaceAllString(s, "(?P<$1>.+)")
}
return re.ReplaceAllString(s, "(?P<$1>[^/]+)")
}
// Finds the correct resource id based on the schema and any overrides. Returns whether a custom ID override was used.
func findResourceID(schema *openapi.Schema, overrides Overrides, location string) (string, bool, error) {
id, ok := schema.Extension["x-dcl-id"].(string)
if !ok {
return "", false, fmt.Errorf("Malformed or missing x-dcl-id: %v", schema.Extension["x-dcl-id"])
}
// Resource Override: Custom ID
cid := CustomIDDetails{}
cidOk, err := overrides.ResourceOverrideWithDetails(CustomID, &cid, location)
if err != nil {
return "", false, fmt.Errorf("failed to decode custom id details: %v", err)
}
if cidOk {
id = cid.ID
}
for _, override := range overrides {
if override.Type == CustomName {
if strings.Contains(id, fmt.Sprintf("{{%s}}", *override.Field)) {
id = strings.Replace(id, fmt.Sprintf("{{%s}}", *override.Field), fmt.Sprintf("{{%s}}", override.Details.(map[interface{}]interface{})["name"].(string)), 1)
}
}
}
return id, cidOk, nil
}
// Finds all import formats for a given id. This can include short forms and
// partial forms with inferred project/region/etc
func defaultImportFormats(id string, onlyLongFormFormat bool) (formats []string) {
if onlyLongFormFormat {
return []string{id}
}
uniqueFormats := stringset.New()
uniqueFormats.Add(id)
parts := idParts(id)
for i, v := range parts {
parts[i] = fmt.Sprintf("{{%s}}", v)
}
// short form "{{project}}/{{region}}/{{name}}"
uniqueFormats[strings.Join(parts, "/")] = struct{}{}
// short form sans project
var locationalParts []string
for _, v := range parts {
if v != "{{project}}" {
locationalParts = append(locationalParts, v)
}
}
if len(locationalParts) != 0 {
uniqueFormats.Add(strings.Join(locationalParts, "/"))
}
// short form sans project, region, zone
var resourceParts []string
for _, v := range locationalParts {
if v != "{{zone}}" && v != "{{region}}" {
resourceParts = append(resourceParts, v)
}
}
if len(resourceParts) != 0 {
uniqueFormats.Add(strings.Join(resourceParts, "/"))
}
for _, f := range uniqueFormats.Elements() {
formats = append(formats, f)
}
// formats must be ordered most to least specific
sort.SliceStable(formats, formatComparator(formats))
return formats
}
func shouldAllowForwardSlashInFormat(id string, props []Property) bool {
parts := idParts(id)
for _, v := range parts {
for _, prop := range props {
propSnakeCaseName := jsonToSnakeCase(prop.PackageName).snakecase()
if v == propSnakeCaseName && prop.forwardSlashAllowed {
return true
}
}
}
return false
}