Module: CfnDsl::Specification

Defined in:
lib/cfndsl/specification.rb

Overview

Helper module for bridging the gap between a static types file included in the repo and dynamically generating the types directly from the AWS specification

Class Method Summary collapse

Class Method Details

.determine_spec_fileObject

rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity



94
95
96
97
98
# File 'lib/cfndsl/specification.rb', line 94

def self.determine_spec_file
  return CfnDsl.specification_file if File.exist? CfnDsl.specification_file

  File.expand_path('aws/resource_specification.json', __dir__)
end

.extract_from_resource_spec!Object



100
101
102
103
104
105
# File 'lib/cfndsl/specification.rb', line 100

def self.extract_from_resource_spec!
  spec_file = JSON.parse File.read(determine_spec_file)
  resources = extract_resources spec_file['ResourceTypes'].merge(Patches.resources)
  types = extract_types spec_file['PropertyTypes'].merge(Patches.types)
  { 'Resources' => resources, 'Types' => types }
end

.extract_resources(spec) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/cfndsl/specification.rb', line 8

def self.extract_resources(spec)
  spec.each_with_object({}) do |(resource_name, resource_info), resources|
    properties = resource_info['Properties'].each_with_object({}) do |(property_name, property_info), extracted|
      # some json incorrectly labelled as Type -> Json instead of PrimitiveType
      # also, AWS now has the concept of Map which cfndsl had never defined
      if property_info['Type'] == 'Map' || property_info['Type'] == 'Json'
        property_type = 'Json'
      elsif property_info['PrimitiveType']
        property_type = property_info['PrimitiveType']
      elsif property_info['PrimitiveItemType']
        property_type = Array(property_info['PrimitiveItemType'])
      elsif property_info['ItemType']
        # Tag is a reused type, but not quite primitive
        # and not all resources use the general form
        property_type = if property_info['ItemType'] == 'Tag'
                          ['Tag']
                        else
                          Array(resource_name.split('::').join + property_info['ItemType'])
                        end
      elsif property_info['Type']
        # Special types (defined below) are joined with their parent
        # resource name for uniqueness and connection
        property_type = resource_name.split('::').join + property_info['Type']
      else
        warn "could not extract type from #{resource_name}"
      end
      extracted[property_name] = property_type
      extracted
    end
    resources[resource_name] = { 'Properties' => properties }
    resources
  end
end

.extract_types(spec) ⇒ Object

rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/cfndsl/specification.rb', line 44

def self.extract_types(spec)
  primitive_types = {
    'String' => 'String',
    'Boolean' => 'Boolean',
    'Json' => 'Json',
    'Integer' => 'Integer',
    'Number' => 'Number',
    'Double' => 'Double',
    'Timestamp' => 'Timestamp',
    'Map' => 'Map',
    'Long' => 'Long'
  }
  spec.each_with_object(primitive_types) do |(property_name, property_info), types|
    # In order to name things uniquely and allow for connections
    # we extract the resource name from the property
    # AWS::IAM::User.Policy becomes AWSIAMUserPolicy
    root_resource = property_name.match(/(.*)\./)
    root_resource_name = root_resource ? root_resource[1].gsub(/::/, '') : property_name
    property_name = property_name.gsub(/::|\./, '')

    if property_info.key?('PrimitiveType')
      properties = property_info['PrimitiveType']
    elsif property_info.key?('Type')
      properties = property_info['Type']
    elsif property_info.key?('Properties')
      properties = property_info['Properties'].each_with_object({}) do |(nested_prop_name, nested_prop_info), extracted|
        if nested_prop_info['Type'] == 'Map' || nested_prop_info['Type'] == 'Json'
          # The Map type and the incorrectly labelled Json type
          nested_prop_type = 'Json'
        elsif nested_prop_info['PrimitiveType']
          nested_prop_type = nested_prop_info['PrimitiveType']
        elsif nested_prop_info['PrimitiveItemType']
          nested_prop_type = Array(nested_prop_info['PrimitiveItemType'])
        elsif nested_prop_info['ItemType']
          nested_prop_type = Array(root_resource_name + nested_prop_info['ItemType'])
        elsif nested_prop_info['Type']
          nested_prop_type = root_resource_name + nested_prop_info['Type']
        else
          warn "could not extract type from #{property_name}"
        end
        extracted[nested_prop_name] = nested_prop_type
        extracted
      end
    end
    types[property_name] = properties
    types
  end
end