Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 81 additions & 2 deletions mmv1/products/dataplex/EntryLink.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,27 @@ description: |
EntryLink represents a link between two Entries.
references:
guides:
'Official Documentation': 'https://cloud.google.com/dataplex/docs' # Update with specific EntryLink docs when available
'Official Documentation': 'https://cloud.google.com/dataplex/docs' # Update with specific EntryLink docs when available
api: 'https://cloud.google.com/dataplex/docs/reference/rest/v1/projects.locations.entryGroups.entryLinks'
docs:
base_url: 'projects/{{project}}/locations/{{location}}/entryGroups/{{entry_group_id}}/entryLinks/{{entry_link_id}}'
self_link: 'projects/{{project}}/locations/{{location}}/entryGroups/{{entry_group_id}}/entryLinks/{{entry_link_id}}'
create_url: 'projects/{{project}}/locations/{{location}}/entryGroups/{{entry_group_id}}/entryLinks?entryLinkId={{entry_link_id}}'
immutable: true
update_verb: 'PATCH'
import_format:
- 'projects/{{project}}/locations/{{location}}/entryGroups/{{entry_group_id}}/entryLinks/{{entry_link_id}}'

custom_code:
constants: templates/terraform/constants/dataplex_entry_link.go.tmpl
decoder: templates/terraform/decoders/dataplex_entry_link.go.tmpl
encoder: templates/terraform/encoders/dataplex_entry_link.go.tmpl

error_retry_predicates:
- 'transport_tpg.IsDataplex1PEntryNotFoundError'

timeouts:
insert_minutes: 20
update_minutes: 20
delete_minutes: 20
examples:
- name: 'dataplex_entry_link_basic'
Expand All @@ -42,23 +52,37 @@ examples:
entry_link_name: 'my_entry_link_full'
test_env_vars:
project_number: 'PROJECT_NUMBER'
- name: 'dataplex_entry_link_with_aspect'
primary_resource_id: 'full_entry_link_with_aspect'
primary_resource_name: 'fmt.Sprintf("tf-test-entry-link-full-with-aspect%s", context["random_suffix"])'
ignore_read_extra:
- 'aspects'
vars:
entry_link_name: 'my_entry_link_full_with_aspect'
test_env_vars:
project_number: 'PROJECT_NUMBER'
project_id: 'PROJECT_NAME'

parameters:
- name: 'entryGroupId'
type: String
description: |
The id of the entry group this entry link is in.
url_param_only: true
immutable: true
required: true
- name: 'entryLinkId'
type: String
description: |
The id of the entry link to create.
url_param_only: true
immutable: true
required: true
- name: location
type: String
description: The location for the entry.
url_param_only: true
immutable: true
required: true
properties:
- name: 'name'
Expand All @@ -67,12 +91,14 @@ properties:
The relative resource name of the Entry Link, of the form:
projects/{project_id_or_number}/locations/{location_id}/entryGroups/{entry_group_id}/entryLinks/{entry_link_id}
output: true
immutable: true
- name: 'entryLinkType'
type: String
description: |
Relative resource name of the Entry Link Type used to create this Entry Link. For example:
projects/dataplex-types/locations/global/entryLinkTypes/definition
required: true
immutable: true
- name: 'createTime'
type: Time
description: |
Expand All @@ -88,6 +114,7 @@ properties:
description: |
Specifies the Entries referenced in the Entry Link. There should be exactly two entry references.
required: true
immutable: true
item_type:
type: NestedObject
properties:
Expand All @@ -109,3 +136,55 @@ properties:
enum_values:
- 'SOURCE'
- 'TARGET'
- name: 'aspects'
type: Array
custom_flatten: 'templates/terraform/custom_flatten/dataplex_entry_link_aspects.go.tmpl'
description: |
The Aspects attached to the Entry Link.
item_type:
type: NestedObject
properties:
- name: 'aspectKey'
type: String
required: true
description: |
The map keys of the Aspects which the service should modify.
It should be the aspect type reference in the format `{project_number}.{location_id}.{aspect_type_id}`.
- name: 'aspect'
type: NestedObject
required: true
properties:
- name: 'aspectType'
type: String
output: true
description: |
The resource name of the type used to create this Aspect.

- name: 'path'
type: String
output: true
description: |
The path in the entry link under which the aspect is attached.

- name: 'createTime'
type: Time
output: true
description: |
The time when the Aspect was created.

