Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bcfc937
Port hearth endpoint rules module
alextwoods Dec 13, 2024
355a83a
Add bindings to codegen
alextwoods Dec 13, 2024
43e4caa
Generation of endpoint parameters
alextwoods Dec 16, 2024
9ef8233
Skeleton endpoint provider
alextwoods Dec 16, 2024
784ff6a
endpoint plugin with basic config
alextwoods Dec 16, 2024
fe46555
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 17, 2024
9724b49
PR Cleanups
alextwoods Dec 17, 2024
8d791d0
More pr cleanups
alextwoods Dec 17, 2024
599d4aa
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 18, 2024
f00e788
Rubocop fixes
alextwoods Dec 18, 2024
6de786b
Fix specs
alextwoods Dec 18, 2024
6f5b086
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 18, 2024
b9e8bbc
Rubocop
alextwoods Dec 18, 2024
79b7b14
Add generated resolver spec (templates/view, not working yet)
alextwoods Dec 18, 2024
ba682bb
Some rubocop cleanups
alextwoods Dec 18, 2024
dae2110
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 19, 2024
b3f5fe4
Endpoint spec generation
alextwoods Dec 20, 2024
5a4be49
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 21, 2024
4ebf372
endpoint provider interface spec
alextwoods Dec 23, 2024
9a52cb1
WIP - add handler code + per operation parameters
alextwoods Dec 23, 2024
3feea81
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 23, 2024
cb91d68
Possible weld spec fix
alextwoods Dec 23, 2024
97bfc55
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 23, 2024
a40a16f
Add new endpoint test model + fix issues in trait usage
alextwoods Dec 24, 2024
6bc541f
Generate create for operation parameters
alextwoods Dec 24, 2024
313dc33
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 26, 2024
959762b
Working parameters spec for operations
alextwoods Dec 26, 2024
0a23ea5
rubocop
alextwoods Dec 26, 2024
2e39864
Run all endpoint specs
alextwoods Dec 26, 2024
96c1cc7
fix rubocop
alextwoods Dec 26, 2024
418d80f
Simplify spec task
Dec 27, 2024
d94f1c1
PR cleanups
alextwoods Dec 27, 2024
af7daa6
Run all endpoint specs together
alextwoods Dec 27, 2024
fd882a9
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 27, 2024
9117b45
Remove endpoint bindings from plan. Remove endpoint binding classes
alextwoods Dec 27, 2024
1c38695
Add smithy files for endpoint fixtures
alextwoods Dec 30, 2024
bf448d1
Merge branch 'decaf' into decaf_endpoints
alextwoods Dec 30, 2024
a98c0e0
fix merge issues
alextwoods Dec 30, 2024
956d432
PR feedback
alextwoods Dec 30, 2024
f1a9a60
generated rubocop fixes
alextwoods Dec 30, 2024
0d31124
Rubocop cleanups
alextwoods Dec 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,11 @@ bundle exec smithy-ruby smith client --gem-name weather --gem-version 1.0.0 --de
IRB on weather gem
```
irb -I build/smithy/weather/smithy-ruby/lib -I gems/smithy-client/lib -r weather
```
```

Build a fixture
```
export SMITHY_PLUGIN_DIR=build/smithy/source/smithy-ruby
bundle exec smithy-ruby smith client --gem-name fixture --gem-version 1.0.0 <<< $(cat gems/smithy/spec/fixtures/endpoints/default-values/model.json)

