-
Notifications
You must be signed in to change notification settings - Fork 7
Waiters #292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Waiters #292
Changes from 43 commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
0f224ac
Add waiters to weather model
richardwang1124 bbcb6cc
Initial code generation for waiters
richardwang1124 4da115b
Add conditional templating for waiters
richardwang1124 33bf1da
Add initial Waiters module
richardwang1124 ad95325
Change casing
richardwang1124 fd76a10
Start adding waiter tests
richardwang1124 c6a9395
Small fixes
richardwang1124 8f4856e
Dynamically create waiters
richardwang1124 4b0a05d
Add dynamic waiter to code gen
richardwang1124 3dd5e64
Debug test failure
richardwang1124 970f236
Revert fixture model changes
richardwang1124 c588576
Start adding tests
richardwang1124 5f70fa1
Add more unit tests
richardwang1124 ff32042
Add more unit tests
richardwang1124 0c8cf8b
Update test names
richardwang1124 63c6e6f
Add underscore methods
richardwang1124 d026d67
Cleanup
richardwang1124 f28e317
Remove runtime trait waiters
richardwang1124 01aca71
Add changes from review comments
richardwang1124 3ff3ca1
Underscore paths during code generation
richardwang1124 6decd06
Add documentation and deprecated traits to generated waiters
richardwang1124 779fe1a
Refactor unit tests
richardwang1124 e8e5f2b
Add more tests
richardwang1124 4b64c83
Remove weather changes
richardwang1124 dcfc31e
Merge decaf
richardwang1124 6da8bfb
Build projections with waiters
richardwang1124 b788ecd
Rubocop and changes from PR comments
richardwang1124 416a74d
Clean up and shave down unit tests
richardwang1124 93cd13d
Remove extra error from spec
richardwang1124 1fdd251
Add wait_until documentation
richardwang1124 b01734d
Move waiters errors to smithy client errors
richardwang1124 7a6b4d2
Preprocess error type
richardwang1124 f8493ed
Use docstrings
richardwang1124 6ef2dfd
Remove waiters/errors
richardwang1124 df63769
Add documentation check
richardwang1124 e58f50c
Fix documentation check
richardwang1124 874cf28
Handle raise response error enabled and use stub responses for tests
richardwang1124 c7c81dd
Update wait_until to return nil upon success
richardwang1124 470b0d3
Cleanup fixture
richardwang1124 dd11d0e
Fix RBS errors
richardwang1124 25394d5
Add some fixes from PR comments
richardwang1124 108834b
Add rest of PR comment fixes
richardwang1124 835e376
Define variables inside context
richardwang1124 3420325
Make PR comment changes
richardwang1124 70c3315
Add todo
richardwang1124 7ebe9b0
Fix namespace issue with plugins
1334bbe
Merge branch 'decaf' into feature/waiters
87185bc
Rubocop
61ada8c
Fix TODO
01a9483
Remove fixed test
richardwang1124 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Smithy | ||
| module Client | ||
| module Waiters | ||
| # Abstract poller class which polls a single API operation and inspects | ||
| # the output and/or error for states matching one of its acceptors. | ||
| class Poller | ||
| def initialize(options = {}) | ||
| @operation_name = options[:operation_name] | ||
| @acceptors = options[:acceptors] | ||
| end | ||
|
|
||
| def call(client, params) | ||
| @input = params | ||
| # TODO: make build_input public and update this line | ||
| input = client.send(:build_input, @operation_name, params) | ||
| input.handlers.remove(Smithy::Client::Plugins::RaiseResponseErrors::Handler) | ||
|
richardwang1124 marked this conversation as resolved.
Outdated
|
||
| output = input.send_request | ||
| status = evaluate_acceptors(output) | ||
| [output, status.to_sym] | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def evaluate_acceptors(output) | ||
| @acceptors.each do |acceptor| | ||
| return acceptor['state'] if acceptor_matches?(acceptor['matcher'], output) | ||
| end | ||
| output.error.nil? ? 'retry' : 'error' | ||
| end | ||
|
|
||
| def acceptor_matches?(matcher, output) | ||
| matcher_type = matcher.keys.first | ||
| send("matches_#{matcher_type}?", matcher[matcher_type], output) | ||
| end | ||
|
|
||
| def matches_output?(path_matcher, output) | ||
| return false if output.data.nil? | ||
|
|
||
| actual = JMESPath.search(path_matcher['path'], output.data) | ||
| equal?(actual, path_matcher['expected'], path_matcher['comparator']) | ||
| end | ||
|
|
||
| # rubocop:disable Naming/MethodName | ||
| def matches_inputOutput?(path_matcher, output) | ||
| return false unless !output.data.nil? && @input | ||
|
|
||
| data = { | ||
| input: @input, | ||
| output: output.data | ||
| } | ||
| actual = JMESPath.search(path_matcher['path'], data) | ||
| equal?(actual, path_matcher['expected'], path_matcher['comparator']) | ||
| end | ||
|
|
||
| def matches_success?(path_matcher, output) | ||
| path_matcher == true ? !output.data.nil? : !output.error.nil? | ||
| end | ||
|
|
||
| def matches_errorType?(path_matcher, output) | ||
| return false if output.error.nil? | ||
|
|
||
| output.error.class.to_s.include?(path_matcher) | ||
| end | ||
|
|
||
| def equal?(actual, expected, comparator) | ||
| send("#{comparator}?", actual, expected) | ||
| end | ||
|
|
||
| def stringEquals?(actual, expected) | ||
| actual == expected | ||
| end | ||
|
|
||
| def booleanEquals?(actual, expected) | ||
| actual.to_s == expected | ||
| end | ||
|
|
||
| def allStringEquals?(actual, expected) | ||
| return false if actual.nil? || actual.empty? | ||
|
|
||
| actual.all? { |value| value == expected } | ||
| end | ||
|
|
||
| def anyStringEquals?(actual, expected) | ||
| return false if actual.nil? || actual.empty? | ||
|
|
||
| actual.any? { |value| value == expected } | ||
| end | ||
| # rubocop:enable Naming/MethodName | ||
| end | ||
| end | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Smithy | ||
| module Client | ||
| module Waiters | ||
| # Raised when a waiter detects a condition where the waiter can never | ||
| # succeed. | ||
| class WaiterFailed < StandardError; end | ||
|
|
||
| # Raised when a waiter enters a failure state. | ||
| class FailureStateError < WaiterFailed | ||
| def initialize(error) | ||
| msg = "stopped waiting, encountered a failure state: #{error}" | ||
| super(msg) | ||
| end | ||
| end | ||
|
|
||
| # Raised when the total wait time of a waiter exceeds the maximum | ||
| # wait time. | ||
| class MaxWaitTimeExceededError < WaiterFailed | ||
| def initialize(max_wait_time) | ||
| msg = "stopped waiting after maximum wait time of #{max_wait_time} seconds was exceeded" | ||
| super(msg) | ||
| end | ||
| end | ||
|
|
||
| # Raised when a waiter encounters an unexpected error. | ||
| class UnexpectedError < WaiterFailed | ||
| def initialize(error) | ||
| msg = "stopped waiting due to an unexpected error: #{error}" | ||
| super(msg) | ||
| end | ||
| end | ||
|
|
||
| # Raised when attempting to get a waiter by name and the waiter has not | ||
| # been defined. | ||
| class NoSuchWaiterError < ArgumentError | ||
| def initialize(waiter_name, valid_waiters) | ||
| msg = "no such waiter: #{waiter_name}; valid waiter names are: #{valid_waiters}" | ||
| super(msg) | ||
| end | ||
| end | ||
|
|
||
| # Abstract waiter class which waits for a resource to reach a desired | ||
| # state. | ||
| class Waiter | ||
| def initialize(options = {}) | ||
| @max_wait_time = max_wait_time(options[:max_wait_time]) | ||
| @remaining_time = @max_wait_time | ||
| @max_delay = max_delay(options[:max_delay]) | ||
| @min_delay = min_delay(options[:min_delay]) | ||
| @poller = options[:poller] | ||
| end | ||
|
|
||
| def wait(client, params) | ||
| poll(client, params) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def max_wait_time(time) | ||
| raise ArgumentError, "expected `:max_wait_time` to be an integer, got: #{time}" unless time.is_a?(Integer) | ||
|
richardwang1124 marked this conversation as resolved.
Outdated
|
||
|
|
||
| time | ||
| end | ||
|
|
||
| def max_delay(delay) | ||
| raise ArgumentError, '`:max_delay` must be greater than 0' if delay < 1 | ||
|
|
||
| delay | ||
| end | ||
|
|
||
| def min_delay(delay) | ||
| if delay < 1 || delay > @max_delay | ||
| raise ArgumentError, '`:min_delay` must be greater than 0 and less than or equal to `:max_delay`' | ||
| end | ||
|
|
||
| delay | ||
| end | ||
|
|
||
| def poll(client, params) | ||
| attempts = 0 | ||
| loop do | ||
| output, status = @poller.call(client, params) | ||
| attempts += 1 | ||
|
|
||
| case status | ||
| when :success then return | ||
| when :failure then raise FailureStateError, output.error | ||
| when :error then raise UnexpectedError, output.error | ||
| when :retry | ||
| raise MaxWaitTimeExceededError, @max_wait_time if @remaining_time.zero? | ||
|
|
||
| delay = delay(attempts) | ||
| @remaining_time -= delay | ||
| sleep(delay) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def delay(attempts) | ||
| attempt_ceiling = (Math.log(@max_delay / @min_delay) / Math.log(2)) + 1 | ||
| delay = attempts > attempt_ceiling ? @max_delay : @min_delay * (2**(attempts - 1)) | ||
| delay = rand(@min_delay..delay) | ||
| delay = @remaining_time if @remaining_time - delay <= @min_delay | ||
| delay | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.