xfrtuc 0.0.13 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ Layout/SpaceInsideHashLiteralBraces:
2
+ Enabled: true
3
+ EnforcedStyle: space
4
+ EnforcedStyleForEmptyBraces: no_space
5
+
6
+ Style/FrozenStringLiteralComment:
7
+ Enabled: true
8
+
9
+ Style/TrailingCommaInArguments:
10
+ Enabled: true
11
+ EnforcedStyleForMultiline: consistent_comma
12
+
13
+ Style/TrailingCommaInArrayLiteral:
14
+ Enabled: true
15
+ EnforcedStyleForMultiline: consistent_comma
16
+
17
+ Style/TrailingCommaInHashLiteral:
18
+ Enabled: true
19
+ EnforcedStyleForMultiline: consistent_comma
20
+
21
+ Lint/NumberConversion:
22
+ Enabled: true
23
+
24
+ RSpec/ExampleLength:
25
+ Max: 12
26
+
27
+ RSpec/MultipleExpectations:
28
+ Max: 3
29
+
30
+ RSpec/MultipleMemoizedHelpers:
31
+ Max: 8
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.9
data/CHANGELOG.md ADDED
@@ -0,0 +1,142 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ -
12
+
13
+ ### Changed
14
+
15
+ -
16
+
17
+ ## [1.0.0] - 2026-04-30
18
+
19
+ ### Added
20
+
21
+ - `Xfrtuc::HTTP::Error` exception hierarchy. See: lib/xfrtuc/errors.rb
22
+ - Support for testing against multiple Ruby versions in GitHub Actions.
23
+ - SimpleCov for code coverage reporting.
24
+
25
+ ### Changed
26
+
27
+ - Replaced `sham_rack` / `FakeTransferatu` test fake with WebMock stubs.
28
+ - Updated rspec configuration.
29
+ - Switched from CircleCI to GitHub Actions for CI.
30
+
31
+ ### Removed
32
+
33
+ - **Breaking Change:** Replace `RestClient` with stdlib `Net::HTTP` - Gem-specific errors are raised rather than Excon-specific errors. See: lib/xfrtuc/errors.rb
34
+ - Dropped support for Ruby < 3.2.
35
+
36
+ ## [0.0.13] - 2021-03-04
37
+
38
+ ### Added
39
+
40
+ - CircleCI configuration for running specs and linting.
41
+
42
+ ### Fixed
43
+
44
+ - Deprecation warning for obsolete `URI.escape` usage.
45
+
46
+ ### Changed
47
+
48
+ - Updated to Ruby 3 compatibility.
49
+ - Updated to Ruby 2.7.
50
+
51
+ ## [0.0.12] - 2020-05-29
52
+
53
+ ### Changed
54
+
55
+ - Upgraded rest-client dependency to ~> 2.0.
56
+
57
+ ## [0.0.11] - 2017-12-12
58
+
59
+ ### Removed
60
+
61
+ - Bastion support.
62
+
63
+ ### Changed
64
+
65
+ - Updated Ruby version and all gem dependencies.
66
+
67
+ ## [0.0.10] - 2017-03-14
68
+
69
+ ### Added
70
+
71
+ - Bastion support for connecting through a bastion host.
72
+
73
+ ## [0.0.9] - 2016-02-04
74
+
75
+ ### Added
76
+
77
+ - `num` parameter support for transfer scheduling.
78
+
79
+ ## [0.0.8] - 2015-08-07
80
+
81
+ ### Added
82
+
83
+ - `delete_transfer` method for removing transfers.
84
+ - `transfer_action` method for performing actions on transfers.
85
+
86
+ ## [0.0.7] - 2015-06-15
87
+
88
+ ### Changed
89
+
90
+ - Updated transfer and group listing to use verbose output.
91
+
92
+ ## [0.0.6] - 2015-04-15
93
+
94
+ ### Added
95
+
96
+ - `schedule` and `schedule_list` methods for managing transfer schedules.
97
+
98
+ ## [0.0.5] - 2015-04-02
99
+
100
+ ### Added
101
+
102
+ - Transfer group support with `group_list` and `group_create` methods.
103
+
104
+ ## [0.0.4] - 2015-02-05
105
+
106
+ ### Added
107
+
108
+ - Public URL support in transfer creation.
109
+
110
+ ## [0.0.3] - 2015-02-05
111
+
112
+ ### Added
113
+
114
+ - Transfer log retrieval via `transfer_log` method.
115
+
116
+ ## [0.0.2] - 2014-09-04
117
+
118
+ ### Added
119
+
120
+ - Transfer listing and creation via Transferatu API.
121
+
122
+ ## [0.0.1] - 2014-09-03
123
+
124
+ ### Added
125
+
126
+ - Initial release with basic Transferatu client structure.
127
+
128
+ [Unreleased]: https://github.com/heroku/xfrtuc/compare/v1.0.0...HEAD
129
+ [1.0.0]: https://github.com/heroku/xfrtuc/compare/v0.0.13...v1.0.0
130
+ [0.0.13]: https://github.com/heroku/xfrtuc/compare/v0.0.12...v0.0.13
131
+ [0.0.12]: https://github.com/heroku/xfrtuc/compare/v0.0.11...v0.0.12
132
+ [0.0.11]: https://github.com/heroku/xfrtuc/compare/v0.0.10...v0.0.11
133
+ [0.0.10]: https://github.com/heroku/xfrtuc/compare/v0.0.9...v0.0.10
134
+ [0.0.9]: https://github.com/heroku/xfrtuc/compare/v0.0.8...v0.0.9
135
+ [0.0.8]: https://github.com/heroku/xfrtuc/compare/v0.0.7...v0.0.8
136
+ [0.0.7]: https://github.com/heroku/xfrtuc/compare/v0.0.6...v0.0.7
137
+ [0.0.6]: https://github.com/heroku/xfrtuc/compare/v0.0.5...v0.0.6
138
+ [0.0.5]: https://github.com/heroku/xfrtuc/compare/v0.0.4...v0.0.5
139
+ [0.0.4]: https://github.com/heroku/xfrtuc/compare/v0.0.3...v0.0.4
140
+ [0.0.3]: https://github.com/heroku/xfrtuc/compare/v0.0.2...v0.0.3
141
+ [0.0.2]: https://github.com/heroku/xfrtuc/compare/v0.0.1...v0.0.2
142
+ [0.0.1]: https://github.com/heroku/xfrtuc/releases/tag/v0.0.1
data/CODEOWNERS ADDED
@@ -0,0 +1,4 @@
1
+ # Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing.
2
+ #ECCN:Open Source
3
+ #GUSINFO:Heroku Data Operations,Heroku Data Intake
4
+ * @heroku/data
data/Gemfile CHANGED
@@ -1,4 +1,13 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in xfrtuc.gemspec
4
6
  gemspec