```
31 changes: 30 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,42 @@ require 'rspec/core/rake_task'

RuboCop::RakeTask.new

# rubocop:disable Metrics/BlockLength
namespace :smithy do
RSpec::Core::RakeTask.new do |t|
RSpec::Core::RakeTask.new('spec:unit') do |t|
t.pattern = 'gems/smithy/spec/**/*_spec.rb'
t.ruby_opts = '-I gems/smithy/spec'
t.rspec_opts = '--format documentation'
end

task 'spec:endpoints' do
require_relative 'gems/smithy/spec/spec_helper'

spec_paths = []
include_paths = []
tmp_dirs = []
Dir.glob('gems/smithy/spec/fixtures/endpoints/*/model.json') do |model_path|
test_name = model_path.split('/')[-2]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to express this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the model_path.split('/')[-2] part? I could do model_path.split('/').last(2).first instead, but that felt worse. Alternatively we coudl use regex to extract it, but I think splitting on / is simpler here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was thinking in terms of readability. What exactly is extracted? It's otherwise fine.

test_module = test_name.gsub('-', '').camelize
puts "Building SDK for #{test_name} at: #{model_path}"
tmpdir = SpecHelper.generate([test_module], :client, { fixture: "endpoints/#{test_name}" })
tmp_dirs << [test_module.to_sym, tmpdir]
spec_paths << "#{tmpdir}/spec"
include_paths << "#{tmpdir}/lib"
include_paths << "#{tmpdir}/spec"
end
specs = spec_paths.join(' ')
includes = include_paths.map { |p| "-I #{p}" }.join(' ')
sh("bundle exec rspec #{specs} #{includes}")
ensure
tmp_dirs.each do |name, tmpdir|
SpecHelper.cleanup([name], tmpdir)
end
end

task 'spec' => %w[spec:unit spec:endpoints]
end
# rubocop:enable Metrics/BlockLength

namespace 'smithy-client' do
RSpec::Core::RakeTask.new do |t|
Expand Down
8 changes: 6 additions & 2 deletions gems/smithy-client/lib/smithy-client.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# frozen_string_literal: true

require 'jmespath'

# client

require_relative 'smithy-client/block_io'
require_relative 'smithy-client/configuration'
require_relative 'smithy-client/handler'
require_relative 'smithy-client/handler_builder'
require_relative 'smithy-client/handler_context'
require_relative 'smithy-client/handler_list'
require_relative 'smithy-client/handler_list_entry'
require_relative 'smithy-client/managed_file'
require_relative 'smithy-client/networking_error'
require_relative 'smithy-client/plugin'
require_relative 'smithy-client/plugin_list'
require_relative 'smithy-client/handler_context'
require_relative 'smithy-client/input'
require_relative 'smithy-client/output'

Expand All @@ -27,7 +29,6 @@

# plugins

require_relative 'smithy-client/plugins/endpoint'
require_relative 'smithy-client/plugins/logging'
require_relative 'smithy-client/plugins/raise_response_errors'
require_relative 'smithy-client/plugins/net_http'
Expand All @@ -41,6 +42,9 @@
require_relative 'smithy-client/operation'
require_relative 'smithy-client/structure'

# endpoints
require_relative 'smithy-client/endpoint_rules'

module Smithy
# Base module for a generated Smithy gem.
module Client
Expand Down
1 change: 0 additions & 1 deletion gems/smithy-client/lib/smithy-client/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class Base
# @api private
@plugins = PluginList.new(
[
Plugins::Endpoint,
# Plugins::NetHTTP
Plugins::RaiseResponseErrors
# Plugins::ResponseTarget,
Expand Down
186 changes: 186 additions & 0 deletions gems/smithy-client/lib/smithy-client/endpoint_rules.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# frozen_string_literal: true

require 'cgi'
require 'ipaddr'
require 'uri'

module Smithy
module Client
# Functions in the Smithy rules engine are named routines that
# operate on a finite set of specified inputs, returning an output.
# The rules engine has a set of included functions that can be
# invoked without additional dependencies, called the standard library.
module EndpointRules
# Regex that extracts anything in square brackets
BRACKET_REGEX = /\[(.*?)\]/

# An Endpoint resolved by an EndpointProvider
class Endpoint
# @param [String] :uri
# @param [Array<AuthScheme>] :auth_schemes ([])
# @param [Hash] :headers ({})
def initialize(uri:, properties: {}, headers: {})
@uri = uri
@properties = properties
@headers = headers
end

# The URI of the endpoint.
# @return [String]
attr_accessor :uri

# The authentication schemes supported by the endpoint.
# @return [Hash]
attr_accessor :properties

# The headers to include in requests to the endpoint.
# @return [Hash]
attr_accessor :headers
end

# Evaluates whether the input string is a compliant RFC 1123 host segment.
# When allowSubDomains is true, evaluates whether the input string is
# composed of values that are each compliant RFC 1123 host segments
# joined by dot (.) characters.
# @api private
def self.valid_host_label?(value, allow_sub_domains)
return false if value.empty?

if allow_sub_domains
labels = value.split('.', -1)
return labels.all? { |l| valid_host_label?(l, false) }
end

!!(value =~ /\A(?!-)[a-zA-Z0-9-]{1,63}(?<!-)\z/)
end

# Computes a URL structure given an input string.
# @api private
def self.parse_url(value)
URL.new(value).as_json
rescue ArgumentError, URI::InvalidURIError
nil
end

# Computes a portion of a given string based on
# the provided start and end indices.
# @api private
def self.substring(input, start, stop, reverse)
return nil if start >= stop || input.size < stop

return nil if input.chars.any? { |c| c.ord > 127 }

return input[start...stop] unless reverse

r_start = input.size - stop
r_stop = input.size - start
input[r_start...r_stop]
end

# Performs RFC 3986#section-2.1 defined percent-encoding on the input value.
# @api private
def self.uri_encode(value)
CGI.escape(value.encode('UTF-8')).gsub('+', '%20').gsub('%7E', '~')
end

# isSet(value: Option<T>) bool
# @api private
def self.set?(value)
!value.nil?
end

# not(value: bool) bool
# @api private
def self.not(bool)
!bool
end

# getAttr(value: Object | Array, path: string) Document
# @api private
def self.attr(value, path)
parts = path.split('.')

val = attr_brackets(parts, value)

if parts.size == 1
val
else
attr_reader(val, parts.slice(1..-1).join('.'))
end
end

# stringEquals(value1: string, value2: string) bool
# @api private
def self.string_equals?(value1, value2)
value1 == value2
end

# booleanEquals(value1: bool, value2: bool) bool
# @api private
def self.boolean_equals?(value1, value2)
value1 == value2
end

# @api private
class URL
def initialize(url)
uri = URI(url)
@scheme = uri.scheme
# only support http and https schemes
raise ArgumentError unless %w[https http].include?(@scheme)

# do not support query
raise ArgumentError if uri.query

@authority = extract_authority(url, uri)
@path = uri.path
@normalized_path = uri.path + (uri.path[-1] == '/' ? '' : '/')
@is_ip = ip?(uri.host)
end

attr_reader :scheme, :authority, :path, :normalized_path, :is_ip

def as_json(*)
{
'scheme' => scheme,
'authority' => authority,
'path' => path,
'normalizedPath' => normalized_path,
'isIp' => is_ip
}
end

private

def extract_authority(url, uri)
# don't include port if it's default and not parsed originally
if uri.default_port == uri.port && !url.include?(":#{uri.port}")
uri.host
else
"#{uri.host}:#{uri.port}"
end
end

def ip?(authority)
IPAddr.new(authority)
true
rescue IPAddr::InvalidAddressError
false
end
end

def self.attr_brackets(parts, value)
if (index = parts.first[BRACKET_REGEX, 1])
# remove brackets and index from part before indexing
if (base = parts.first.gsub(BRACKET_REGEX, '')) && !base.empty?
value[base][index.to_i]
else
value[index.to_i]
end
else
value[parts.first]
end
end
end
end
end
48 changes: 0 additions & 48 deletions gems/smithy-client/lib/smithy-client/plugins/endpoint.rb

This file was deleted.

2 changes: 2 additions & 0 deletions gems/smithy-client/smithy-client.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ Gem::Specification.new do |spec|
spec.license = 'Apache-2.0'
spec.files = Dir['CHANGELOG.md', 'VERSION', 'lib/**/*']

spec.add_dependency('jmespath', '~> 1', '>= 1.6.1') # necessary for secure jmespath JSON parsing

spec.required_ruby_version = '>= 3.0'
end
Loading
Loading