- name: 'updateTime'
type: Time
output: true
description: |
The time when the Aspect was last modified.

- name: 'data'
type: String
required: true
state_func: 'func(v interface{}) string { s, _ := structure.NormalizeJsonString(v); return s }'
custom_flatten: 'templates/terraform/custom_flatten/json_schema.tmpl'
custom_expand: 'templates/terraform/custom_expand/json_schema.tmpl'
validation:
function: 'validation.StringIsJSON'
description: |
The content of the aspect in JSON form, according to its aspect type schema. The maximum size of the field is 120KB (encoded as UTF-8).
152 changes: 152 additions & 0 deletions mmv1/templates/terraform/constants/dataplex_entry_link.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
var (
entryLinkProjectNumberRegex = regexp.MustCompile(`^projects\/[1-9]\d*\/.+$`)
)

// EntryLinkProjectNumberValidation checks if the input string conforms to the pattern:
// "projects/<project-number>/<anything>"
func EntryLinkProjectNumberValidation(i interface{}, k string) (warnings []string, errors []error) {
v, ok := i.(string)

if !ok {
errors = append(errors, fmt.Errorf("expected type of field %q to be string, but got %T", k, i))
return warnings, errors
}

if !entryLinkProjectNumberRegex.MatchString(v) {
errors = append(errors, fmt.Errorf(
"field %q has an invalid format: %q. Expected format: 'projects/<project-number>/<anything>'. Please note that project IDs are not supported.",
k, v,
))
}

return warnings, errors
}

// FilterEntryLinkAspects filters the aspects in res based on aspectKeySet.
// It returns an error if type assertions fail.
func FilterEntryLinkAspects(aspectKeySet map[string]struct{}, res map[string]interface{}) error {
aspectsRaw, ok := res["aspects"]
if !ok || aspectsRaw == nil {
return nil
}

aspectsMap, ok := aspectsRaw.(map[string]interface{})
if !ok {
return fmt.Errorf("FilterEntryLinkAspects: 'aspects' field is not a map[string]interface{}, got %T", aspectsRaw)
}

for key := range aspectsMap {
if _, keep := aspectKeySet[key]; !keep {
delete(aspectsMap, key)
}
}
return nil
}

// AddEntryLinkAspectsToSet adds aspect keys from the aspects interface to the aspectKeySet.
// It returns an error if type assertions fail or expected keys are missing.
func AddEntryLinkAspectsToSet(aspectKeySet map[string]struct{}, aspects interface{}) error {
if aspects == nil {
return nil
}
aspectsSlice, ok := aspects.([]interface{})
if !ok {
return fmt.Errorf("AddEntryLinkAspectsToSet: input 'aspects' is not a []interface{}, got %T", aspects)
}

for i, aspectItemRaw := range aspectsSlice {
aspectMap, ok := aspectItemRaw.(map[string]interface{})
if !ok {
return fmt.Errorf("AddEntryLinkAspectsToSet: item at index %d is not a map[string]interface{}, got %T", i, aspectItemRaw)
}

keyRaw, keyExists := aspectMap["aspect_key"]
if !keyExists {
return fmt.Errorf("AddEntryLinkAspectsToSet: 'aspect_key' not found in aspect item at index %d", i)
}

keyString, ok := keyRaw.(string)
if !ok {
return fmt.Errorf("AddEntryLinkAspectsToSet: 'aspect_key' in item at index %d is not a string, got %T", i, keyRaw)
}
aspectKeySet[keyString] = struct{}{}
}
return nil
}

// InverseTransformEntryLinkAspects converts the "aspects" map back to a slice of maps,
// re-inserting the "aspectKey". Modifies obj in-place.
// It returns an error if type assertions fail.
func InverseTransformEntryLinkAspects(res map[string]interface{}) error {
aspectsRaw, ok := res["aspects"]
if !ok || aspectsRaw == nil {
return nil
}

originalMap, ok := aspectsRaw.(map[string]interface{})
if !ok {
return fmt.Errorf("InverseTransformEntryLinkAspects: 'aspects' field is not a map[string]interface{}, got %T", aspectsRaw)
}

newSlice := make([]interface{}, 0, len(originalMap))

for key, value := range originalMap {
innerMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("InverseTransformEntryLinkAspects: value for key '%s' is not a map[string]interface{}, got %T", key, value)
}
box := make(map[string]interface{}, 2)
box["aspectKey"] = key
box["aspect"] = innerMap
newSlice = append(newSlice, box)
}
res["aspects"] = newSlice
return nil
}