7
+
8
+ gem "rspec", "~> 3.0"
9
+ gem "rubocop"
10
+ gem "rubocop-performance"
11
+ gem "rubocop-rspec"
12
+ gem "simplecov"
13
+ gem "webmock"
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # xfrtuc
2
+
3
+ A Ruby client for the [Transferatu](https://github.com/heroku/transferatu) API, used to manage data transfers, schedules, and groups.
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem 'xfrtuc'
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ client = Xfrtuc::Client.new(user: "user", password: "secret", url: "https://transferatu.example.com")
15
+
16
+ group = client.group.create("my-group", "https://log-input.example.com")
17
+ client.group("my-group").transfer.create(from_url: "postgres:///source", to_url: "postgres:///target")
18
+ client.group("my-group").transfer.list
19
+ client.group("my-group").schedule.create(name: "nightly", callback_url: "https://example.com/callback")
20
+ ```
21
+
22
+ ## Development
23
+
24
+ ```shell
25
+ bundle install
26
+ bundle exec rspec
27
+ ```
28
+
29
+ ## Releasing
30
+
31
+ 1. **Bump the version** in `lib/xfrtuc/version.rb`
32
+
33
+ 2. **Update `CHANGELOG.md`** — move entries from `[Unreleased]` into a new versioned section and update the comparison links at the bottom
34
+
35
+ 3. **Commit the changes**
36
+
37
+ ```shell
38
+ git add lib/xfrtuc/version.rb CHANGELOG.md
39
+ git commit -m "version -> x.y.z"
40
+ ```
41
+
42
+ 4. **Create and push a git tag**
43
+
44
+ ```shell
45
+ git tag vx.y.z
46
+ git push origin main --tags
47
+ ```
48
+
49
+ 5. **Build and push the gem to RubyGems.org**
50
+
51
+ ```shell
52
+ gem build
53
+ gem push xfrtuc-x.y.z.gem
54
+ ```
55
+
56
+ ## License
57
+
58
+ MIT
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xfrtuc
4
+ module HTTP
5
+ class Error < StandardError; end
6
+
7
+ class ClientError < Error; end
8
+ class BadRequest < ClientError; end
9
+ class NotFound < ClientError; end
10
+ class Conflict < ClientError; end
11
+ class Gone < ClientError; end
12
+
13
+ class ServerError < Error; end
14
+ class ServiceUnavailable < ServerError; end
15
+
16
+ class ConnectionResetError < Error; end
17
+ class SocketError < Error; end
18
+ end
19
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Xfrtuc
2
- VERSION = "0.0.13"
4
+ VERSION = "1.0.0"
3
5
  end
data/lib/xfrtuc.rb CHANGED
@@ -1,19 +1,19 @@
1
- require 'json'
2
- require 'rest_client'
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "json"
5
+ require "net/http"
6
+ require "uri"
7
+ require "xfrtuc/errors"
3
8
 
4
9
  module Xfrtuc
5
10
  class Client
6
11
  attr_reader :base_url
7
12
 
8
- def initialize(username, password, base_url='https://transferatu.heroku.com')
13
+ def initialize(username, password, base_url = "https://transferatu.heroku.com")
9
14
  @base_url = base_url
10
15
  @username = username
11
16
  @password = password
12
- @resource = RestClient::Resource.new(base_url,
13
- user: username,
14
- password: password,
15
- headers: { content_type: 'application/json',
16
- accept: 'application/json' })
17
17
  end
18
18
 
19
19
  def transfer
@@ -24,29 +24,77 @@ module Xfrtuc
24
24
  @sched_client ||= Xfrtuc::Schedule.new(self)
25
25
  end
26
26
 
27
- def group(name=nil)
27
+ def group(name = nil)
28
28
  if name.nil?
29
29
  @group_client ||= Xfrtuc::Group.new(self)
30
30
  else
31
31
  self.class.new(@username, @password,
32
- @base_url + "/groups/#{CGI.escape(name)}")
32
+ @base_url + "/groups/#{CGI.escape(name)}",)
33
33
  end
34
34
  end
35
35
 
36
- def get(path, params={})
37
- JSON.parse(@resource[path].get(params))
36
+ def get(path, params = {})
37
+ uri = build_uri(path)
38
+ uri.query = URI.encode_www_form(params[:params]) if params[:params]
39
+ request = Net::HTTP::Get.new(uri)
40
+ execute(uri, request)
38
41
  end
39
42
 
40
- def post(path, data={})
41
- JSON.parse(@resource[path].post(JSON.generate(data)))
43
+ def post(path, data = {})
44
+ uri = build_uri(path)
45
+ request = Net::HTTP::Post.new(uri)
46
+ request.body = JSON.generate(data)
47
+ execute(uri, request)
42
48
  end
43
49
 
44
- def put(path, data={})
45
- JSON.parse(@resource[path].put(JSON.generate(data)))
50
+ def put(path, data = {})
51
+ uri = build_uri(path)
52
+ request = Net::HTTP::Put.new(uri)
53
+ request.body = JSON.generate(data)
54
+ execute(uri, request)
46
55
  end
47
56
 
48
57
  def delete(path)
49
- JSON.parse(@resource[path].delete)
58
+ uri = build_uri(path)
59
+ request = Net::HTTP::Delete.new(uri)
60
+ execute(uri, request)
61
+ end
62
+
63
+ private
64
+
65
+ def build_uri(path)
66
+ URI.parse("#{@base_url}#{path}")
67
+ end
68
+
69
+ HTTP_ERROR_MAP = {
70
+ 400 => HTTP::BadRequest,
71
+ 404 => HTTP::NotFound,
72
+ 409 => HTTP::Conflict,
73
+ 410 => HTTP::Gone,
74
+ 503 => HTTP::ServiceUnavailable,
75
+ }.freeze
76
+
77
+ def execute(uri, request)
78
+ request.basic_auth(@username, @password)
79
+ request["Content-Type"] = "application/json"
80
+ request["Accept"] = "application/json"
81
+ http = Net::HTTP.new(uri.host, uri.port)
82
+ http.use_ssl = uri.scheme == "https"
83
+ response = http.request(request)
84
+ status = Integer(response.code)
85
+ unless (200..299).cover?(status)
86
+ error_class = HTTP_ERROR_MAP.fetch(status) do
87
+ (status >= 500) ? HTTP::ServerError : HTTP::ClientError
88
+ end
89
+ raise error_class, "Expected 2xx, got #{response.code}"
90
+ end
91
+ return nil if response.body.nil? || response.body.empty?
92
+ JSON.parse(response.body)
93
+ rescue Errno::ECONNRESET => e
94
+ raise HTTP::ConnectionResetError, e.message
95
+ rescue ::SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
96
+ Errno::ENETUNREACH, Net::OpenTimeout, Net::ReadTimeout => e
97
+ raise HTTP::SocketError, e.message
50
98
  end
51
99
  end
52
100
 
@@ -61,7 +109,9 @@ module Xfrtuc
61
109
  end
62
110
 
63
111
  class Group < ApiEndpoint
64
- def initialize(client); super; end
112
+ def initialize(client)
113
+ super
114
+ end
65
115
 
66
116
  def info(name)
67
117
  client.get("/groups/#{CGI.escape(name)}")
@@ -71,7 +121,7 @@ module Xfrtuc
71
121
  client.get("/groups")
72
122
  end
73
123
 
74
- def create(name, log_input_url=nil)
124
+ def create(name, log_input_url = nil)
75
125
  client.post("/groups", { name: name, log_input_url: log_input_url })
76
126
  end
77
127
 
@@ -81,9 +131,11 @@ module Xfrtuc
81
131
  end
82
132
 
83
133
  class Transfer < ApiEndpoint
84
- def initialize(client); super; end
134
+ def initialize(client)
135
+ super
136
+ end
85
137
 
86
- def info(id, opts={})
138
+ def info(id, opts = {})
87
139
  verbose = opts.delete(:verbose) || false
88
140
  unless opts.empty?
89
141
  raise ArgumentError, "Unsupported option(s): #{opts.keys}"
@@ -100,7 +152,7 @@ module Xfrtuc
100
152
  from_url = opts.fetch :from_url
101
153
  to_type = opts.fetch :to_type
102
154
  to_url = opts.fetch :to_url
103
- [ :from_type, :from_url, :to_type, :to_url ].each { |key| opts.delete key }
155
+ [:from_type, :from_url, :to_type, :to_url].each { |key| opts.delete key }
104
156
  from_name = opts.delete :from_name
105
157
  to_name = opts.delete :to_name
106
158
  log_input_url = opts.delete :log_input_url
@@ -110,15 +162,15 @@ module Xfrtuc
110
162
  raise ArgumentError, "Unsupported option(s): #{opts.keys}"
111
163
  end
112
164
  payload = {
113
- from_type: from_type,
114
- from_url: from_url,
115
- from_name: from_name,
116
- to_type: to_type,
117
- to_url: to_url,
118
- to_name: to_name
119
- }
120
- payload.merge!(log_input_url: log_input_url) unless log_input_url.nil?
121
- payload.merge!(num_keep: num_keep) unless num_keep.nil?
165
+ from_type: from_type,
166
+ from_url: from_url,
167
+ from_name: from_name,
168
+ to_type: to_type,
169
+ to_url: to_url,
170
+ to_name: to_name,
171
+ }
172
+ payload[:log_input_url] = log_input_url unless log_input_url.nil?
173
+ payload[:num_keep] = num_keep unless num_keep.nil?
122
174
  client.post("/transfers", payload)
123
175
  end
124
176
 
@@ -130,13 +182,15 @@ module Xfrtuc
130
182
  client.post("/transfers/#{CGI.escape(id)}/actions/cancel")
131
183
  end
132
184
 
133
- def public_url(id, opts={})
185
+ def public_url(id, opts = {})
134
186
  client.post("/transfers/#{CGI.escape(id)}/actions/public-url", opts)
135
187
  end
136
188
  end
137
189
 
138
190
  class Schedule < ApiEndpoint
139
- def initialize(client); super; end
191
+ def initialize(client)
192
+ super
193
+ end
140
194
 
141
195
  def info(id)
142
196
  client.get("/schedules/#{id}")
@@ -151,11 +205,11 @@ module Xfrtuc
151
205
  callback_url = opts.fetch :callback_url
152
206
  hour = opts.fetch :hour
153
207
  days = opts.fetch(:days, Date::DAYNAMES)
154
- timezone = opts.fetch(:timezone, 'UTC')
208
+ timezone = opts.fetch(:timezone, "UTC")
155
209
  retain_weeks = opts.delete(:retain_weeks)
156
210
  retain_months = opts.delete(:retain_months)
157
211
 
158
- [ :name, :callback_url, :hour, :days, :timezone ].each { |key| opts.delete key }
212
+ [:name, :callback_url, :hour, :days, :timezone].each { |key| opts.delete key }
159
213
  unless opts.empty?
160
214
  raise ArgumentError, "Unsupported option(s): #{opts.keys}"
161
215
  end
@@ -164,7 +218,7 @@ module Xfrtuc
164
218
  callback_url: callback_url,
165
219
  hour: hour,
166
220
  days: days,
167
- timezone: timezone }
221
+ timezone: timezone, }
168
222
  sched_opts[:retain_weeks] = retain_weeks unless retain_weeks.nil?
169
223
  sched_opts[:retain_months] = retain_months unless retain_months.nil?
170
224
 
data/spec/spec_helper.rb CHANGED
@@ -1,24 +1,26 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
1
+ # frozen_string_literal: true
7
2
 
8
- require 'bundler'
9
- require 'xfrtuc'
3
+ require "simplecov"
4
+ SimpleCov.start
10
5
 
11
- RSpec.configure do |config|
12
- config.run_all_when_everything_filtered = true
13
- config.filter_run :focus
6
+ require "bundler"
7
+ require "date"
8
+ require "xfrtuc"
9
+ require "webmock/rspec"
14
10
 
15
- # Run specs in random order to surface order dependencies. If you find an
16
- # order dependency and want to debug it, you can fix the order by providing
17
- # the seed, which is printed after each run.
18
- # --seed 1234
19
- config.order = 'random'
11
+ RSpec.configure do |config|
12
+ config.expect_with :rspec do |expectations|
13
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
14
+ end
20
15
 
21
- config.expect_with :rspec do |c|
22
- c.syntax = :expect
16
+ config.mock_with :rspec do |mocks|
17
+ mocks.verify_partial_doubles = true
23
18
  end
19
+
20
+ config.shared_context_metadata_behavior = :apply_to_host_groups
21
+ config.filter_run_when_matching :focus
22
+ config.disable_monkey_patching!
23
+ config.warnings = true
24
+ config.order = :random
25
+ Kernel.srand config.seed
24
26
  end