Skip to content

Commit

Permalink
Implemented update actions and prevent field types from being altered…
Browse files Browse the repository at this point in the history
… once created
  • Loading branch information
tleguijt authored and mvantellingen committed Oct 4, 2018
1 parent 6970e45 commit 30e0360
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 20 deletions.
180 changes: 160 additions & 20 deletions commercetools/resource_type.go
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"time"

"github.com/hashicorp/terraform/helper/customdiff"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/labd/commercetools-go-sdk/commercetools"
Expand Down Expand Up @@ -36,6 +37,7 @@ func resourceType() *schema.Resource {
"resource_type_ids": {
Type: schema.TypeList,
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"field": {
Expand Down Expand Up @@ -74,6 +76,33 @@ func resourceType() *schema.Resource {
Computed: true,
},
},
CustomizeDiff: customdiff.All(
customdiff.ValidateChange("field", func(old, new, meta interface{}) error {
oldLookup := resourceTypeCreateFieldLookup(old.([]interface{}))
newV := new.([]interface{})

for _, field := range newV {
newF := field.(map[string]interface{})
name := newF["name"].(string)
oldF, ok := oldLookup[name].(map[string]interface{})
if !ok {
// It means this is a new field, that's ok.
continue
}

log.Printf("[DEBUG] Checking %s", oldF["name"])
oldType := oldF["type"].(*schema.Set).List()[0].(map[string]interface{})
newType := newF["type"].(*schema.Set).List()[0].(map[string]interface{})

if oldType["name"] != newType["name"] {
return fmt.Errorf(
"Field '%s' type changed from %s to %s. Changing types is not supported; please remove the field first and re-define it later",
name, oldType["name"], newType["name"])
}
}
return nil
}),
),
}
}

Expand Down Expand Up @@ -339,16 +368,118 @@ func resourceTypeUpdate(d *schema.ResourceData, m interface{}) error {
Actions: commercetools.UpdateActions{},
}

// TODO: Implement UpdateActions
if d.HasChange("key") {
newKey := d.Get("key").(string)
input.Actions = append(
input.Actions,
&types.ChangeKey{Key: newKey})
}

if d.HasChange("name") {
newName := commercetools.LocalizedString(
expandStringMap(d.Get("name").(map[string]interface{})))
input.Actions = append(
input.Actions,
&types.ChangeName{Name: newName})
}

if d.HasChange("description") {
newDescr := commercetools.LocalizedString(
expandStringMap(d.Get("description").(map[string]interface{})))
input.Actions = append(
input.Actions,
&types.SetDescription{Description: newDescr})
}

if d.HasChange("field") {
old, new := d.GetChange("field")
fieldChangeActions, err := resourceTypeFieldChangeActions(old.([]interface{}), new.([]interface{}))
if err != nil {
return err
}

input.Actions = append(input.Actions, fieldChangeActions...)
}

log.Printf(
"[DEBUG] Will perform update operation with the following actions:\n%s",
stringFormatActions(input.Actions))

_, err := svc.Update(input)
if err != nil {
if ctErr, ok := err.(commercetools.Error); ok {
log.Printf("[DEBUG] %v: %v", ctErr, stringFormatErrorExtras(ctErr))
}
return err
}

return resourceTypeRead(d, m)
}

func resourceTypeCreateFieldLookup(fields []interface{}) map[string]interface{} {
lookup := make(map[string]interface{})
for _, field := range fields {
f := field.(map[string]interface{})
lookup[f["name"].(string)] = field
}
return lookup
}

func resourceTypeFieldChangeActions(oldValues []interface{}, newValues []interface{}) ([]commercetools.UpdateAction, error) {
oldLookup := resourceTypeCreateFieldLookup(oldValues)
newLookup := resourceTypeCreateFieldLookup(newValues)
actions := []commercetools.UpdateAction{}

log.Printf("[DEBUG] Construction Field change actions")

// Action: removeFieldDefinition
for name := range oldLookup {
if _, ok := newLookup[name]; !ok {
log.Printf("[DEBUG] Field deleted: %s", name)
actions = append(actions, types.RemoveFieldDefinition{FieldName: name})
}
}

for name, value := range newLookup {
oldValue, existingField := oldLookup[name]
value := value.(map[string]interface{})
oldValue = oldValue.(map[string]interface{})

fieldDef, err := resourceTypeGetFieldDefinition(value)
if err != nil {
return nil, err
}

if !existingField {
log.Printf("[DEBUG] Field added: %s", name)
actions = append(
actions,
types.AddFieldDefinition{FieldDefinition: *fieldDef})
continue
}

}

// Action: changeLabel
// TODO: Change FieldDefinition Label: https://docs.commercetools.com/http-api-projects-types.html#change-fielddefinition-label

// Action: addEnumValue
// TODO: Add EnumValue to FieldDefinition: https://docs.commercetools.com/http-api-projects-types.html#add-enumvalue-to-fielddefinition

// Action: addLocalizedEnumValue
// TODO: Add LocalizedEnumValue to FieldDefinition: https://docs.commercetools.com/http-api-projects-types.html#add-localizedenumvalue-to-fielddefinition

// Action: changeFieldDefinitionOrder
// TODO: Change the order of FieldDefinitions: https://docs.commercetools.com/http-api-projects-types.html#change-the-order-of-fielddefinitions

// Action: changeEnumValueOrder
// TODO: Change the order of EnumValues: https://docs.commercetools.com/http-api-projects-types.html#change-the-order-of-fielddefinitions

// Action: changeLocalizedEnumValueOrder
// TODO: Change the order of LocalizedEnumValues: https://docs.commercetools.com/http-api-projects-types.html#change-the-order-of-localizedenumvalues
return actions, nil
}

