humidifier 4.0.1 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ humidifier (4.2.0)
5
+ aws-sdk-cloudformation (~> 1.25)
6
+ aws-sdk-s3 (~> 1.48)
7
+ fast_underscore (~> 0.3)
8
+ nokogiri (~> 1.10)
9
+ thor (~> 1.0)
10
+ thor-hollaback (~> 0.2)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ ast (2.4.2)
16
+ aws-eventstream (1.2.0)
17
+ aws-partitions (1.592.0)
18
+ aws-sdk-cloudformation (1.69.0)
19
+ aws-sdk-core (~> 3, >= 3.127.0)
20
+ aws-sigv4 (~> 1.1)
21
+ aws-sdk-core (3.131.1)
22
+ aws-eventstream (~> 1, >= 1.0.2)
23
+ aws-partitions (~> 1, >= 1.525.0)
24
+ aws-sigv4 (~> 1.1)
25
+ jmespath (~> 1, >= 1.6.1)
26
+ aws-sdk-kms (1.56.0)
27
+ aws-sdk-core (~> 3, >= 3.127.0)
28
+ aws-sigv4 (~> 1.1)
29
+ aws-sdk-s3 (1.114.0)
30
+ aws-sdk-core (~> 3, >= 3.127.0)
31
+ aws-sdk-kms (~> 1)
32
+ aws-sigv4 (~> 1.4)
33
+ aws-sigv4 (1.5.0)
34
+ aws-eventstream (~> 1, >= 1.0.2)
35
+ docile (1.4.0)
36
+ fast_underscore (0.3.3)
37
+ hollaback (0.1.1)
38
+ jmespath (1.6.1)
39
+ minitest (5.15.0)
40
+ nokogiri (1.13.6-x86_64-darwin)
41
+ racc (~> 1.4)
42
+ parallel (1.22.1)
43
+ parser (3.1.2.0)
44
+ ast (~> 2.4.1)
45
+ racc (1.6.0)
46
+ rainbow (3.1.1)
47
+ rake (13.0.6)
48
+ regexp_parser (2.5.0)
49
+ rexml (3.2.5)
50
+ rubocop (1.30.1)
51
+ parallel (~> 1.10)
52
+ parser (>= 3.1.0.0)
53
+ rainbow (>= 2.2.2, < 4.0)
54
+ regexp_parser (>= 1.8, < 3.0)
55
+ rexml (>= 3.2.5, < 4.0)
56
+ rubocop-ast (>= 1.18.0, < 2.0)
57
+ ruby-progressbar (~> 1.7)
58
+ unicode-display_width (>= 1.4.0, < 3.0)
59
+ rubocop-ast (1.18.0)
60
+ parser (>= 3.1.1.0)
61
+ ruby-progressbar (1.11.0)
62
+ simplecov (0.21.2)
63
+ docile (~> 1.1)
64
+ simplecov-html (~> 0.11)
65
+ simplecov_json_formatter (~> 0.1)
66
+ simplecov-html (0.12.3)
67
+ simplecov_json_formatter (0.1.3)
68
+ thor (1.2.1)
69
+ thor-hollaback (0.2.1)
70
+ hollaback (~> 0.1)
71
+ thor (>= 0.19.1)
72
+ unicode-display_width (2.1.0)
73
+ webrick (1.7.0)
74
+ yard (0.9.28)
75
+ webrick (~> 1.7.0)
76
+
77
+ PLATFORMS
78
+ ruby
79
+
80
+ DEPENDENCIES
81
+ bundler (~> 2.0)
82
+ humidifier!
83
+ minitest (~> 5.13)
84
+ rake (~> 13.0)
85
+ rubocop (~> 1.24)
86
+ simplecov (~> 0.17)
87
+ yard (~> 0.9)
88
+
89
+ BUNDLED WITH
90
+ 2.2.3
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016-present Kevin Deisz
3
+ Copyright (c) 2016-present Kevin Newton
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Humidifier
2
2
 
