Class: Jets::Resource::ApiGateway::RestApi::Routes::Change::Base

Inherits:
Object
  • Object
show all
Extended by:
Memoist
Includes:
AwsServices
Defined in:
lib/jets/resource/api_gateway/rest_api/routes/change/base.rb

Direct Known Subclasses

MediaTypes, Page, Variable

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AwsServices

#apigateway, #aws_lambda, #cfn, #dynamodb, #logs, #s3, #s3_resource, #sns, #sqs, #sts

Methods included from AwsServices::StackStatus

#lookup, #stack_exists?, #stack_in_progress?

Methods included from AwsServices::GlobalMemoist

included

Class Method Details

.changed?Boolean

Returns:

  • (Boolean)


6
7
8
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 6

def self.changed?
  new.changed?
end

Instance Method Details

#controller_action_from_api(function_arn) ⇒ Object

TODO: If this hits the Lambda Rate limit, then list_functions also contains the Lambda function description. So we can paginate through list_functions results and store description from there if needed. Dont think this will be needed though because controller_action_from_string gets called most of the time. Also, user might be able to request their Lambda limit to be increased.



110
111
112
113
114
115
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 110

def controller_action_from_api(function_arn)
  desc = lambda_function_description(function_arn)
  controller, action = desc.split('#')
  controller = controller.underscore.sub(/_controller$/,'')
  [controller, action]
end

#controller_action_from_string(function_arn) ⇒ Object



117
118
119
120
121
122
123
124
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 117

def controller_action_from_string(function_arn)
  controller_action = function_arn.sub("#{Jets.project_namespace}-", '')
  md = controller_action.match(/(.*)_controller-(.*)/)
  controller = md[1]
  controller = controller.gsub('-','/')
  action = md[2]
  [controller, action]
end

#deployed_routesObject

Build up deployed routes from the existing CloudFormation resources.



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
41
42
43
44
45
46
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 11

def deployed_routes
  routes = []

  resources, position = [], true
  while position
    position = nil if position == true # start of loop
    resp = apigateway.get_resources(
      rest_api_id: rest_api_id,
      position: position,
      limit: 500, # default: 25 max: 500
    )
    resources += resp.items
    position = resp.position
  end

  resources.each do |resource|
    resource_methods = resource.resource_methods
    next if resource_methods.nil?

    resource_methods.each do |http_verb, resource_method|
      # puts "#{http_verb} #{resource.path} | resource.id #{resource.id}"
      # puts to(resource.id, http_verb)

      # Test changing config.cors and CloudFormation does an in-place update
      # on the resource. So no need to do bluegreen deployments for OPTIONS.
      next if http_verb == "OPTIONS"

      path = recreate_path(resource.path)
      method = http_verb.downcase.to_sym
      to = to(resource.id, http_verb)
      route = Jets::Router::Route.new(path: path, method: method, to: to)
      routes << route
    end
  end
  routes
end

#get_controller_action(function_arn) ⇒ Object



97
98
99
100
101
102
103
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 97

def get_controller_action(function_arn)
  if function_arn.include?('_controller-')
    controller_action_from_string(function_arn)
  else
    controller_action_from_api(function_arn)
  end
end

#lambda_function_description(function_arn) ⇒ Object



126
127
128
129
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 126

def lambda_function_description(function_arn)
  resp = aws_lambda.get_function(function_name: function_arn)
  resp.configuration.description # contains full info: PostsController#index
end

#method_uri(resource_id, http_method) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 60

def method_uri(resource_id, http_method)
  # https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html
  retries = 0
  begin
    resp = apigateway.get_method(
      rest_api_id: rest_api_id,
      resource_id: resource_id,
      http_method: http_method
    )
  rescue Aws::APIGateway::Errors::TooManyRequestsException => e
    retries += 1
    seconds = 2 ** retries

    puts "WARN: method_uri #{e.class} #{e.message}".color(:yellow)
    puts "Backing off and will retry in #{seconds} seconds."
    sleep(seconds)
    if seconds > 90 # 2 ** 6 is 64 so will give up after 6 retries
      puts "Giving up after #{retries} retries"
    else
      retry
    end
  end
  resp.method_integration.uri
end

#new_routesObject



146
147
148
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 146

def new_routes
  Jets::Router.routes
end

#recreate_path(path) ⇒ Object



49
50
51
52
53
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 49

def recreate_path(path)
  path = path.gsub(%r{^/},'')
  path = path.gsub(/{([^}]*)\+}/, '*\1')
  path.gsub(/{([^}]*)}/, ':\1')
end

#recreate_to(method_uri) ⇒ Object

Parses method uri and recreates a Route to argument. So:

"arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:112233445566:function:demo-test-posts_controller-new/invocations"

Returns:

posts#new


90
91
92
93
94
95
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 90

def recreate_to(method_uri)
  md = method_uri.match(/function:(.*)\//)
  function_arn = md[1] # IE: demo-dev-posts_controller-new
  controller, action = get_controller_action(function_arn)
  "#{controller}##{action}" # IE: posts#new
end

#rest_api_idObject

Duplicated in rest_api/change_detection.rb, base_path/role.rb, rest_api/routes.rb



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 132

def rest_api_id
  stack_name = Jets::Naming.parent_stack_name
  return "RestApi" unless stack_exists?(stack_name)

  stack = cfn.describe_stacks(stack_name: stack_name).stacks.first

  api_gateway_stack_arn = lookup(stack[:outputs], "ApiGateway")

  # resources = cfn.describe_stack_resources(stack_name: api_gateway_stack_arn).stack_resources
  stack = cfn.describe_stacks(stack_name: api_gateway_stack_arn).stacks.first
  lookup(stack[:outputs], "RestApi") # rest_api_id
end

#to(resource_id, http_method) ⇒ Object



55
56
57
58
# File 'lib/jets/resource/api_gateway/rest_api/routes/change/base.rb', line 55

def to(resource_id, http_method)
  uri = method_uri(resource_id, http_method)
  recreate_to(uri) unless uri.nil?
end