github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/aws/cloudfront_distribution_configuration_structure.go (about)

     1  // CloudFront DistributionConfig structure helpers.
     2  //
     3  // These functions assist in pulling in data from Terraform resource
     4  // configuration for the aws_cloudfront_distribution resource, as there are
     5  // several sub-fields that require their own data type, and do not necessarily
     6  // 1-1 translate to resource configuration.
     7  
     8  package aws
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"reflect"
    14  	"sort"
    15  	"strconv"
    16  	"time"
    17  
    18  	"github.com/aws/aws-sdk-go/aws"
    19  	"github.com/aws/aws-sdk-go/service/cloudfront"
    20  	"github.com/hashicorp/terraform/flatmap"
    21  	"github.com/hashicorp/terraform/helper/hashcode"
    22  	"github.com/hashicorp/terraform/helper/schema"
    23  )
    24  
    25  // cloudFrontRoute53ZoneID defines the route 53 zone ID for CloudFront. This
    26  // is used to set the zone_id attribute.
    27  const cloudFrontRoute53ZoneID = "Z2FDTNDATAQYW2"
    28  
    29  // Define Sort interface for []*string so we can ensure the order of
    30  // geo_restrictions.locations
    31  type StringPtrSlice []*string
    32  
    33  func (p StringPtrSlice) Len() int           { return len(p) }
    34  func (p StringPtrSlice) Less(i, j int) bool { return *p[i] < *p[j] }
    35  func (p StringPtrSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    36  
    37  // Assemble the *cloudfront.DistributionConfig variable. Calls out to various
    38  // expander functions to convert attributes and sub-attributes to the various
    39  // complex structures which are necessary to properly build the
    40  // DistributionConfig structure.
    41  //
    42  // Used by the aws_cloudfront_distribution Create and Update functions.
    43  func expandDistributionConfig(d *schema.ResourceData) *cloudfront.DistributionConfig {
    44  	distributionConfig := &cloudfront.DistributionConfig{
    45  		CacheBehaviors:       expandCacheBehaviors(d.Get("cache_behavior").(*schema.Set)),
    46  		CustomErrorResponses: expandCustomErrorResponses(d.Get("custom_error_response").(*schema.Set)),
    47  		DefaultCacheBehavior: expandDefaultCacheBehavior(d.Get("default_cache_behavior").(*schema.Set).List()[0].(map[string]interface{})),
    48  		Enabled:              aws.Bool(d.Get("enabled").(bool)),
    49  		IsIPV6Enabled:        aws.Bool(d.Get("is_ipv6_enabled").(bool)),
    50  		HttpVersion:          aws.String(d.Get("http_version").(string)),
    51  		Origins:              expandOrigins(d.Get("origin").(*schema.Set)),
    52  		PriceClass:           aws.String(d.Get("price_class").(string)),
    53  	}
    54  	// This sets CallerReference if it's still pending computation (ie: new resource)
    55  	if v, ok := d.GetOk("caller_reference"); ok == false {
    56  		distributionConfig.CallerReference = aws.String(time.Now().Format(time.RFC3339Nano))
    57  	} else {
    58  		distributionConfig.CallerReference = aws.String(v.(string))
    59  	}
    60  	if v, ok := d.GetOk("comment"); ok {
    61  		distributionConfig.Comment = aws.String(v.(string))
    62  	} else {
    63  		distributionConfig.Comment = aws.String("")
    64  	}
    65  	if v, ok := d.GetOk("default_root_object"); ok {
    66  		distributionConfig.DefaultRootObject = aws.String(v.(string))
    67  	} else {
    68  		distributionConfig.DefaultRootObject = aws.String("")
    69  	}
    70  	if v, ok := d.GetOk("logging_config"); ok {
    71  		distributionConfig.Logging = expandLoggingConfig(v.(*schema.Set).List()[0].(map[string]interface{}))
    72  	} else {
    73  		distributionConfig.Logging = expandLoggingConfig(nil)
    74  	}
    75  	if v, ok := d.GetOk("aliases"); ok {
    76  		distributionConfig.Aliases = expandAliases(v.(*schema.Set))
    77  	} else {
    78  		distributionConfig.Aliases = expandAliases(schema.NewSet(aliasesHash, []interface{}{}))
    79  	}
    80  	if v, ok := d.GetOk("restrictions"); ok {
    81  		distributionConfig.Restrictions = expandRestrictions(v.(*schema.Set).List()[0].(map[string]interface{}))
    82  	}
    83  	if v, ok := d.GetOk("viewer_certificate"); ok {
    84  		distributionConfig.ViewerCertificate = expandViewerCertificate(v.(*schema.Set).List()[0].(map[string]interface{}))
    85  	}
    86  	if v, ok := d.GetOk("web_acl_id"); ok {
    87  		distributionConfig.WebACLId = aws.String(v.(string))
    88  	} else {
    89  		distributionConfig.WebACLId = aws.String("")
    90  	}
    91  
    92  	return distributionConfig
    93  }
    94  
    95  // Unpack the *cloudfront.DistributionConfig variable and set resource data.
    96  // Calls out to flatten functions to convert the DistributionConfig
    97  // sub-structures to their respective attributes in the
    98  // aws_cloudfront_distribution resource.
    99  //
   100  // Used by the aws_cloudfront_distribution Read function.
   101  func flattenDistributionConfig(d *schema.ResourceData, distributionConfig *cloudfront.DistributionConfig) error {
   102  	var err error
   103  
   104  	d.Set("enabled", distributionConfig.Enabled)
   105  	d.Set("is_ipv6_enabled", distributionConfig.IsIPV6Enabled)
   106  	d.Set("price_class", distributionConfig.PriceClass)
   107  	d.Set("hosted_zone_id", cloudFrontRoute53ZoneID)
   108  
   109  	err = d.Set("default_cache_behavior", flattenDefaultCacheBehavior(distributionConfig.DefaultCacheBehavior))
   110  	if err != nil {
   111  		return err
   112  	}
   113  	err = d.Set("viewer_certificate", flattenViewerCertificate(distributionConfig.ViewerCertificate))
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	if distributionConfig.CallerReference != nil {
   119  		d.Set("caller_reference", distributionConfig.CallerReference)
   120  	}
   121  	if distributionConfig.Comment != nil {
   122  		if *distributionConfig.Comment != "" {
   123  			d.Set("comment", distributionConfig.Comment)
   124  		}
   125  	}
   126  	if distributionConfig.DefaultRootObject != nil {
   127  		d.Set("default_root_object", distributionConfig.DefaultRootObject)
   128  	}
   129  	if distributionConfig.HttpVersion != nil {
   130  		d.Set("http_version", distributionConfig.HttpVersion)
   131  	}
   132  	if distributionConfig.WebACLId != nil {
   133  		d.Set("web_acl_id", distributionConfig.WebACLId)
   134  	}
   135  
   136  	if distributionConfig.CustomErrorResponses != nil {
   137  		err = d.Set("custom_error_response", flattenCustomErrorResponses(distributionConfig.CustomErrorResponses))
   138  		if err != nil {
   139  			return err
   140  		}
   141  	}
   142  	if distributionConfig.CacheBehaviors != nil {
   143  		err = d.Set("cache_behavior", flattenCacheBehaviors(distributionConfig.CacheBehaviors))
   144  		if err != nil {
   145  			return err
   146  		}
   147  	}
   148  
   149  	if distributionConfig.Logging != nil && *distributionConfig.Logging.Enabled {
   150  		err = d.Set("logging_config", flattenLoggingConfig(distributionConfig.Logging))
   151  	} else {
   152  		err = d.Set("logging_config", schema.NewSet(loggingConfigHash, []interface{}{}))
   153  	}
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	if distributionConfig.Aliases != nil {
   159  		err = d.Set("aliases", flattenAliases(distributionConfig.Aliases))
   160  		if err != nil {
   161  			return err
   162  		}
   163  	}
   164  	if distributionConfig.Restrictions != nil {
   165  		err = d.Set("restrictions", flattenRestrictions(distributionConfig.Restrictions))
   166  		if err != nil {
   167  			return err
   168  		}
   169  	}
   170  	if *distributionConfig.Origins.Quantity > 0 {
   171  		err = d.Set("origin", flattenOrigins(distributionConfig.Origins))
   172  		if err != nil {
   173  			return err
   174  		}
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  func expandDefaultCacheBehavior(m map[string]interface{}) *cloudfront.DefaultCacheBehavior {
   181  	cb := expandCacheBehavior(m)
   182  	var dcb cloudfront.DefaultCacheBehavior
   183  
   184  	simpleCopyStruct(cb, &dcb)
   185  	return &dcb
   186  }
   187  
   188  func flattenDefaultCacheBehavior(dcb *cloudfront.DefaultCacheBehavior) *schema.Set {
   189  	m := make(map[string]interface{})
   190  	var cb cloudfront.CacheBehavior
   191  
   192  	simpleCopyStruct(dcb, &cb)
   193  	m = flattenCacheBehavior(&cb)
   194  	return schema.NewSet(defaultCacheBehaviorHash, []interface{}{m})
   195  }
   196  
   197  // Assemble the hash for the aws_cloudfront_distribution default_cache_behavior
   198  // TypeSet attribute.
   199  func defaultCacheBehaviorHash(v interface{}) int {
   200  	var buf bytes.Buffer
   201  	m := v.(map[string]interface{})
   202  	buf.WriteString(fmt.Sprintf("%t-", m["compress"].(bool)))
   203  	buf.WriteString(fmt.Sprintf("%s-", m["viewer_protocol_policy"].(string)))
   204  	buf.WriteString(fmt.Sprintf("%s-", m["target_origin_id"].(string)))
   205  	buf.WriteString(fmt.Sprintf("%d-", forwardedValuesHash(m["forwarded_values"].(*schema.Set).List()[0].(map[string]interface{}))))
   206  	buf.WriteString(fmt.Sprintf("%d-", m["min_ttl"].(int)))
   207  	if d, ok := m["trusted_signers"]; ok {
   208  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   209  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   210  		}
   211  	}
   212  	if d, ok := m["max_ttl"]; ok {
   213  		buf.WriteString(fmt.Sprintf("%d-", d.(int)))
   214  	}
   215  	if d, ok := m["smooth_streaming"]; ok {
   216  		buf.WriteString(fmt.Sprintf("%t-", d.(bool)))
   217  	}
   218  	if d, ok := m["default_ttl"]; ok {
   219  		buf.WriteString(fmt.Sprintf("%d-", d.(int)))
   220  	}
   221  	if d, ok := m["allowed_methods"]; ok {
   222  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   223  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   224  		}
   225  	}
   226  	if d, ok := m["cached_methods"]; ok {
   227  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   228  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   229  		}
   230  	}
   231  	return hashcode.String(buf.String())
   232  }
   233  
   234  func expandCacheBehaviors(s *schema.Set) *cloudfront.CacheBehaviors {
   235  	var qty int64
   236  	var items []*cloudfront.CacheBehavior
   237  	for _, v := range s.List() {
   238  		items = append(items, expandCacheBehavior(v.(map[string]interface{})))
   239  		qty++
   240  	}
   241  	return &cloudfront.CacheBehaviors{
   242  		Quantity: aws.Int64(qty),
   243  		Items:    items,
   244  	}
   245  }
   246  
   247  func flattenCacheBehaviors(cbs *cloudfront.CacheBehaviors) *schema.Set {
   248  	s := []interface{}{}
   249  	for _, v := range cbs.Items {
   250  		s = append(s, flattenCacheBehavior(v))
   251  	}
   252  	return schema.NewSet(cacheBehaviorHash, s)
   253  }
   254  
   255  func expandCacheBehavior(m map[string]interface{}) *cloudfront.CacheBehavior {
   256  	cb := &cloudfront.CacheBehavior{
   257  		Compress:             aws.Bool(m["compress"].(bool)),
   258  		ViewerProtocolPolicy: aws.String(m["viewer_protocol_policy"].(string)),
   259  		TargetOriginId:       aws.String(m["target_origin_id"].(string)),
   260  		ForwardedValues:      expandForwardedValues(m["forwarded_values"].(*schema.Set).List()[0].(map[string]interface{})),
   261  		MinTTL:               aws.Int64(int64(m["min_ttl"].(int))),
   262  		MaxTTL:               aws.Int64(int64(m["max_ttl"].(int))),
   263  		DefaultTTL:           aws.Int64(int64(m["default_ttl"].(int))),
   264  	}
   265  	if v, ok := m["trusted_signers"]; ok {
   266  		cb.TrustedSigners = expandTrustedSigners(v.([]interface{}))
   267  	} else {
   268  		cb.TrustedSigners = expandTrustedSigners([]interface{}{})
   269  	}
   270  	if v, ok := m["smooth_streaming"]; ok {
   271  		cb.SmoothStreaming = aws.Bool(v.(bool))
   272  	}
   273  	if v, ok := m["allowed_methods"]; ok {
   274  		cb.AllowedMethods = expandAllowedMethods(v.([]interface{}))
   275  	}
   276  	if v, ok := m["cached_methods"]; ok {
   277  		cb.AllowedMethods.CachedMethods = expandCachedMethods(v.([]interface{}))
   278  	}
   279  	if v, ok := m["path_pattern"]; ok {
   280  		cb.PathPattern = aws.String(v.(string))
   281  	}
   282  	return cb
   283  }
   284  
   285  func flattenCacheBehavior(cb *cloudfront.CacheBehavior) map[string]interface{} {
   286  	m := make(map[string]interface{})
   287  
   288  	m["compress"] = *cb.Compress
   289  	m["viewer_protocol_policy"] = *cb.ViewerProtocolPolicy
   290  	m["target_origin_id"] = *cb.TargetOriginId
   291  	m["forwarded_values"] = schema.NewSet(forwardedValuesHash, []interface{}{flattenForwardedValues(cb.ForwardedValues)})
   292  	m["min_ttl"] = int(*cb.MinTTL)
   293  
   294  	if len(cb.TrustedSigners.Items) > 0 {
   295  		m["trusted_signers"] = flattenTrustedSigners(cb.TrustedSigners)
   296  	}
   297  	if cb.MaxTTL != nil {
   298  		m["max_ttl"] = int(*cb.MaxTTL)
   299  	}
   300  	if cb.SmoothStreaming != nil {
   301  		m["smooth_streaming"] = *cb.SmoothStreaming
   302  	}
   303  	if cb.DefaultTTL != nil {
   304  		m["default_ttl"] = int(*cb.DefaultTTL)
   305  	}
   306  	if cb.AllowedMethods != nil {
   307  		m["allowed_methods"] = flattenAllowedMethods(cb.AllowedMethods)
   308  	}
   309  	if cb.AllowedMethods.CachedMethods != nil {
   310  		m["cached_methods"] = flattenCachedMethods(cb.AllowedMethods.CachedMethods)
   311  	}
   312  	if cb.PathPattern != nil {
   313  		m["path_pattern"] = *cb.PathPattern
   314  	}
   315  	return m
   316  }
   317  
   318  // Assemble the hash for the aws_cloudfront_distribution cache_behavior
   319  // TypeSet attribute.
   320  func cacheBehaviorHash(v interface{}) int {
   321  	var buf bytes.Buffer
   322  	m := v.(map[string]interface{})
   323  	buf.WriteString(fmt.Sprintf("%t-", m["compress"].(bool)))
   324  	buf.WriteString(fmt.Sprintf("%s-", m["viewer_protocol_policy"].(string)))
   325  	buf.WriteString(fmt.Sprintf("%s-", m["target_origin_id"].(string)))
   326  	buf.WriteString(fmt.Sprintf("%d-", forwardedValuesHash(m["forwarded_values"].(*schema.Set).List()[0].(map[string]interface{}))))
   327  	buf.WriteString(fmt.Sprintf("%d-", m["min_ttl"].(int)))
   328  	if d, ok := m["trusted_signers"]; ok {
   329  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   330  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   331  		}
   332  	}
   333  	if d, ok := m["max_ttl"]; ok {
   334  		buf.WriteString(fmt.Sprintf("%d-", d.(int)))
   335  	}
   336  	if d, ok := m["smooth_streaming"]; ok {
   337  		buf.WriteString(fmt.Sprintf("%t-", d.(bool)))
   338  	}
   339  	if d, ok := m["default_ttl"]; ok {
   340  		buf.WriteString(fmt.Sprintf("%d-", d.(int)))
   341  	}
   342  	if d, ok := m["allowed_methods"]; ok {
   343  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   344  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   345  		}
   346  	}
   347  	if d, ok := m["cached_methods"]; ok {
   348  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   349  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   350  		}
   351  	}
   352  	if d, ok := m["path_pattern"]; ok {
   353  		buf.WriteString(fmt.Sprintf("%s-", d))
   354  	}
   355  	return hashcode.String(buf.String())
   356  }
   357  
   358  func expandTrustedSigners(s []interface{}) *cloudfront.TrustedSigners {
   359  	var ts cloudfront.TrustedSigners
   360  	if len(s) > 0 {
   361  		ts.Quantity = aws.Int64(int64(len(s)))
   362  		ts.Items = expandStringList(s)
   363  		ts.Enabled = aws.Bool(true)
   364  	} else {
   365  		ts.Quantity = aws.Int64(0)
   366  		ts.Enabled = aws.Bool(false)
   367  	}
   368  	return &ts
   369  }
   370  
   371  func flattenTrustedSigners(ts *cloudfront.TrustedSigners) []interface{} {
   372  	if ts.Items != nil {
   373  		return flattenStringList(ts.Items)
   374  	}
   375  	return []interface{}{}
   376  }
   377  
   378  func expandForwardedValues(m map[string]interface{}) *cloudfront.ForwardedValues {
   379  	fv := &cloudfront.ForwardedValues{
   380  		QueryString: aws.Bool(m["query_string"].(bool)),
   381  	}
   382  	if v, ok := m["cookies"]; ok && v.(*schema.Set).Len() > 0 {
   383  		fv.Cookies = expandCookiePreference(v.(*schema.Set).List()[0].(map[string]interface{}))
   384  	}
   385  	if v, ok := m["headers"]; ok {
   386  		fv.Headers = expandHeaders(v.([]interface{}))
   387  	}
   388  	if v, ok := m["query_string_cache_keys"]; ok {
   389  		fv.QueryStringCacheKeys = expandQueryStringCacheKeys(v.([]interface{}))
   390  	}
   391  	return fv
   392  }
   393  
   394  func flattenForwardedValues(fv *cloudfront.ForwardedValues) map[string]interface{} {
   395  	m := make(map[string]interface{})
   396  	m["query_string"] = *fv.QueryString
   397  	if fv.Cookies != nil {
   398  		m["cookies"] = schema.NewSet(cookiePreferenceHash, []interface{}{flattenCookiePreference(fv.Cookies)})
   399  	}
   400  	if fv.Headers != nil {
   401  		m["headers"] = flattenHeaders(fv.Headers)
   402  	}
   403  	if fv.QueryStringCacheKeys != nil {
   404  		m["query_string_cache_keys"] = flattenQueryStringCacheKeys(fv.QueryStringCacheKeys)
   405  	}
   406  	return m
   407  }
   408  
   409  // Assemble the hash for the aws_cloudfront_distribution forwarded_values
   410  // TypeSet attribute.
   411  func forwardedValuesHash(v interface{}) int {
   412  	var buf bytes.Buffer
   413  	m := v.(map[string]interface{})
   414  	buf.WriteString(fmt.Sprintf("%t-", m["query_string"].(bool)))
   415  	if d, ok := m["cookies"]; ok && d.(*schema.Set).Len() > 0 {
   416  		buf.WriteString(fmt.Sprintf("%d-", cookiePreferenceHash(d.(*schema.Set).List()[0].(map[string]interface{}))))
   417  	}
   418  	if d, ok := m["headers"]; ok {
   419  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   420  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   421  		}
   422  	}
   423  	if d, ok := m["query_string_cache_keys"]; ok {
   424  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   425  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   426  		}
   427  	}
   428  	return hashcode.String(buf.String())
   429  }
   430  
   431  func expandHeaders(d []interface{}) *cloudfront.Headers {
   432  	return &cloudfront.Headers{
   433  		Quantity: aws.Int64(int64(len(d))),
   434  		Items:    expandStringList(d),
   435  	}
   436  }
   437  
   438  func flattenHeaders(h *cloudfront.Headers) []interface{} {
   439  	if h.Items != nil {
   440  		return flattenStringList(h.Items)
   441  	}
   442  	return []interface{}{}
   443  }
   444  
   445  func expandQueryStringCacheKeys(d []interface{}) *cloudfront.QueryStringCacheKeys {
   446  	return &cloudfront.QueryStringCacheKeys{
   447  		Quantity: aws.Int64(int64(len(d))),
   448  		Items:    expandStringList(d),
   449  	}
   450  }
   451  
   452  func flattenQueryStringCacheKeys(k *cloudfront.QueryStringCacheKeys) []interface{} {
   453  	if k.Items != nil {
   454  		return flattenStringList(k.Items)
   455  	}
   456  	return []interface{}{}
   457  }
   458  
   459  func expandCookiePreference(m map[string]interface{}) *cloudfront.CookiePreference {
   460  	cp := &cloudfront.CookiePreference{
   461  		Forward: aws.String(m["forward"].(string)),
   462  	}
   463  	if v, ok := m["whitelisted_names"]; ok {
   464  		cp.WhitelistedNames = expandCookieNames(v.([]interface{}))
   465  	}
   466  	return cp
   467  }
   468  
   469  func flattenCookiePreference(cp *cloudfront.CookiePreference) map[string]interface{} {
   470  	m := make(map[string]interface{})
   471  	m["forward"] = *cp.Forward
   472  	if cp.WhitelistedNames != nil {
   473  		m["whitelisted_names"] = flattenCookieNames(cp.WhitelistedNames)
   474  	}
   475  	return m
   476  }
   477  
   478  // Assemble the hash for the aws_cloudfront_distribution cookies
   479  // TypeSet attribute.
   480  func cookiePreferenceHash(v interface{}) int {
   481  	var buf bytes.Buffer
   482  	m := v.(map[string]interface{})
   483  	buf.WriteString(fmt.Sprintf("%s-", m["forward"].(string)))
   484  	if d, ok := m["whitelisted_names"]; ok {
   485  		for _, e := range sortInterfaceSlice(d.([]interface{})) {
   486  			buf.WriteString(fmt.Sprintf("%s-", e.(string)))
   487  		}
   488  	}
   489  	return hashcode.String(buf.String())
   490  }
   491  
   492  func expandCookieNames(d []interface{}) *cloudfront.CookieNames {
   493  	return &cloudfront.CookieNames{
   494  		Quantity: aws.Int64(int64(len(d))),
   495  		Items:    expandStringList(d),
   496  	}
   497  }
   498  
   499  func flattenCookieNames(cn *cloudfront.CookieNames) []interface{} {
   500  	if cn.Items != nil {
   501  		return flattenStringList(cn.Items)
   502  	}
   503  	return []interface{}{}
   504  }
   505  
   506  func expandAllowedMethods(s []interface{}) *cloudfront.AllowedMethods {
   507  	return &cloudfront.AllowedMethods{
   508  		Quantity: aws.Int64(int64(len(s))),
   509  		Items:    expandStringList(s),
   510  	}
   511  }
   512  
   513  func flattenAllowedMethods(am *cloudfront.AllowedMethods) []interface{} {
   514  	if am.Items != nil {
   515  		return flattenStringList(am.Items)
   516  	}
   517  	return []interface{}{}
   518  }
   519  
   520  func expandCachedMethods(s []interface{}) *cloudfront.CachedMethods {
   521  	return &cloudfront.CachedMethods{
   522  		Quantity: aws.Int64(int64(len(s))),
   523  		Items:    expandStringList(s),
   524  	}
   525  }
   526  
   527  func flattenCachedMethods(cm *cloudfront.CachedMethods) []interface{} {
   528  	if cm.Items != nil {
   529  		return flattenStringList(cm.Items)
   530  	}
   531  	return []interface{}{}
   532  }
   533  
   534  func expandOrigins(s *schema.Set) *cloudfront.Origins {
   535  	qty := 0
   536  	items := []*cloudfront.Origin{}
   537  	for _, v := range s.List() {
   538  		items = append(items, expandOrigin(v.(map[string]interface{})))
   539  		qty++
   540  	}
   541  	return &cloudfront.Origins{
   542  		Quantity: aws.Int64(int64(qty)),
   543  		Items:    items,
   544  	}
   545  }
   546  
   547  func flattenOrigins(ors *cloudfront.Origins) *schema.Set {
   548  	s := []interface{}{}
   549  	for _, v := range ors.Items {
   550  		s = append(s, flattenOrigin(v))
   551  	}
   552  	return schema.NewSet(originHash, s)
   553  }
   554  
   555  func expandOrigin(m map[string]interface{}) *cloudfront.Origin {
   556  	origin := &cloudfront.Origin{
   557  		Id:         aws.String(m["origin_id"].(string)),
   558  		DomainName: aws.String(m["domain_name"].(string)),
   559  	}
   560  	if v, ok := m["custom_header"]; ok {
   561  		origin.CustomHeaders = expandCustomHeaders(v.(*schema.Set))
   562  	}
   563  	if v, ok := m["custom_origin_config"]; ok {
   564  		if s := v.(*schema.Set).List(); len(s) > 0 {
   565  			origin.CustomOriginConfig = expandCustomOriginConfig(s[0].(map[string]interface{}))
   566  		}
   567  	}
   568  	if v, ok := m["origin_path"]; ok {
   569  		origin.OriginPath = aws.String(v.(string))
   570  	}
   571  	if v, ok := m["s3_origin_config"]; ok {
   572  		if s := v.(*schema.Set).List(); len(s) > 0 {
   573  			origin.S3OriginConfig = expandS3OriginConfig(s[0].(map[string]interface{}))
   574  		}
   575  	}
   576  
   577  	// if both custom and s3 origin are missing, add an empty s3 origin
   578  	// One or the other must be specified, but the S3 origin can be "empty"
   579  	if origin.S3OriginConfig == nil && origin.CustomOriginConfig == nil {
   580  		origin.S3OriginConfig = &cloudfront.S3OriginConfig{
   581  			OriginAccessIdentity: aws.String(""),
   582  		}
   583  	}
   584  
   585  	return origin
   586  }
   587  
   588  func flattenOrigin(or *cloudfront.Origin) map[string]interface{} {
   589  	m := make(map[string]interface{})
   590  	m["origin_id"] = *or.Id
   591  	m["domain_name"] = *or.DomainName
   592  	if or.CustomHeaders != nil {
   593  		m["custom_header"] = flattenCustomHeaders(or.CustomHeaders)
   594  	}
   595  	if or.CustomOriginConfig != nil {
   596  		m["custom_origin_config"] = schema.NewSet(customOriginConfigHash, []interface{}{flattenCustomOriginConfig(or.CustomOriginConfig)})
   597  	}
   598  	if or.OriginPath != nil {
   599  		m["origin_path"] = *or.OriginPath
   600  	}
   601  	if or.S3OriginConfig != nil {
   602  		if or.S3OriginConfig.OriginAccessIdentity != nil && *or.S3OriginConfig.OriginAccessIdentity != "" {
   603  			m["s3_origin_config"] = schema.NewSet(s3OriginConfigHash, []interface{}{flattenS3OriginConfig(or.S3OriginConfig)})
   604  		}
   605  	}
   606  	return m
   607  }
   608  
   609  // Assemble the hash for the aws_cloudfront_distribution origin
   610  // TypeSet attribute.
   611  func originHash(v interface{}) int {
   612  	var buf bytes.Buffer
   613  	m := v.(map[string]interface{})
   614  	buf.WriteString(fmt.Sprintf("%s-", m["origin_id"].(string)))
   615  	buf.WriteString(fmt.Sprintf("%s-", m["domain_name"].(string)))
   616  	if v, ok := m["custom_header"]; ok {
   617  		buf.WriteString(fmt.Sprintf("%d-", customHeadersHash(v.(*schema.Set))))
   618  	}
   619  	if v, ok := m["custom_origin_config"]; ok {
   620  		if s := v.(*schema.Set).List(); len(s) > 0 {
   621  			buf.WriteString(fmt.Sprintf("%d-", customOriginConfigHash((s[0].(map[string]interface{})))))
   622  		}
   623  	}
   624  	if v, ok := m["origin_path"]; ok {
   625  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   626  	}
   627  	if v, ok := m["s3_origin_config"]; ok {
   628  		if s := v.(*schema.Set).List(); len(s) > 0 {
   629  			buf.WriteString(fmt.Sprintf("%d-", s3OriginConfigHash((s[0].(map[string]interface{})))))
   630  		}
   631  	}
   632  	return hashcode.String(buf.String())
   633  }
   634  
   635  func expandCustomHeaders(s *schema.Set) *cloudfront.CustomHeaders {
   636  	qty := 0
   637  	items := []*cloudfront.OriginCustomHeader{}
   638  	for _, v := range s.List() {
   639  		items = append(items, expandOriginCustomHeader(v.(map[string]interface{})))
   640  		qty++
   641  	}
   642  	return &cloudfront.CustomHeaders{
   643  		Quantity: aws.Int64(int64(qty)),
   644  		Items:    items,
   645  	}
   646  }
   647  
   648  func flattenCustomHeaders(chs *cloudfront.CustomHeaders) *schema.Set {
   649  	s := []interface{}{}
   650  	for _, v := range chs.Items {
   651  		s = append(s, flattenOriginCustomHeader(v))
   652  	}
   653  	return schema.NewSet(originCustomHeaderHash, s)
   654  }
   655  
   656  func expandOriginCustomHeader(m map[string]interface{}) *cloudfront.OriginCustomHeader {
   657  	return &cloudfront.OriginCustomHeader{
   658  		HeaderName:  aws.String(m["name"].(string)),
   659  		HeaderValue: aws.String(m["value"].(string)),
   660  	}
   661  }
   662  
   663  func flattenOriginCustomHeader(och *cloudfront.OriginCustomHeader) map[string]interface{} {
   664  	return map[string]interface{}{
   665  		"name":  *och.HeaderName,
   666  		"value": *och.HeaderValue,
   667  	}
   668  }
   669  
   670  // Helper function used by originHash to get a composite hash for all
   671  // aws_cloudfront_distribution custom_header attributes.
   672  func customHeadersHash(s *schema.Set) int {
   673  	var buf bytes.Buffer
   674  	for _, v := range s.List() {
   675  		buf.WriteString(fmt.Sprintf("%d-", originCustomHeaderHash(v)))
   676  	}
   677  	return hashcode.String(buf.String())
   678  }
   679  
   680  // Assemble the hash for the aws_cloudfront_distribution custom_header
   681  // TypeSet attribute.
   682  func originCustomHeaderHash(v interface{}) int {
   683  	var buf bytes.Buffer
   684  	m := v.(map[string]interface{})
   685  	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
   686  	buf.WriteString(fmt.Sprintf("%s-", m["value"].(string)))
   687  	return hashcode.String(buf.String())
   688  }
   689  
   690  func expandCustomOriginConfig(m map[string]interface{}) *cloudfront.CustomOriginConfig {
   691  	return &cloudfront.CustomOriginConfig{
   692  		OriginProtocolPolicy: aws.String(m["origin_protocol_policy"].(string)),
   693  		HTTPPort:             aws.Int64(int64(m["http_port"].(int))),
   694  		HTTPSPort:            aws.Int64(int64(m["https_port"].(int))),
   695  		OriginSslProtocols:   expandCustomOriginConfigSSL(m["origin_ssl_protocols"].([]interface{})),
   696  	}
   697  }
   698  
   699  func flattenCustomOriginConfig(cor *cloudfront.CustomOriginConfig) map[string]interface{} {
   700  	return map[string]interface{}{
   701  		"origin_protocol_policy": *cor.OriginProtocolPolicy,
   702  		"http_port":              int(*cor.HTTPPort),
   703  		"https_port":             int(*cor.HTTPSPort),
   704  		"origin_ssl_protocols":   flattenCustomOriginConfigSSL(cor.OriginSslProtocols),
   705  	}
   706  }
   707  
   708  // Assemble the hash for the aws_cloudfront_distribution custom_origin_config
   709  // TypeSet attribute.
   710  func customOriginConfigHash(v interface{}) int {
   711  	var buf bytes.Buffer
   712  	m := v.(map[string]interface{})
   713  	buf.WriteString(fmt.Sprintf("%s-", m["origin_protocol_policy"].(string)))
   714  	buf.WriteString(fmt.Sprintf("%d-", m["http_port"].(int)))
   715  	buf.WriteString(fmt.Sprintf("%d-", m["https_port"].(int)))
   716  	for _, v := range sortInterfaceSlice(m["origin_ssl_protocols"].([]interface{})) {
   717  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   718  	}
   719  	return hashcode.String(buf.String())
   720  }
   721  
   722  func expandCustomOriginConfigSSL(s []interface{}) *cloudfront.OriginSslProtocols {
   723  	items := expandStringList(s)
   724  	return &cloudfront.OriginSslProtocols{
   725  		Quantity: aws.Int64(int64(len(items))),
   726  		Items:    items,
   727  	}
   728  }
   729  
   730  func flattenCustomOriginConfigSSL(osp *cloudfront.OriginSslProtocols) []interface{} {
   731  	return flattenStringList(osp.Items)
   732  }
   733  
   734  func expandS3OriginConfig(m map[string]interface{}) *cloudfront.S3OriginConfig {
   735  	return &cloudfront.S3OriginConfig{
   736  		OriginAccessIdentity: aws.String(m["origin_access_identity"].(string)),
   737  	}
   738  }
   739  
   740  func flattenS3OriginConfig(s3o *cloudfront.S3OriginConfig) map[string]interface{} {
   741  	return map[string]interface{}{
   742  		"origin_access_identity": *s3o.OriginAccessIdentity,
   743  	}
   744  }
   745  
   746  // Assemble the hash for the aws_cloudfront_distribution s3_origin_config
   747  // TypeSet attribute.
   748  func s3OriginConfigHash(v interface{}) int {
   749  	var buf bytes.Buffer
   750  	m := v.(map[string]interface{})
   751  	buf.WriteString(fmt.Sprintf("%s-", m["origin_access_identity"].(string)))
   752  	return hashcode.String(buf.String())
   753  }
   754  
   755  func expandCustomErrorResponses(s *schema.Set) *cloudfront.CustomErrorResponses {
   756  	qty := 0
   757  	items := []*cloudfront.CustomErrorResponse{}
   758  	for _, v := range s.List() {
   759  		items = append(items, expandCustomErrorResponse(v.(map[string]interface{})))
   760  		qty++
   761  	}
   762  	return &cloudfront.CustomErrorResponses{
   763  		Quantity: aws.Int64(int64(qty)),
   764  		Items:    items,
   765  	}
   766  }
   767  
   768  func flattenCustomErrorResponses(ers *cloudfront.CustomErrorResponses) *schema.Set {
   769  	s := []interface{}{}
   770  	for _, v := range ers.Items {
   771  		s = append(s, flattenCustomErrorResponse(v))
   772  	}
   773  	return schema.NewSet(customErrorResponseHash, s)
   774  }
   775  
   776  func expandCustomErrorResponse(m map[string]interface{}) *cloudfront.CustomErrorResponse {
   777  	er := cloudfront.CustomErrorResponse{
   778  		ErrorCode: aws.Int64(int64(m["error_code"].(int))),
   779  	}
   780  	if v, ok := m["error_caching_min_ttl"]; ok {
   781  		er.ErrorCachingMinTTL = aws.Int64(int64(v.(int)))
   782  	}
   783  	if v, ok := m["response_code"]; ok && v.(int) != 0 {
   784  		er.ResponseCode = aws.String(strconv.Itoa(v.(int)))
   785  	} else {
   786  		er.ResponseCode = aws.String("")
   787  	}
   788  	if v, ok := m["response_page_path"]; ok {
   789  		er.ResponsePagePath = aws.String(v.(string))
   790  	}
   791  
   792  	return &er
   793  }
   794  
   795  func flattenCustomErrorResponse(er *cloudfront.CustomErrorResponse) map[string]interface{} {
   796  	m := make(map[string]interface{})
   797  	m["error_code"] = int(*er.ErrorCode)
   798  	if er.ErrorCachingMinTTL != nil {
   799  		m["error_caching_min_ttl"] = int(*er.ErrorCachingMinTTL)
   800  	}
   801  	if er.ResponseCode != nil {
   802  		m["response_code"], _ = strconv.Atoi(*er.ResponseCode)
   803  	}
   804  	if er.ResponsePagePath != nil {
   805  		m["response_page_path"] = *er.ResponsePagePath
   806  	}
   807  	return m
   808  }
   809  
   810  // Assemble the hash for the aws_cloudfront_distribution custom_error_response
   811  // TypeSet attribute.
   812  func customErrorResponseHash(v interface{}) int {
   813  	var buf bytes.Buffer
   814  	m := v.(map[string]interface{})
   815  	buf.WriteString(fmt.Sprintf("%d-", m["error_code"].(int)))
   816  	if v, ok := m["error_caching_min_ttl"]; ok {
   817  		buf.WriteString(fmt.Sprintf("%d-", v.(int)))
   818  	}
   819  	if v, ok := m["response_code"]; ok {
   820  		buf.WriteString(fmt.Sprintf("%d-", v.(int)))
   821  	}
   822  	if v, ok := m["response_page_path"]; ok {
   823  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   824  	}
   825  	return hashcode.String(buf.String())
   826  }
   827  
   828  func expandLoggingConfig(m map[string]interface{}) *cloudfront.LoggingConfig {
   829  	var lc cloudfront.LoggingConfig
   830  	if m != nil {
   831  		lc.Prefix = aws.String(m["prefix"].(string))
   832  		lc.Bucket = aws.String(m["bucket"].(string))
   833  		lc.IncludeCookies = aws.Bool(m["include_cookies"].(bool))
   834  		lc.Enabled = aws.Bool(true)
   835  	} else {
   836  		lc.Prefix = aws.String("")
   837  		lc.Bucket = aws.String("")
   838  		lc.IncludeCookies = aws.Bool(false)
   839  		lc.Enabled = aws.Bool(false)
   840  	}
   841  	return &lc
   842  }
   843  
   844  func flattenLoggingConfig(lc *cloudfront.LoggingConfig) *schema.Set {
   845  	m := make(map[string]interface{})
   846  	m["prefix"] = *lc.Prefix
   847  	m["bucket"] = *lc.Bucket
   848  	m["include_cookies"] = *lc.IncludeCookies
   849  	return schema.NewSet(loggingConfigHash, []interface{}{m})
   850  }
   851  
   852  // Assemble the hash for the aws_cloudfront_distribution logging_config
   853  // TypeSet attribute.
   854  func loggingConfigHash(v interface{}) int {
   855  	var buf bytes.Buffer
   856  	m := v.(map[string]interface{})
   857  	buf.WriteString(fmt.Sprintf("%s-", m["prefix"].(string)))
   858  	buf.WriteString(fmt.Sprintf("%s-", m["bucket"].(string)))
   859  	buf.WriteString(fmt.Sprintf("%t-", m["include_cookies"].(bool)))
   860  	return hashcode.String(buf.String())
   861  }
   862  
   863  func expandAliases(as *schema.Set) *cloudfront.Aliases {
   864  	s := as.List()
   865  	var aliases cloudfront.Aliases
   866  	if len(s) > 0 {
   867  		aliases.Quantity = aws.Int64(int64(len(s)))
   868  		aliases.Items = expandStringList(s)
   869  	} else {
   870  		aliases.Quantity = aws.Int64(0)
   871  	}
   872  	return &aliases
   873  }
   874  
   875  func flattenAliases(aliases *cloudfront.Aliases) *schema.Set {
   876  	if aliases.Items != nil {
   877  		return schema.NewSet(aliasesHash, flattenStringList(aliases.Items))
   878  	}
   879  	return schema.NewSet(aliasesHash, []interface{}{})
   880  }
   881  
   882  // Assemble the hash for the aws_cloudfront_distribution aliases
   883  // TypeSet attribute.
   884  func aliasesHash(v interface{}) int {
   885  	return hashcode.String(v.(string))
   886  }
   887  
   888  func expandRestrictions(m map[string]interface{}) *cloudfront.Restrictions {
   889  	return &cloudfront.Restrictions{
   890  		GeoRestriction: expandGeoRestriction(m["geo_restriction"].(*schema.Set).List()[0].(map[string]interface{})),
   891  	}
   892  }
   893  
   894  func flattenRestrictions(r *cloudfront.Restrictions) *schema.Set {
   895  	m := make(map[string]interface{})
   896  	s := schema.NewSet(geoRestrictionHash, []interface{}{flattenGeoRestriction(r.GeoRestriction)})
   897  	m["geo_restriction"] = s
   898  	return schema.NewSet(restrictionsHash, []interface{}{m})
   899  }
   900  
   901  // Assemble the hash for the aws_cloudfront_distribution restrictions
   902  // TypeSet attribute.
   903  func restrictionsHash(v interface{}) int {
   904  	var buf bytes.Buffer
   905  	m := v.(map[string]interface{})
   906  	buf.WriteString(fmt.Sprintf("%d-", geoRestrictionHash(m["geo_restriction"].(*schema.Set).List()[0].(map[string]interface{}))))
   907  	return hashcode.String(buf.String())
   908  }
   909  
   910  func expandGeoRestriction(m map[string]interface{}) *cloudfront.GeoRestriction {
   911  	gr := cloudfront.GeoRestriction{
   912  		RestrictionType: aws.String(m["restriction_type"].(string)),
   913  	}
   914  	if v, ok := m["locations"]; ok {
   915  		gr.Quantity = aws.Int64(int64(len(v.([]interface{}))))
   916  		gr.Items = expandStringList(v.([]interface{}))
   917  		sort.Sort(StringPtrSlice(gr.Items))
   918  	} else {
   919  		gr.Quantity = aws.Int64(0)
   920  	}
   921  	return &gr
   922  }
   923  
   924  func flattenGeoRestriction(gr *cloudfront.GeoRestriction) map[string]interface{} {
   925  	m := make(map[string]interface{})
   926  
   927  	m["restriction_type"] = *gr.RestrictionType
   928  	if gr.Items != nil {
   929  		sort.Sort(StringPtrSlice(gr.Items))
   930  		m["locations"] = flattenStringList(gr.Items)
   931  	}
   932  	return m
   933  }
   934  
   935  // Assemble the hash for the aws_cloudfront_distribution geo_restriction
   936  // TypeSet attribute.
   937  func geoRestrictionHash(v interface{}) int {
   938  	var buf bytes.Buffer
   939  	m := v.(map[string]interface{})
   940  	// All keys added in alphabetical order.
   941  	buf.WriteString(fmt.Sprintf("%s-", m["restriction_type"].(string)))
   942  	if v, ok := m["locations"]; ok {
   943  		for _, w := range sortInterfaceSlice(v.([]interface{})) {
   944  			buf.WriteString(fmt.Sprintf("%s-", w.(string)))
   945  		}
   946  	}
   947  	return hashcode.String(buf.String())
   948  }
   949  
   950  func expandViewerCertificate(m map[string]interface{}) *cloudfront.ViewerCertificate {
   951  	var vc cloudfront.ViewerCertificate
   952  	if v, ok := m["iam_certificate_id"]; ok && v != "" {
   953  		vc.IAMCertificateId = aws.String(v.(string))
   954  		vc.SSLSupportMethod = aws.String(m["ssl_support_method"].(string))
   955  	} else if v, ok := m["acm_certificate_arn"]; ok && v != "" {
   956  		vc.ACMCertificateArn = aws.String(v.(string))
   957  		vc.SSLSupportMethod = aws.String(m["ssl_support_method"].(string))
   958  	} else {
   959  		vc.CloudFrontDefaultCertificate = aws.Bool(m["cloudfront_default_certificate"].(bool))
   960  	}
   961  	if v, ok := m["minimum_protocol_version"]; ok && v != "" {
   962  		vc.MinimumProtocolVersion = aws.String(v.(string))
   963  	}
   964  	return &vc
   965  }
   966  
   967  func flattenViewerCertificate(vc *cloudfront.ViewerCertificate) *schema.Set {
   968  	m := make(map[string]interface{})
   969  
   970  	if vc.IAMCertificateId != nil {
   971  		m["iam_certificate_id"] = *vc.IAMCertificateId
   972  		m["ssl_support_method"] = *vc.SSLSupportMethod
   973  	}
   974  	if vc.ACMCertificateArn != nil {
   975  		m["acm_certificate_arn"] = *vc.ACMCertificateArn
   976  		m["ssl_support_method"] = *vc.SSLSupportMethod
   977  	}
   978  	if vc.CloudFrontDefaultCertificate != nil {
   979  		m["cloudfront_default_certificate"] = *vc.CloudFrontDefaultCertificate
   980  	}
   981  	if vc.MinimumProtocolVersion != nil {
   982  		m["minimum_protocol_version"] = *vc.MinimumProtocolVersion
   983  	}
   984  	return schema.NewSet(viewerCertificateHash, []interface{}{m})
   985  }
   986  
   987  // Assemble the hash for the aws_cloudfront_distribution viewer_certificate
   988  // TypeSet attribute.
   989  func viewerCertificateHash(v interface{}) int {
   990  	var buf bytes.Buffer
   991  	m := v.(map[string]interface{})
   992  	if v, ok := m["iam_certificate_id"]; ok && v.(string) != "" {
   993  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   994  		buf.WriteString(fmt.Sprintf("%s-", m["ssl_support_method"].(string)))
   995  	} else if v, ok := m["acm_certificate_arn"]; ok && v.(string) != "" {
   996  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   997  		buf.WriteString(fmt.Sprintf("%s-", m["ssl_support_method"].(string)))
   998  	} else {
   999  		buf.WriteString(fmt.Sprintf("%t-", m["cloudfront_default_certificate"].(bool)))
  1000  	}
  1001  	if v, ok := m["minimum_protocol_version"]; ok && v.(string) != "" {
  1002  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
  1003  	}
  1004  	return hashcode.String(buf.String())
  1005  }
  1006  
  1007  // Do a top-level copy of struct fields from one struct to another. Used to
  1008  // copy fields between CacheBehavior and DefaultCacheBehavior structs.
  1009  func simpleCopyStruct(src, dst interface{}) {
  1010  	s := reflect.ValueOf(src).Elem()
  1011  	d := reflect.ValueOf(dst).Elem()
  1012  
  1013  	for i := 0; i < s.NumField(); i++ {
  1014  		if s.Field(i).CanSet() == true {
  1015  			if s.Field(i).Interface() != nil {
  1016  				for j := 0; j < d.NumField(); j++ {
  1017  					if d.Type().Field(j).Name == s.Type().Field(i).Name {
  1018  						d.Field(j).Set(s.Field(i))
  1019  					}
  1020  				}
  1021  			}
  1022  		}
  1023  	}
  1024  }
  1025  
  1026  // Convert *cloudfront.ActiveTrustedSigners to a flatmap.Map type, which ensures
  1027  // it can probably be inserted into the schema.TypeMap type used by the
  1028  // active_trusted_signers attribute.
  1029  func flattenActiveTrustedSigners(ats *cloudfront.ActiveTrustedSigners) flatmap.Map {
  1030  	m := make(map[string]interface{})
  1031  	s := []interface{}{}
  1032  	m["enabled"] = *ats.Enabled
  1033  
  1034  	for _, v := range ats.Items {
  1035  		signer := make(map[string]interface{})
  1036  		signer["aws_account_number"] = *v.AwsAccountNumber
  1037  		signer["key_pair_ids"] = aws.StringValueSlice(v.KeyPairIds.Items)
  1038  		s = append(s, signer)
  1039  	}
  1040  	m["items"] = s
  1041  	return flatmap.Flatten(m)
  1042  }