func resourceTypeDelete(d *schema.ResourceData, m interface{}) error {
svc := getTypeService(m)
version := d.Get("version").(int)
Expand All @@ -371,35 +502,44 @@ func resourceTypeGetFieldDefinitions(d *schema.ResourceData) ([]types.FieldDefin
var result []types.FieldDefinition

for _, raw := range input {
i := raw.(map[string]interface{})
fieldTypes, ok := i["type"].(*schema.Set)
fieldDef, err := resourceTypeGetFieldDefinition(raw.(map[string]interface{}))

if !ok {
return nil, fmt.Errorf("No type defined for field definition")
}
if fieldTypes.Len() > 1 {
return nil, fmt.Errorf("More then 1 type definition detected. Please remove the redundant ones")
}
fieldType, err := getFieldType(fieldTypes.List()[0].(map[string]interface{}))
if err != nil {
return nil, err
}

label := commercetools.LocalizedString(
expandStringMap(i["label"].(map[string]interface{})))

result = append(result, types.FieldDefinition{
Type: fieldType,
Name: i["name"].(string),
Label: label,
Required: i["required"].(bool),
InputHint: commercetools.TextInputHint(i["input_hint"].(string)),
})
result = append(result, *fieldDef)
}

return result, nil
}

func resourceTypeGetFieldDefinition(input map[string]interface{}) (*types.FieldDefinition, error) {
fieldTypes, ok := input["type"].(*schema.Set)

if !ok {
return nil, fmt.Errorf("No type defined for field definition")
}
if fieldTypes.Len() > 1 {
return nil, fmt.Errorf("More then 1 type definition detected. Please remove the redundant ones")
}
fieldType, err := getFieldType(fieldTypes.List()[0].(map[string]interface{}))
if err != nil {
return nil, err
}

label := commercetools.LocalizedString(
expandStringMap(input["label"].(map[string]interface{})))

return &types.FieldDefinition{
Type: fieldType,
Name: input["name"].(string),
Label: label,
Required: input["required"].(bool),
InputHint: commercetools.TextInputHint(input["input_hint"].(string)),
}, nil
}

func getFieldType(config map[string]interface{}) (types.FieldType, error) {
typeName, ok := config["name"].(string)
refTypeID, refTypeIDOk := config["reference_type_id"].(string)
Expand Down
27 changes: 27 additions & 0 deletions commercetools/utils.go
Expand Up @@ -3,6 +3,7 @@ package commercetools
import (
"encoding/json"
"fmt"
"strings"

"github.com/labd/commercetools-go-sdk/commercetools"
)
Expand Down Expand Up @@ -33,8 +34,34 @@ func localizedStringToMap(input commercetools.LocalizedString) map[string]interf

func stringFormatObject(object interface{}) string {
data, err := json.MarshalIndent(object, "", " ")

if err != nil {
return fmt.Sprintf("%+v", object)
}
return string(append(data, '\n'))
}

func stringFormatErrorExtras(err commercetools.Error) string {
switch len(err.Errors) {
case 0:
return ""
case 1:
return stringFormatObject(err.Errors[0].Extra())
default:
{
messages := make([]string, len(err.Errors))
for i, item := range err.Errors {
messages[i] = fmt.Sprintf(" %d. %s", i+1, stringFormatObject(item.Extra()))
}
return strings.Join(messages, "\n")
}
}
}

func stringFormatActions(actions commercetools.UpdateActions) string {
lines := make([]string, len(actions))
for i, action := range actions {
lines[i] = fmt.Sprintf("%d: %s", i, stringFormatObject(action))
}
return strings.Join(lines, "\n")
}

0 comments on commit 30e0360

Please sign in to comment.