@@ -25,6 +25,50 @@ import (
2525 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2626)
2727
28+ //Use it to delete TagTemplate Field
29+ func deleteTagTemplateField (d * schema.ResourceData , config * Config , name , billingProject , userAgent string ) error {
30+
31+ url_delete , err := replaceVars (d , config , "{{DataCatalogBasePath}}{{name}}/fields/" + name + "?force={{force_delete}}" )
32+ if err != nil {
33+ return err
34+ }
35+ var obj map [string ]interface {}
36+ res , err := sendRequestWithTimeout (config , "DELETE" , billingProject , url_delete , userAgent , obj , d .Timeout (schema .TimeoutDelete ))
37+ if err != nil {
38+ return fmt .Errorf ("Error deleting TagTemplate Field %v: %s" , name , err )
39+ }
40+
41+ log .Printf ("[DEBUG] Finished deleting TagTemplate Field %q: %#v" , name , res )
42+ return nil
43+ }
44+
45+ //Use it to create TagTemplate Field
46+ func createTagTemplateField (d * schema.ResourceData , config * Config , body map [string ]interface {}, name , billingProject , userAgent string ) error {
47+
48+ url_create , err := replaceVars (d , config , "{{DataCatalogBasePath}}{{name}}/fields" )
49+ if err != nil {
50+ return err
51+ }
52+
53+ url_create , err = addQueryParams (url_create , map [string ]string {"tagTemplateFieldId" : name })
54+ if err != nil {
55+ return err
56+ }
57+
58+ res_create , err := sendRequestWithTimeout (config , "POST" , billingProject , url_create , userAgent , body , d .Timeout (schema .TimeoutCreate ))
59+ if err != nil {
60+ return fmt .Errorf ("Error creating TagTemplate Field: %s" , err )
61+ }
62+
63+ if err != nil {
64+ return fmt .Errorf ("Error creating TagTemplate Field %v: %s" , name , err )
65+ } else {
66+ log .Printf ("[DEBUG] Finished creating TagTemplate Field %v: %#v" , name , res_create )
67+ }
68+
69+ return nil
70+ }
71+
2872func resourceDataCatalogTagTemplate () * schema.Resource {
2973 return & schema.Resource {
3074 Create : resourceDataCatalogTagTemplateCreate ,
@@ -46,14 +90,12 @@ func resourceDataCatalogTagTemplate() *schema.Resource {
4690 "fields" : {
4791 Type : schema .TypeSet ,
4892 Required : true ,
49- ForceNew : true ,
50- Description : `Set of tag template field IDs and the settings for the field. This set is an exhaustive list of the allowed fields. This set must contain at least one field and at most 500 fields.` ,
93+ Description : `Set of tag template field IDs and the settings for the field. This set is an exhaustive list of the allowed fields. This set must contain at least one field and at most 500 fields. The change of field_id will be resulting in re-creating of field. The change of primitive_type will be resulting in re-creating of field, however if the field is a required, you cannot update it.` ,
5194 Elem : & schema.Resource {
5295 Schema : map [string ]* schema.Schema {
5396 "field_id" : {
5497 Type : schema .TypeString ,
5598 Required : true ,
56- ForceNew : true ,
5799 },
58100 "type" : {
59101 Type : schema .TypeList ,
@@ -86,6 +128,7 @@ Can have up to 500 allowed values.`,
86128 },
87129 "primitive_type" : {
88130 Type : schema .TypeString ,
131+ Computed : true ,
89132 Optional : true ,
90133 ValidateFunc : validateEnum ([]string {"DOUBLE" , "STRING" , "BOOL" , "TIMESTAMP" , "" }),
91134 Description : `Represents primitive types - string, bool etc.
@@ -96,21 +139,25 @@ Can have up to 500 allowed values.`,
96139 },
97140 "description" : {
98141 Type : schema .TypeString ,
142+ Computed : true ,
99143 Optional : true ,
100144 Description : `A description for this field.` ,
101145 },
102146 "display_name" : {
103147 Type : schema .TypeString ,
148+ Computed : true ,
104149 Optional : true ,
105150 Description : `The display name for this field.` ,
106151 },
107152 "is_required" : {
108153 Type : schema .TypeBool ,
154+ Computed : true ,
109155 Optional : true ,
110156 Description : `Whether this is a required field. Defaults to false.` ,
111157 },
112158 "order" : {
113159 Type : schema .TypeInt ,
160+ Computed : true ,
114161 Optional : true ,
115162 Description : `The order of this field with respect to other fields in this tag template.
116163A higher value indicates a more important field. The value can be negative.
@@ -314,6 +361,12 @@ func resourceDataCatalogTagTemplateUpdate(d *schema.ResourceData, meta interface
314361 } else if v , ok := d .GetOkExists ("display_name" ); ! isEmptyValue (reflect .ValueOf (v )) && (ok || ! reflect .DeepEqual (v , displayNameProp )) {
315362 obj ["displayName" ] = displayNameProp
316363 }
364+ fieldsProp , err := expandDataCatalogTagTemplateFields (d .Get ("fields" ), d , config )
365+ if err != nil {
366+ return err
367+ } else if v , ok := d .GetOkExists ("fields" ); ! isEmptyValue (reflect .ValueOf (v )) && (ok || ! reflect .DeepEqual (v , fieldsProp )) {
368+ obj ["fields" ] = fieldsProp
369+ }
317370
318371 url , err := replaceVars (d , config , "{{DataCatalogBasePath}}{{name}}" )
319372 if err != nil {
@@ -326,26 +379,153 @@ func resourceDataCatalogTagTemplateUpdate(d *schema.ResourceData, meta interface
326379 if d .HasChange ("display_name" ) {
327380 updateMask = append (updateMask , "displayName" )
328381 }
382+
329383 // updateMask is a URL parameter but not present in the schema, so replaceVars
330384 // won't set it
331385 url , err = addQueryParams (url , map [string ]string {"updateMask" : strings .Join (updateMask , "," )})
332386 if err != nil {
333387 return err
334388 }
335389
336- // err == nil indicates that the billing_project value was found
337- if bp , err := getBillingProject (d , config ); err == nil {
338- billingProject = bp
390+ if len (updateMask ) > 0 {
391+
392+ // err == nil indicates that the billing_project value was found
393+ if bp , err := getBillingProject (d , config ); err == nil {
394+ billingProject = bp
395+ }
396+
397+ res , err := sendRequestWithTimeout (config , "PATCH" , billingProject , url , userAgent , obj , d .Timeout (schema .TimeoutUpdate ))
398+
399+ if err != nil {
400+ return fmt .Errorf ("Error updating TagTemplate %q: %s" , d .Id (), err )
401+ } else {
402+ log .Printf ("[DEBUG] Finished updating TagTemplate %q: %#v" , d .Id (), res )
403+ }
404+
339405 }
340406
341- res , err := sendRequestWithTimeout (config , "PATCH" , billingProject , url , userAgent , obj , d .Timeout (schema .TimeoutUpdate ))
407+ // since fields have a separate endpoint,
408+ // we need to handle it manually
342409
343- if err != nil {
344- return fmt .Errorf ("Error updating TagTemplate %q: %s" , d .Id (), err )
345- } else {
346- log .Printf ("[DEBUG] Finished updating TagTemplate %q: %#v" , d .Id (), res )
410+ type FieldChange struct {
411+ Old , New map [string ]interface {}
347412 }
348413
414+ o , n := d .GetChange ("fields" )
415+ vals := make (map [string ]* FieldChange )
416+
417+ // this will create a dictionary with the value
418+ // of field_id as the key that will contain the
419+ // maps of old and new values
420+ for _ , raw := range o .(* schema.Set ).List () {
421+ obj := raw .(map [string ]interface {})
422+ k := obj ["field_id" ].(string )
423+ vals [k ] = & FieldChange {Old : obj }
424+ }
425+
426+ for _ , raw := range n .(* schema.Set ).List () {
427+ obj := raw .(map [string ]interface {})
428+ k := obj ["field_id" ].(string )
429+ if _ , ok := vals [k ]; ! ok {
430+ // if key is not present in the vals,
431+ // then create an empty object to hold the new value
432+ vals [k ] = & FieldChange {}
433+ }
434+ vals [k ].New = obj
435+ }
436+
437+ // fields schema to create schema.set below
438+ dataCatalogTagTemplateFieldsSchema := & schema.Resource {
439+ Schema : resourceDataCatalogTagTemplate ().Schema ["fields" ].Elem .(* schema.Resource ).Schema ,
440+ }
441+
442+ for name , change := range vals {
443+ // A few different situations to deal with in here:
444+ // - change.Old is nil: create a new role
445+ // - change.New is nil: remove an existing role
446+ // - both are set: test if New is different than Old and update if so
447+
448+ changeOldSet := schema .NewSet (schema .HashResource (dataCatalogTagTemplateFieldsSchema ), []interface {}{})
449+ changeOldSet .Add (change .Old )
450+ var changeOldProp map [string ]interface {}
451+ if len (change .Old ) != 0 {
452+ changeOldProp , _ = expandDataCatalogTagTemplateFields (changeOldSet , nil , nil )
453+ changeOldProp = changeOldProp [name ].(map [string ]interface {})
454+ }
455+
456+ changeNewSet := schema .NewSet (schema .HashResource (dataCatalogTagTemplateFieldsSchema ), []interface {}{})
457+ changeNewSet .Add (change .New )
458+ var changeNewProp map [string ]interface {}
459+ if len (change .New ) != 0 {
460+ changeNewProp , _ = expandDataCatalogTagTemplateFields (changeNewSet , nil , nil )
461+ changeNewProp = changeNewProp [name ].(map [string ]interface {})
462+ }
463+
464+ // if old state is empty, then we have a new field to create
465+ if len (change .Old ) == 0 {
466+ err := createTagTemplateField (d , config , changeNewProp , name , billingProject , userAgent )
467+ if err != nil {
468+ return err
469+ }
470+
471+ continue
472+ }
473+
474+ // if new state is empty, then we need to delete the current field
475+ if len (change .New ) == 0 {
476+ err := deleteTagTemplateField (d , config , name , billingProject , userAgent )
477+ if err != nil {
478+ return err
479+ }
480+
481+ continue
482+ }
483+
484+ // if we have old and new values, but are not equal, update with the new state
485+ if ! reflect .DeepEqual (changeOldProp , changeNewProp ) {
486+ url1 , err := replaceVars (d , config , "{{DataCatalogBasePath}}{{name}}/fields/" + name )
487+ if err != nil {
488+ return err
489+ }
490+
491+ oldType := changeOldProp ["type" ].(map [string ]interface {})
492+ newType := changeNewProp ["type" ].(map [string ]interface {})
493+
494+ if oldType ["primitiveType" ] != newType ["primitiveType" ] {
495+ // As primitiveType can't be changed, it is considered as ForceNew which triggers the deletion of old field and recreation of a new field
496+ // Before that, we need to check that is_required is True for the newType or not, as we don't have support to add new required field in the existing TagTemplate,
497+ // So in such cases, we can simply return the error
498+
499+ // Reason for checking the isRequired in changeNewProp -
500+ // Because this changeNewProp check should be ignored when the user wants to update the primitive type and make it optional rather than keeping it required.
501+ if changeNewProp ["isRequired" ] != nil && changeNewProp ["isRequired" ].(bool ) {
502+ return fmt .Errorf ("Updating the primitive type for a required field on an existing tag template is not supported as TagTemplateField %q is required" , name )
503+ }
504+
505+ // delete changeOldProp
506+ err_delete := deleteTagTemplateField (d , config , name , billingProject , userAgent )
507+ if err_delete != nil {
508+ return err_delete
509+ }
510+
511+ // recreate changeNewProp
512+ err_create := createTagTemplateField (d , config , changeNewProp , name , billingProject , userAgent )
513+ if err_create != nil {
514+ return err_create
515+ }
516+
517+ log .Printf ("[DEBUG] Finished updating TagTemplate Field %q" , name )
518+ return resourceDataCatalogTagTemplateRead (d , meta )
519+ }
520+
521+ res , err := sendRequestWithTimeout (config , "PATCH" , billingProject , url1 , userAgent , changeNewProp , d .Timeout (schema .TimeoutDelete ))
522+ if err != nil {
523+ return fmt .Errorf ("Error updating TagTemplate Field %v: %s" , name , err )
524+ }
525+
526+ log .Printf ("[DEBUG] Finished updating TagTemplate Field %q: %#v" , name , res )
527+ }
528+ }
349529 return resourceDataCatalogTagTemplateRead (d , meta )
350530}
351531
0 commit comments