api_gateway_dsl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/.simplecov +2 -0
- data/.travis.yml +2 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +4 -0
- data/api_gateway_dsl.gemspec +30 -0
- data/bin/api_gateway_dsl +32 -0
- data/lib/api_gateway_dsl.rb +26 -0
- data/lib/api_gateway_dsl/context.rb +13 -0
- data/lib/api_gateway_dsl/document.rb +41 -0
- data/lib/api_gateway_dsl/dsl/document_node.rb +46 -0
- data/lib/api_gateway_dsl/dsl/integration_node.rb +32 -0
- data/lib/api_gateway_dsl/dsl/operation_node.rb +66 -0
- data/lib/api_gateway_dsl/dsl/response_node.rb +24 -0
- data/lib/api_gateway_dsl/integration.rb +47 -0
- data/lib/api_gateway_dsl/integration/collection.rb +7 -0
- data/lib/api_gateway_dsl/integration/http.rb +26 -0
- data/lib/api_gateway_dsl/integration/http_proxy.rb +29 -0
- data/lib/api_gateway_dsl/integration/lambda.rb +38 -0
- data/lib/api_gateway_dsl/integration/mock.rb +33 -0
- data/lib/api_gateway_dsl/mapping.rb +68 -0
- data/lib/api_gateway_dsl/mapping/collection.rb +15 -0
- data/lib/api_gateway_dsl/operation.rb +70 -0
- data/lib/api_gateway_dsl/operation/collection.rb +62 -0
- data/lib/api_gateway_dsl/parameter.rb +24 -0
- data/lib/api_gateway_dsl/parameter/body.rb +21 -0
- data/lib/api_gateway_dsl/parameter/collection.rb +7 -0
- data/lib/api_gateway_dsl/parameter/header.rb +13 -0
- data/lib/api_gateway_dsl/parameter/path.rb +14 -0
- data/lib/api_gateway_dsl/parameter/query.rb +13 -0
- data/lib/api_gateway_dsl/parameter/simple.rb +23 -0
- data/lib/api_gateway_dsl/response.rb +53 -0
- data/lib/api_gateway_dsl/response/collection.rb +19 -0
- data/lib/api_gateway_dsl/response_header.rb +19 -0
- data/lib/api_gateway_dsl/response_header/collection.rb +11 -0
- data/lib/api_gateway_dsl/response_integration.rb +30 -0
- data/lib/api_gateway_dsl/response_integration/collection.rb +11 -0
- data/lib/api_gateway_dsl/template.rb +48 -0
- data/lib/api_gateway_dsl/template/collection.rb +32 -0
- data/lib/api_gateway_dsl/version.rb +5 -0
- data/spec/api_gateway_dsl/document_spec.rb +20 -0
- data/spec/fixtures/greedy_http_proxy/README.md +69 -0
- data/spec/fixtures/greedy_http_proxy/index.rb +13 -0
- data/spec/fixtures/greedy_http_proxy/index.yml +35 -0
- data/spec/fixtures/greedy_http_proxy/pets/proxy.rb +9 -0
- data/spec/fixtures/http_get/README.md +150 -0
- data/spec/fixtures/http_get/index.rb +17 -0
- data/spec/fixtures/http_get/index.yml +74 -0
- data/spec/fixtures/http_get/pets/get.rb +15 -0
- data/spec/fixtures/http_get/pets/response/200.vtl +1 -0
- data/spec/fixtures/http_get/pets/response/200.yml +4 -0
- data/spec/fixtures/http_get/pets/response/500.vtl +3 -0
- data/spec/fixtures/http_get/pets/response/500.yml +4 -0
- data/spec/fixtures/lambda_post/README.md +185 -0
- data/spec/fixtures/lambda_post/index.rb +18 -0
- data/spec/fixtures/lambda_post/index.yml +89 -0
- data/spec/fixtures/lambda_post/pets/post.rb +11 -0
- data/spec/fixtures/lambda_post/pets/request/body.vtl +9 -0
- data/spec/fixtures/lambda_post/pets/request/body.yml +7 -0
- data/spec/fixtures/lambda_post/pets/response/201.vtl +1 -0
- data/spec/fixtures/lambda_post/pets/response/201.yml +1 -0
- data/spec/fixtures/lambda_post/pets/response/500.vtl +3 -0
- data/spec/fixtures/lambda_post/pets/response/500.yml +4 -0
- data/spec/fixtures/lambda_post_with_cors/README.md +193 -0
- data/spec/fixtures/lambda_post_with_cors/index.rb +9 -0
- data/spec/fixtures/lambda_post_with_cors/index.yml +106 -0
- data/spec/fixtures/lambda_post_with_cors/pets/post.rb +11 -0
- data/spec/fixtures/lambda_post_with_cors/pets/request/body.vtl +9 -0
- data/spec/fixtures/lambda_post_with_cors/pets/request/body.yml +7 -0
- data/spec/fixtures/lambda_post_with_cors/pets/response/201.vtl +1 -0
- data/spec/fixtures/lambda_post_with_cors/pets/response/201.yml +1 -0
- data/spec/fixtures/lambda_post_with_cors/pets/response/500.vtl +3 -0
- data/spec/fixtures/lambda_post_with_cors/pets/response/500.yml +4 -0
- data/spec/fixtures/markdown.rb +73 -0
- data/spec/fixtures/mock_options/README.md +80 -0
- data/spec/fixtures/mock_options/index.rb +15 -0
- data/spec/fixtures/mock_options/index.yml +44 -0
- data/spec/fixtures/mock_options/pets/options.rb +9 -0
- data/spec/spec_helper.rb +5 -0
- metadata +265 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class Parameter
|
3
|
+
class Simple < Parameter
|
4
|
+
|
5
|
+
def initialize(name, **options)
|
6
|
+
super
|
7
|
+
|
8
|
+
@type = options[:type] || 'string'
|
9
|
+
end
|
10
|
+
|
11
|
+
def as_json
|
12
|
+
super.tap do |result|
|
13
|
+
result[:type] = @type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'api_gateway_dsl/parameter/header'
|
22
|
+
require 'api_gateway_dsl/parameter/path'
|
23
|
+
require 'api_gateway_dsl/parameter/query'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class Response
|
3
|
+
|
4
|
+
attr_reader :status_code, :mappings, :templates, :context
|
5
|
+
|
6
|
+
def initialize(operation, status_code, regexp, &block)
|
7
|
+
@context = operation.context.dup.tap { |c| c.default_body_file = "response/#{status_code}" }
|
8
|
+
|
9
|
+
@status_code = status_code.to_s
|
10
|
+
@regexp = regexp(regexp)
|
11
|
+
|
12
|
+
@mappings = Mapping::Collection.new
|
13
|
+
@templates = Template::Collection.new(@context)
|
14
|
+
|
15
|
+
DSL::ResponseNode.new(self, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def content_types
|
19
|
+
templates.content_types
|
20
|
+
end
|
21
|
+
|
22
|
+
def as_json
|
23
|
+
{}.tap do |result|
|
24
|
+
result[:description] = "#{status_code} response"
|
25
|
+
|
26
|
+
if (headers = mappings.response_headers.as_json).present?
|
27
|
+
result[:headers] = headers
|
28
|
+
end
|
29
|
+
|
30
|
+
if (template = templates.current)
|
31
|
+
result[:schema] = template.schema_value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def response_integration
|
37
|
+
ResponseIntegration.new(@regexp, status_code, mappings, templates)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def regexp(value)
|
43
|
+
case value
|
44
|
+
when NilClass then 'default'
|
45
|
+
when Regexp then value.source
|
46
|
+
else value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
require 'api_gateway_dsl/response/collection'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class Response
|
3
|
+
class Collection < Array
|
4
|
+
|
5
|
+
def content_types
|
6
|
+
flat_map(&:content_types).uniq
|
7
|
+
end
|
8
|
+
|
9
|
+
def response_integrations
|
10
|
+
ResponseIntegration::Collection.new.concat(map(&:response_integration))
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json
|
14
|
+
index_by(&:status_code).transform_values(&:as_json)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class ResponseHeader
|
3
|
+
|
4
|
+
VALUE = { type: 'string' }.freeze
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json
|
13
|
+
VALUE
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'api_gateway_dsl/response_header/collection'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class ResponseIntegration
|
3
|
+
|
4
|
+
attr_reader :regexp
|
5
|
+
|
6
|
+
def initialize(regexp, status_code, mappings, templates)
|
7
|
+
@regexp = regexp
|
8
|
+
@status_code = status_code
|
9
|
+
@mappings = mappings
|
10
|
+
@templates = templates
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json
|
14
|
+
{}.tap do |result|
|
15
|
+
result[:statusCode] = @status_code
|
16
|
+
|
17
|
+
if (response_parameters = @mappings.as_json).present?
|
18
|
+
result[:responseParameters] = response_parameters
|
19
|
+
end
|
20
|
+
|
21
|
+
if (response_templates = @templates.as_json).present?
|
22
|
+
result[:responseTemplates] = response_templates
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'api_gateway_dsl/response_integration/collection'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class Template
|
3
|
+
|
4
|
+
attr_reader :schema, :description, :content_type
|
5
|
+
|
6
|
+
def self.new_if_schema_present(context, **options)
|
7
|
+
template = new(context, **options)
|
8
|
+
template.schema_value ? template : nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(context, **options)
|
12
|
+
@context = context
|
13
|
+
|
14
|
+
@schema = options[:schema] || @context.default_body_file
|
15
|
+
@velocity = options[:velocity] || @context.default_body_file
|
16
|
+
|
17
|
+
@description = options[:description].try(:strip_heredoc)
|
18
|
+
@content_type = options[:content_type] || 'application/json'
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_json
|
22
|
+
current_dir.join("#{@velocity}.vtl").read
|
23
|
+
end
|
24
|
+
|
25
|
+
def schema_value
|
26
|
+
return unless current_dir
|
27
|
+
|
28
|
+
if (file = current_dir.join("#{@schema}.json")).exist?
|
29
|
+
JSON.parse(file.read)
|
30
|
+
elsif (file = current_dir.join("#{@schema}.yml")).exist?
|
31
|
+
YAML.safe_load(file.read)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def parameter
|
36
|
+
Parameter::Body.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def current_dir
|
42
|
+
@context.current_dir
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'api_gateway_dsl/template/collection'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module APIGatewayDSL
|
2
|
+
class Template
|
3
|
+
class Collection < Array
|
4
|
+
|
5
|
+
def initialize(context)
|
6
|
+
@fallback = Template.new_if_schema_present(context)
|
7
|
+
end
|
8
|
+
|
9
|
+
def content_types
|
10
|
+
currents.map(&:content_type).uniq
|
11
|
+
end
|
12
|
+
|
13
|
+
def current
|
14
|
+
any? ? first : @fallback
|
15
|
+
end
|
16
|
+
|
17
|
+
# Array containing current or empty array
|
18
|
+
def currents
|
19
|
+
Array(current)
|
20
|
+
end
|
21
|
+
|
22
|
+
def as_json
|
23
|
+
currents.index_by(&:content_type).transform_values(&:as_json)
|
24
|
+
end
|
25
|
+
|
26
|
+
def parameters
|
27
|
+
Parameter::Collection.new.concat(currents.map(&:parameter))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
describe APIGatewayDSL::Document do
|
2
|
+
let(:fixtures_dir) { Pathname.new('spec/fixtures') }
|
3
|
+
|
4
|
+
%w( greedy_http_proxy http_get lambda_post lambda_post_with_cors mock_options ).each do |fixture|
|
5
|
+
describe "for '#{fixture}' fixture" do
|
6
|
+
let(:fixture_dir) { fixtures_dir.join(fixture) }
|
7
|
+
|
8
|
+
let(:from_dsl) { described_class.load(fixture_dir).as_json }
|
9
|
+
let(:from_yml) { YAML.load_file(fixture_dir.join('index.yml')) }
|
10
|
+
|
11
|
+
it 'includes the expected definitions' do
|
12
|
+
expect(from_dsl).to include_json(from_yml)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does not include unexptected definitions' do
|
16
|
+
expect(from_yml).to include_json(from_dsl)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Greedy Endpoint for any HTTP method with HTTP proxy integration
|
2
|
+
|
3
|
+
This example demonstrates how to specify a greedy endpoint which accepts any HTTP method for all paths below
|
4
|
+
`/pets/*` and integrates with an HTTP downstream service at `https://petstore.example.com`.
|
5
|
+
|
6
|
+
## Configuration
|
7
|
+
|
8
|
+
* Beyond the path, the request accepts a header `Accept-Language` which is passed through to the downstream service.
|
9
|
+
|
10
|
+
## Given
|
11
|
+
|
12
|
+
* [`index.rb`](index.rb) (skipped for readability)
|
13
|
+
|
14
|
+
* [`pets/proxy.rb`](pets/proxy.rb)
|
15
|
+
|
16
|
+
```rb
|
17
|
+
ANY '/pets/{proxy+}' do
|
18
|
+
path 'proxy'
|
19
|
+
header 'Accept-Language'
|
20
|
+
|
21
|
+
HTTP_PROXY_ANY 'https://petstore.example.com/{proxy}' do
|
22
|
+
path 'proxy'
|
23
|
+
header 'Accept-Language'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
## Generates
|
29
|
+
|
30
|
+
* [`index.yml`](index.yml)
|
31
|
+
|
32
|
+
```yml
|
33
|
+
swagger: "2.0"
|
34
|
+
info:
|
35
|
+
version: "1.2.3"
|
36
|
+
title: "Greedy Endpoint for any HTTP method with HTTP proxy integration"
|
37
|
+
description: |
|
38
|
+
This example demonstrates how to specify a greedy endpoint which accepts any HTTP method for all paths below
|
39
|
+
`/pets/*` and integrates with an HTTP downstream service at `https://petstore.example.com`.
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
* Beyond the path, the request accepts a header `Accept-Language` which is passed through to the downstream service.
|
44
|
+
host: "api.example.com"
|
45
|
+
schemes:
|
46
|
+
- "https"
|
47
|
+
paths:
|
48
|
+
/pets/{proxy+}:
|
49
|
+
x-amazon-apigateway-any-method:
|
50
|
+
parameters:
|
51
|
+
- name: "proxy"
|
52
|
+
in: "path"
|
53
|
+
required: true
|
54
|
+
type: "string"
|
55
|
+
- name: "Accept-Language"
|
56
|
+
in: "header"
|
57
|
+
required: false
|
58
|
+
type: "string"
|
59
|
+
responses: {}
|
60
|
+
x-amazon-apigateway-integration:
|
61
|
+
requestParameters:
|
62
|
+
integration.request.path.proxy: "method.request.path.proxy"
|
63
|
+
integration.request.header.Accept-Language: "method.request.header.Accept-Language"
|
64
|
+
uri: "https://petstore.example.com/{proxy}"
|
65
|
+
passthroughBehavior: "WHEN_NO_MATCH"
|
66
|
+
httpMethod: "ANY"
|
67
|
+
type: "http_proxy"
|
68
|
+
```
|
69
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
title 'Greedy Endpoint for any HTTP method with HTTP proxy integration'
|
2
|
+
version '1.2.3'
|
3
|
+
description <<-EOS
|
4
|
+
This example demonstrates how to specify a greedy endpoint which accepts any HTTP method for all paths below
|
5
|
+
`/pets/*` and integrates with an HTTP downstream service at `https://petstore.example.com`.
|
6
|
+
|
7
|
+
## Configuration
|
8
|
+
|
9
|
+
* Beyond the path, the request accepts a header `Accept-Language` which is passed through to the downstream service.
|
10
|
+
EOS
|
11
|
+
|
12
|
+
host 'api.example.com'
|
13
|
+
schemes 'https'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
swagger: "2.0"
|
2
|
+
info:
|
3
|
+
version: "1.2.3"
|
4
|
+
title: "Greedy Endpoint for any HTTP method with HTTP proxy integration"
|
5
|
+
description: |
|
6
|
+
This example demonstrates how to specify a greedy endpoint which accepts any HTTP method for all paths below
|
7
|
+
`/pets/*` and integrates with an HTTP downstream service at `https://petstore.example.com`.
|
8
|
+
|
9
|
+
## Configuration
|
10
|
+
|
11
|
+
* Beyond the path, the request accepts a header `Accept-Language` which is passed through to the downstream service.
|
12
|
+
host: "api.example.com"
|
13
|
+
schemes:
|
14
|
+
- "https"
|
15
|
+
paths:
|
16
|
+
/pets/{proxy+}:
|
17
|
+
x-amazon-apigateway-any-method:
|
18
|
+
parameters:
|
19
|
+
- name: "proxy"
|
20
|
+
in: "path"
|
21
|
+
required: true
|
22
|
+
type: "string"
|
23
|
+
- name: "Accept-Language"
|
24
|
+
in: "header"
|
25
|
+
required: false
|
26
|
+
type: "string"
|
27
|
+
responses: {}
|
28
|
+
x-amazon-apigateway-integration:
|
29
|
+
requestParameters:
|
30
|
+
integration.request.path.proxy: "method.request.path.proxy"
|
31
|
+
integration.request.header.Accept-Language: "method.request.header.Accept-Language"
|
32
|
+
uri: "https://petstore.example.com/{proxy}"
|
33
|
+
passthroughBehavior: "WHEN_NO_MATCH"
|
34
|
+
httpMethod: "ANY"
|
35
|
+
type: "http_proxy"
|