3
- [![Build Status](https://travis-ci.com/kddeisz/humidifier.svg?branch=master)](https://travis-ci.com/kddeisz/humidifier)
4
- [![Gem Version](https://img.shields.io/gem/v/humidifier.svg?maxAge=3600)](https://rubygems.org/gems/humidifier)
3
+ [![Build Status](https://github.com/kddnewton/humidifier/workflows/Main/badge.svg)](https://github.com/kddnewton/humidifier/actions)
4
+ [![Gem Version](https://img.shields.io/gem/v/humidifier.svg)](https://rubygems.org/gems/humidifier)
5
5
 
6
6
  Humidifier is a ruby tool for managing [AWS CloudFormation](https://aws.amazon.com/cloudformation/) stacks. You can use it to build and manage stacks programmatically or you can use it as a command line tool to manage stacks through configuration files.
7
7
 
@@ -22,8 +22,10 @@ Humidifier is a ruby tool for managing [AWS CloudFormation](https://aws.amazon.c
22
22
  - [`deploy [?stack] [*parameters]`](#deploy-stack-parameters)
23
23
  - [`display [stack] [?pattern]`](#display-stack-pattern)
24
24
  - [`stacks`](#stacks)
25
+ - [`upgrade`](#upgrade)
25
26
  - [`upload [?stack]`](#upload-stack)
26
27
  - [`validate [?stack]`](#validate-stack)
28
+ - [`version`](#version)
27
29
  - [Parameters](#parameters)
28
30
  - [Shortcuts](#shortcuts)
29
31
  - [Automatic id properties](#automatic-id-properties)
@@ -190,7 +192,7 @@ AdminUser:
190
192
  - Administration
191
193
  ```
192
194
 
193
- The top-level keys are the logical resource names that will be displayed in the CloudFormation screen. They point to a map of key/value pairs that will be passed on to `humidifier`. Any `humidifier` (and therefore any CloudFormation) attribute may be specified. For more information on CloudFormation templates and which attributes may be specified, see both the [`humidifier` docs](http://kddeisz.github.io/humidifier) and the [CloudFormation docs](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html).
195
+ The top-level keys are the logical resource names that will be displayed in the CloudFormation screen. They point to a map of key/value pairs that will be passed on to `humidifier`. Any `humidifier` (and therefore any CloudFormation) attribute may be specified. For more information on CloudFormation templates and which attributes may be specified, see both the [`humidifier` docs](http://kddnewton.github.io/humidifier) and the [CloudFormation docs](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html).
194
196
 
195
197
  ### Mappers
196
198
 
@@ -259,6 +261,10 @@ Displays the specified stack in JSON format on the command line. If you optional
259
261
 
260
262
  Displays the names of all of the stacks that `humidifier` is managing.
261
263
 
264
+ #### `upgrade`
265
+
266
+ Downloads the latest CloudFormation resource specification. Periodically AWS will update the file that `humidifier` is based on, in which case the attributes of the resources that were changed could change. This gem usually stays relatively in sync, but if you need to use the latest specs and this gem has not yet released a new version containing them, then you can run this command to download the latest specs onto your system.
267
+
262
268
  #### `upload [?stack]`
263
269
 
264
270
  Upload one or all stacks in the repo to S3 for reference later. Note that this must be combined with the `humidifier` `s3_bucket` configuration option.
@@ -267,6 +273,10 @@ Upload one or all stacks in the repo to S3 for reference later. Note that this m
267
273
 
268
274
  Validate that one or all stacks in the repo are properly configured and using values that CloudFormation understands.
269
275
 
276
+ #### `version`
277
+
278
+ Output the version of `Humidifier` as well as the version of the CloudFormation resource specification that you are using.
279
+
270
280
  ### Parameters
271
281
 
272
282
  CloudFormation template parameters can be specified by having a special `parameters.yml` file in your stack directory. This file should contain a YAML-encoded object whose keys are the names of the parameters and whose values are the parameter configuration (using the same underscore paradigm as `humidifier` resources for specifying configuration).
@@ -397,7 +407,7 @@ The specs pulled from the CFN docs is saved to `CloudFormationResourceSpecificat
397
407
 
398
408
  ### Contributing
399
409
 
400
- Bug reports and pull requests are welcome on GitHub at https://github.com/kddeisz/humidifier.
410
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kddnewton/humidifier.
401
411
 
402
412
  ### License
403
413
 
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "fileutils"
5
+ require "rake/testtask"
6
+ require "yard"
7
+
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << "test"
10
+ t.libs << "lib"
11
+ t.test_files = FileList["test/**/*_test.rb"]
12
+ t.warning = false
13
+ end
14
+
15
+ task default: :test
16
+
17
+ YARD::Rake::YardocTask.new(:yard) do |t|
18
+ filepath = File.join("lib", "humidifier", "magic.rb")
19
+
20
+ t.stats_options = ["--list-undoc"]
21
+ t.before = lambda do
22
+ require "humidifier"
23
+ require_relative "yard/dynamic"
24
+ Dynamic.generate(filepath)
25
+ end
26
+ t.after = -> { FileUtils.rm(filepath) }
27
+ end
data/exe/humidifier ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # If there is a `bin/humidifier` file, then swap out execution of this default
5
+ # CLI with the custom CLI instead.
6
+ if File.file?(File.join("bin", "humidifier"))
7
+ exec(File.join("bin", "humidifier"))
8
+ end
9
+
10
+ require "bundler/setup"
11
+
12
+ $LOAD_PATH.unshift File.expand_path(File.join("..", "lib"), __dir__)
13
+ require "humidifier"
14
+
15
+ Humidifier::CLI.start(ARGV)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/humidifier/version"
4
+
5
+ version = Humidifier::VERSION
6
+ repository = "https://github.com/kddnewton/humidifier"
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "humidifier"
10
+ spec.version = version
11
+ spec.authors = ["Kevin Newton"]
12
+ spec.email = ["kddnewton@gmail.com"]
13
+
14
+ spec.summary = "CloudFormation made easy"
15
+ spec.description = "Programmatically generate and manage AWS " \
16
+ "CloudFormation templates, stacks, and change sets."
17
+ spec.homepage = repository
18
+ spec.license = "MIT"
19
+
20
+ spec.metadata = {
21
+ "bug_tracker_uri" => "#{repository}/issues",
22
+ "changelog_uri" => "#{repository}/blob/v#{version}/CHANGELOG.md",
23
+ "source_code_uri" => repository,
24
+ "rubygems_mfa_required" => "true"
25
+ }
26
+
27
+ spec.files = Dir.chdir(__dir__) do
28
+ `git ls-files -z`.split("\x0").reject do |f|
29
+ f.match(%r{^(bin|docs|example|test|yard)/})
30
+ end
31
+ end
32
+
33
+ spec.bindir = "exe"
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ["lib"]
36
+
37
+ spec.add_dependency "aws-sdk-cloudformation", "~> 1.25"
38
+ spec.add_dependency "aws-sdk-s3", "~> 1.48"
39
+ spec.add_dependency "fast_underscore", "~> 0.3"
40
+ spec.add_dependency "nokogiri", "~> 1.10"
41
+ spec.add_dependency "thor", "~> 1.0"
42
+ spec.add_dependency "thor-hollaback", "~> 0.2"
43
+
44
+ spec.add_development_dependency "bundler", "~> 2.0"
45
+ spec.add_development_dependency "minitest", "~> 5.13"
46
+ spec.add_development_dependency "rake", "~> 13.0"
47
+ spec.add_development_dependency "rubocop", "~> 1.24"
48
+ spec.add_development_dependency "simplecov", "~> 0.17"
49
+ spec.add_development_dependency "yard", "~> 0.9"
50
+ end
@@ -4,13 +4,13 @@ module Humidifier
4
4
  # A CLI for running commands to manipulate the stacks that Humidifier knows
5
5
  # about.
6
6
  class CLI < Thor
7
- class_option :aws_profile, desc: 'The AWS profile to authenticate with',
8
- aliases: ['-p']
7
+ class_option :aws_profile, desc: "The AWS profile to authenticate with",
8
+ aliases: ["-p"]
9
9
 
10
- class_option :debug, desc: 'Sets up debug mode', aliases: ['-d']
10
+ class_option :debug, desc: "Sets up debug mode", aliases: ["-d"]
11
11
  class_around :safe_execute
12
12
 
13
- desc 'change [?stack]', 'Create changesets for one or all stacks'
13
+ desc "change [?stack]", "Create changesets for one or all stacks"
14
14
  def change(name = nil)
15
15
  authorize
16
16
 
@@ -22,10 +22,10 @@ module Humidifier
22
22
  end
23
23
  end
24
24
 
25
- desc 'deploy [?stack] [*parameters]', 'Update one or all stacks'
26
- option :wait, desc: 'Wait for the stack to create/update',
25
+ desc "deploy [?stack] [*parameters]", "Update one or all stacks"
26
+ option :wait, desc: "Wait for the stack to create/update",
27
27
  type: :boolean, default: false
28
- option :prefix, desc: 'The prefix to use for the stack'
28
+ option :prefix, desc: "The prefix to use for the stack"
29
29
  def deploy(name = nil, *parameters)
30
30
  authorize
31
31
 
@@ -37,8 +37,8 @@ module Humidifier
37
37
  end
38
38
  end
39
39
 
40
- desc 'display [stack] [?pattern]',
41
- 'Display the CloudFormation JSON for a given stack'
40
+ desc "display [stack] [?pattern]",
41
+ "Display the CloudFormation JSON for a given stack"
42
42
  def display(name, pattern = nil)
43
43
  directory = Directory.new(name, pattern: pattern && /#{pattern}/i)
44
44
 
@@ -46,13 +46,21 @@ module Humidifier
46
46
  puts directory.to_cf
47
47
  end
48
48
 
49
- desc 'stacks', 'List the stacks known to Humidifier'
49
+ desc "stacks", "List the stacks known to Humidifier"
50
50
  def stacks
51
- puts '🗒 Listing stacks'
51
+ puts "🗒 Listing stacks"
52
52
  puts Humidifier.config.stack_names.sort.map { |name| "- #{name}" }
53
53
  end
54
54
 
55
- desc 'upload [?stack]', 'Upload one or all stacks to S3'
55
+ desc "upgrade", "Download the latest CloudFormation resource specification"
56
+ def upgrade
57
+ print "💾 Downloading..."
58
+
59
+ version = Upgrade.perform
60
+ puts " upgraded to v#{version}"
61
+ end
62
+
63
+ desc "upload [?stack]", "Upload one or all stacks to S3"
56
64
  def upload(name = nil)
57
65
  authorize
58
66
 
@@ -64,22 +72,30 @@ module Humidifier
64
72
  end
65
73
  end
66
74
 
67
- desc 'validate [?stack]',
68
- 'Validate that one or all stacks are valid with CloudFormation'
75
+ desc "validate [?stack]",
76
+ "Validate that one or all stacks are valid with CloudFormation"
69
77
  def validate(name = nil)
70
78
  authorize
71
79
 
72
- print '🔍 Validating... '
80
+ print "🔍 Validating... "
73
81
 
74
82
  valid =
75
83
  stack_names_from(name).all? do |stack_name|
76
84
  Directory.new(stack_name).valid?
77
85
  end
78
86
 
79
- puts valid ? 'Valid.' : 'Invalid.'
87
+ puts valid ? "Valid." : "Invalid."
88
+ end
89
+
90
+ desc "version", "Display the version of Humidifier"
91
+ def version
92
+ filepath = File.expand_path("../../#{SPECIFICATION}", __dir__)
93
+ version = JSON.parse(File.read(filepath))["ResourceSpecificationVersion"]
94
+
95
+ puts "📦 CloudFormation specification v#{version}"
80
96
  end
81
97
 
82
- no_commands do # rubocop:disable Metrics/BlockLength
98
+ no_commands do
83
99
  def authorize
84
100
  return unless options[:aws_profile]
85
101
 
@@ -89,14 +105,14 @@ module Humidifier
89
105
 
90
106
  def parameters_from(opts)
91
107
  opts.map do |opt|
92
- key, value = opt.split('=')
108
+ key, value = opt.split("=")
93
109
  { parameter_key: key, parameter_value: value }
94
110
  end
95
111
  end
96
112
 
97
113
  def prelude
98
114
  command = @_invocations.values.dig(0, 0)
99
- command = command ? "#{command} " : ''
115
+ command = command ? "#{command} " : ""
100
116
  puts "\033[1mhumidifier #{command}v#{VERSION}\033[0m"
101
117
  end
102
118
 
@@ -110,7 +126,7 @@ module Humidifier
110
126
  puts error.message
111
127
  exit 1
112
128
  else
113
- puts '✨ Done in %.2fs.' % (Time.now.to_f - start)
129
+ puts "✨ Done in %.2fs." % (Time.now.to_f - start)
114
130
  end
115
131
 
116
132
  def stack_names_from(name)
@@ -83,7 +83,7 @@ module Humidifier
83
83
  end
84
84
  end
85
85
 
86
- def mapped_from(clazz, key, value) # rubocop:disable Metrics/MethodLength
86
+ def mapped_from(clazz, key, value)
87
87
  if self.class.attribute_methods.include?(key.to_sym)
88
88
  # The given attribute name has been defined using the `::attribute`
89
89
  # DSL method, so send the given value to that method and return the
@@ -10,7 +10,7 @@ module Humidifier
10
10
  raise Error, "Invalid resource: #{opts[:to].inspect}" if @clazz.nil?
11
11
 
12
12
  if opts[:using] && block_given?
13
- raise Error, 'Cannot specify :using and provide an anonymous mapper'
13
+ raise Error, "Cannot specify :using and provide an anonymous mapper"
14
14
  end
15
15
 
16
16
  @mapper = mapper_from(opts, &block)
@@ -33,7 +33,7 @@ module Humidifier
33
33
  end
34
34
 
35
35
  def normalized(name)
36
- name.start_with?('AWS') ? name : "AWS::#{name}"
36
+ name.start_with?("AWS") ? name : "AWS::#{name}"
37
37
  end
38
38
  end
39
39
  end
@@ -26,7 +26,7 @@ module Humidifier
26
26
 
27
27
  def initialize
28
28
  @mappings = {}
29
- @stack_path = '.'
29
+ @stack_path = "."
30
30
  end
31
31
 
32
32
  def files_for(name)
@@ -82,7 +82,7 @@ module Humidifier
82
82
  begin
83
83
  parameter_filepath =
84
84
  Humidifier.config.files_for(name).detect do |filepath|
85
- File.basename(filepath, '.yml') == 'parameters'
85
+ File.basename(filepath, ".yml") == "parameters"
86
86
  end
87
87
 
88
88
  parameter_filepath ? parameters_from(parameter_filepath) : {}
@@ -94,7 +94,7 @@ module Humidifier
94
94
  return {} unless loaded
95
95
 
96
96
  loaded.each_with_object({}) do |(name, opts), params|
97
- opts = opts.map { |key, value| [key.to_sym, value] }.to_h
97
+ opts = opts.to_h { |key, value| [key.to_sym, value] }
98
98
  params[name] = Parameter.new(opts)
99
99
  end
100
100
  end
@@ -109,7 +109,7 @@ module Humidifier
109
109
  loaded.each_with_object({}) do |(name, attributes), resources|
110
110
  next if pattern && name !~ pattern
111
111
 
112
- attribute = attributes.delete('export')
112
+ attribute = attributes.delete("export")
113
113
  exports << Export.new(name, attribute) if attribute
114
114
 
115
115
  resources[name] = mapping.resource_for(name, attributes)
@@ -120,10 +120,10 @@ module Humidifier
120
120
  filepaths = Humidifier.config.files_for(name)
121
121
 
122
122
  filepaths.each_with_object({}) do |filepath, resources|
123
- basename = File.basename(filepath, '.yml')
123
+ basename = File.basename(filepath, ".yml")
124
124
 
125
125
  # Explicitly skip past parameters so we can pull them out later
126
- next if basename == 'parameters'
126
+ next if basename == "parameters"
127
127
 
128
128
  resources.merge!(parse(filepath, basename))
129
129
  end
@@ -4,38 +4,58 @@ module Humidifier
4
4
  # Reads the specs/CloudFormationResourceSpecification.json file and load each
5
5
  # resource as a class
6
6
  module Loader
7
- # Handles searching the PropertyTypes specifications for a specific
8
- # resource type
9
- class PropertyTypes
10
- attr_reader :structs
7
+ class Compiler
8
+ attr_reader :specification, :property_types
11
9
 
12
- def initialize(structs)
13
- @structs = structs
14
- end
10
+ def initialize(specification)
11
+ @specification = specification
15
12
 
16
- # find the substructures necessary for the given resource key
17
- def search(key)
18
- results = structs.keys.grep(/#{key}/)
19
- shortened_names = results.map { |result| result.gsub("#{key}.", '') }
20
- shortened_names.zip(structs.values_at(*results)).to_h.merge(global)
13
+ # Set an initial value for each property types so that we can handle
14
+ # cycles in the specification
15
+ @property_types = specification["PropertyTypes"].to_h do |name, _|
16
+ [name, {}]
17
+ end
21
18
  end
22
19
 
23
- private
20
+ def compile
21
+ # Loop through every property type that's already defined and build up
22
+ # each of the properties into the list
23
+ property_types.each do |property_type_name, property_type|
24
+ prefix = property_type_name.split(".", 2).first
24
25
 
25
- def global
26
- @global ||= structs.reject { |key, _| key.match(/AWS/) }
27
- end
28
- end
26
+ subspec = specification["PropertyTypes"].fetch(property_type_name)
27
+ subspec.fetch("Properties") { {} }.each do |property_name, property|
28
+ property = build_property(prefix, property_name, property)
29
+ property_type[property.name] = property if property
30
+ end
31
+ end
32
+
33
+ # Loop through every resource type in the specification and define a
34
+ # class for each one dynamically.
35
+ specification["ResourceTypes"].each do |aws_name, resource_type|
36
+ _top, group, resource = aws_name.split("::")
37
+
38
+ properties = {}
39
+ resource_type["Properties"].each do |property_name, property|
40
+ property = build_property(aws_name, property_name, property)
41
+ properties[property.name] = property if property
42
+ end
29
43
 
30
- class << self
31
- # loop through the specs and register each class
32
- def load
33
- parsed = parse_spec
34
- types = PropertyTypes.new(parsed['PropertyTypes'])
44
+ resource_class =
45
+ Class.new(Resource) do
46
+ self.aws_name = aws_name
47
+ self.props = properties
48
+ end
35
49
 
36
- parsed['ResourceTypes'].each do |key, spec|
37
- match = key.match(/\A(\w+)::(\w+)::(\w+)\z/)
38
- register(match[1], match[2], match[3], spec, types.search(key))
50
+ group_module =
51
+ if Humidifier.const_defined?(group)
52
+ Humidifier.const_get(group)
53
+ else
54
+ Humidifier.const_set(group, Module.new)
55
+ end
56
+
57
+ Humidifier.registry[aws_name] =
58
+ group_module.const_set(resource, resource_class)
39
59
  end
40
60
 
41
61
  Humidifier.registry.freeze
@@ -43,35 +63,72 @@ module Humidifier
43
63
 
44
64
  private
45
65
 
46
- def build_class(aws_name, spec, substructs)
47
- Class.new(Resource) do
48
- self.aws_name = aws_name
49
- self.props =
50
- spec['Properties'].map do |(key, config)|
51
- prop = Props.from(key, config, substructs)
52
- [prop.name, prop]
53
- end.to_h
66
+ def build_primitive(type, name, spec)
67
+ case type
68
+ in "Boolean"
69
+ Props::BooleanProp.new(name, spec)
70
+ in "Double"
71
+ Props::DoubleProp.new(name, spec)
72
+ in "Integer" | "Long"
73
+ Props::IntegerProp.new(name, spec)
74
+ in "Json"
75
+ Props::JsonProp.new(name, spec)
76
+ in "String"
77
+ Props::StringProp.new(name, spec)
78
+ in "Timestamp"
79
+ Props::TimestampProp.new(name, spec)
54
80
  end
55
81
  end
56
82
 
57
- def parse_spec
58
- relative =
59
- File.join('..', '..', 'CloudFormationResourceSpecification.json')
60
-
61
- JSON.parse(File.read(File.expand_path(relative, __dir__)))
83
+ def build_property(prefix, name, spec)
84
+ case spec.transform_keys(&:to_sym)
85
+ in { PrimitiveType: type }
86
+ build_primitive(type, name, spec)
87
+ in { Type: "List", PrimitiveItemType: type }
88
+ Props::ListProp.new(name, spec, build_primitive(type, name, spec))
89
+ in { Type: "Map", PrimitiveItemType: type }
90
+ Props::MapProp.new(name, spec, build_primitive(type, name, spec))
91
+ in { Type: "List", ItemType: "List" }
92
+ # specifically calling this out since
93
+ # AWS::Rekognition::StreamProcessor.PolygonRegionsOfInterest has a
94
+ # nested list structure that otherwise breaks the compiler
95
+ Props::ListProp.new(name, spec, Props::ListProp.new(name, spec))
96
+ in { Type: "List", ItemType: item_type }
97
+ Props::ListProp.new(
98
+ name,
99
+ spec,
100
+ Props::StructureProp.new(name, spec,
101
+ property_type(prefix, item_type))
102
+ )
103
+ in { Type: "Map", ItemType: item_type }
104
+ Props::MapProp.new(
105
+ name,
106
+ spec,
107
+ Props::StructureProp.new(name, spec,
108
+ property_type(prefix, item_type))
109
+ )
110
+ in { Type: type }
111
+ Props::StructureProp.new(name, spec, property_type(prefix, type))
112
+ else # rubocop:disable Layout/IndentationWidth
113
+ # It's possible to hit this clause if the specification has a property
114
+ # that is not currently supported by CloudFormation. In this case,
115
+ # we're not going to create a property at all for it.
116
+ end
62
117
  end
63
118
 
64
- def register(top, group, resource, spec, substructs)
65
- aws_name = "#{top}::#{group}::#{resource}"
66
- resource_class = build_class(aws_name, spec, substructs)
67
-
68
- unless Humidifier.const_defined?(group)
69
- Humidifier.const_set(group, Module.new)
119
+ def property_type(prefix, type)
120
+ property_types.fetch("#{prefix}.#{type}") do
121
+ property_types.fetch(type)
70
122
  end
71
-
72
- Humidifier.const_get(group).const_set(resource, resource_class)
73
- Humidifier.registry[aws_name] = resource_class
74
123
  end
75
124
  end
125
+
126
+ # loop through the specs and register each class
127
+ def self.load
128
+ filepath = File.expand_path("../../#{SPECIFICATION}", __dir__)
129
+ return unless File.file?(filepath)
130
+
131
+ Compiler.new(JSON.parse(File.read(filepath))).compile
132
+ end
76
133
  end
77
134
  end
@@ -11,9 +11,9 @@ module Humidifier
11
11
  end
12
12
 
13
13
  def to_cf
14
- { 'Value' => Serializer.dump(value) }.tap do |cf|
15
- cf['Description'] = description if description
16
- cf['Export'] = { 'Name' => export_name } if export_name
14
+ { "Value" => Serializer.dump(value) }.tap do |cf|
15
+ cf["Description"] = description if description
16
+ cf["Export"] = { "Name" => export_name } if export_name
17
17
  end
18
18
  end
19
19
  end