// TransformEntryLinkAspects concisely transforms the "aspects" slice within obj into a map.
// Modifies obj in-place.
// It returns an error if type assertions fail or expected keys are missing.
func TransformEntryLinkAspects(obj map[string]interface{}) error {
aspectsRaw, ok := obj["aspects"]
if !ok || aspectsRaw == nil {
return nil
}

originalSlice, ok := aspectsRaw.([]interface{})
if !ok {
return fmt.Errorf("TransformEntryLinkAspects: 'aspects' field is not a []interface{}, got %T", aspectsRaw)
}

newMap := make(map[string]interface{}, len(originalSlice))
for i, item := range originalSlice {
aspectMap, ok := item.(map[string]interface{})
if !ok {
return fmt.Errorf("TransformEntryLinkAspects: item in 'aspects' slice at index %d is not a map[string]interface{}, got %T", i, item)
}

keyRaw, keyExists := aspectMap["aspectKey"]
if !keyExists {
return fmt.Errorf("TransformEntryLinkAspects: 'aspectKey' not found in aspect item at index %d", i)
}
key, ok := keyRaw.(string)
if !ok {
return fmt.Errorf("TransformEntryLinkAspects: 'aspectKey' in item at index %d is not a string, got %T", i, keyRaw)
}

valueRaw, valueExists := aspectMap["aspect"]
if !valueExists {
newMap[key] = map[string]interface{}{"data": map[string]interface{}{}}
continue
}

value, ok := valueRaw.(map[string]interface{})
if ok {
newMap[key] = value
} else {
newMap[key] = map[string]interface{}{"data": map[string]interface{}{}}
}
}
obj["aspects"] = newMap
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// This file is a transposition of mmv1/templates/terraform/flatten_property_method.go.tmpl
// Most of the code is copied from there, with the exception of sorting logic.
func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
}
l := v.([]interface{})
transformed := make([]map[string]interface{}, 0, len(l))
for _, raw := range l {
original := raw.(map[string]interface{})
if len(original) < 1 {
// Do not include empty json objects coming back from the api
continue
}
transformed = append(transformed, map[string]interface{}{

{{- range $prop := $.ItemType.UserProperties }}
{{- if not (or $prop.IgnoreRead $prop.WriteOnlyLegacy $prop.WriteOnly) }}
"{{ underscore $prop.Name }}": flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}{{$prop.TitlelizeProperty}}(original["{{ $prop.ApiName }}"], d, config),
{{- end }}
{{- end }}
})
}

configData := []map[string]interface{}{}

for _, item := range d.Get("aspects").([]interface{}) {
configData = append(configData, item.(map[string]interface{}))
}

sorted, err := tpgresource.SortMapsByConfigOrder(configData, transformed, "aspect_key")
if err != nil {
log.Printf("[ERROR] Could not sort API response value: %s", err)
return v
}

return sorted
}

{{- if $.NestedProperties }}
{{- range $prop := $.NestedProperties }}
{{ template "flattenPropertyMethod" $prop -}}
{{- end }}
{{- end }}
31 changes: 31 additions & 0 deletions mmv1/templates/terraform/decoders/dataplex_entry_link.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
aspectKeysOfInterest := make(map[string]struct{})
var err error

if d.HasChange("aspects") {
currentAspects, futureAspects := d.GetChange("aspects")
err = AddEntryLinkAspectsToSet(aspectKeysOfInterest, currentAspects)
if err != nil {
return nil, err
}
err = AddEntryLinkAspectsToSet(aspectKeysOfInterest, futureAspects)
if err != nil {
return nil, err
}
} else {
err = AddEntryLinkAspectsToSet(aspectKeysOfInterest, d.Get("aspects"))
if err != nil {
return nil, err
}
}

err = FilterEntryLinkAspects(aspectKeysOfInterest, res)
if err != nil {
return nil, err
}

err = InverseTransformEntryLinkAspects(res)
if err != nil {
return nil, err
}

return res, nil
5 changes: 5 additions & 0 deletions mmv1/templates/terraform/encoders/dataplex_entry_link.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if err := TransformEntryLinkAspects(obj); err != nil {
return nil, err
}

return obj, nil
Loading
Loading