happi 0.1.0 → 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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +90 -0
- data/CLAUDE.md +108 -0
- data/README.md +52 -32
- data/happi.gemspec +13 -10
- data/lib/happi/client.rb +12 -5
- data/lib/happi/configuration.rb +15 -8
- data/lib/happi/error.rb +6 -6
- data/lib/happi/file.rb +6 -2
- data/lib/happi/version.rb +1 -1
- metadata +25 -66
- data/.travis.yml +0 -12
- data/spec/client_spec.rb +0 -82
- data/spec/configuration_spec.rb +0 -13
- data/spec/file_spec.rb +0 -46
- data/spec/fixtures/award.docx +0 -0
- data/spec/spec_helper.rb +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8af047b2bd93ef7975d0d19cac03998a5ffaba9e07932e68d3935d34d3d665f2
|
|
4
|
+
data.tar.gz: e235a40250203d1bc42d441354d59a1c648b121c41a328c1091bbbf0b03bd864
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 801e1f3019493f895448e75a509198c3e1d5ef486fff29d6666104a46ff6f981c7c74c743fd78f051e36924d871338aafca83141829fd7f71d5e99a335a092b5
|
|
7
|
+
data.tar.gz: eeee30b92a62a9f18effa0050b024d05bc3142467d2b8fc41c5c77e8b3e4a537d28ee860a49f3e961ee8bf8086417f674ea1bea0adfc4dd6b8f42c72930be79d
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.4.8
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.0.0] - 2026-08-12
|
|
10
|
+
Supersedes the `1.0.0-rc` and `1.0.0-rc2` pre-releases; everything below is relative
|
|
11
|
+
to 0.6.0.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **BREAKING**: OAuth2 tokens are sent only as an `Authorization` header. `FaradayMiddleware::OAuth2`
|
|
15
|
+
defaulted to its `'param'` token type, which appended the token as an `access_token` query string
|
|
16
|
+
parameter *and* set an `Authorization: Token token="…"` header; that applied whenever `token_type`
|
|
17
|
+
was unset or explicitly `'param'`. Servers reading the token from the query string will no longer
|
|
18
|
+
receive it. The scheme now defaults to `Bearer` and is overridden by `token_type`; no header is set
|
|
19
|
+
when `oauth_token` is blank.
|
|
20
|
+
- **BREAKING**: `token_type` has changed meaning. It was a mode selector for
|
|
21
|
+
`FaradayMiddleware::OAuth2` accepting `'param'` or `'bearer'`; it is now interpolated verbatim as
|
|
22
|
+
the `Authorization` scheme. Two consequences for 0.6.0 consumers:
|
|
23
|
+
- `token_type: 'param'` produces the malformed header `Authorization: param <token>` and no query
|
|
24
|
+
parameter. There is no replacement for the query-string mode in 1.0.0 — remove the setting and
|
|
25
|
+
have the server read the token from the `Authorization` header. Tokens in query strings end up
|
|
26
|
+
in access logs, proxy caches and `Referer` headers, which is why the mode is gone.
|
|
27
|
+
- `token_type: 'bearer'` now emits `bearer <token>` where 0.6.0 normalised it to `Bearer <token>`.
|
|
28
|
+
Scheme names are case-insensitive per RFC 7235 §2.1, so conforming servers are unaffected.
|
|
29
|
+
- **BREAKING**: Removed the `oauth2`, `multi_json`, `faraday-follow_redirects` and `faraday-http`
|
|
30
|
+
dependencies. Applications using these directly must add them to their own Gemfile.
|
|
31
|
+
- **BREAKING**: Minimum Ruby is now 3.2.0.
|
|
32
|
+
- Replaced `activemodel` with `activesupport` - only ActiveSupport features were used.
|
|
33
|
+
- Widened `faraday` to `>= 1.10.0` and `mime-types` to `>= 2.4.0`. Faraday 1.10 is the earliest
|
|
34
|
+
release that registers the `:json` request/response middleware this gem relies on.
|
|
35
|
+
- Packaged gem now contains only runtime files. Specs (and their 2MB fixture), research notes and
|
|
36
|
+
CI/editor config are no longer shipped, taking the gem from ~2.1MB to ~14KB. The deprecated
|
|
37
|
+
`spec.test_files` directive was removed along with them.
|
|
38
|
+
|
|
39
|
+
### Removed
|
|
40
|
+
- **BREAKING**: `Happi::Client#connection_options`. It built an options hash for
|
|
41
|
+
`FaradayMiddleware::OAuth2` and has had no callers since that middleware was dropped —
|
|
42
|
+
`#connection` never consulted it. Subclasses that overrode it were already having no effect.
|
|
43
|
+
- All `FaradayMiddleware` usage. The gem was never compatible with Faraday 2.x and was not a
|
|
44
|
+
declared dependency of Happi.
|
|
45
|
+
- Travis and Buildkite CI configuration, superseded by the GitHub Actions workflow (Buildkite
|
|
46
|
+
still provisioned Ruby 2.1.5).
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
- Requests no longer fail with `NoMethodError: undefined method 'new' for JSON:Module`. The
|
|
50
|
+
connection passed the `::JSON` module itself to `f.use` as response middleware; it now uses
|
|
51
|
+
Faraday's built-in `:json` response middleware.
|
|
52
|
+
- `Happi::Client` requires `logger` itself rather than relying on Faraday to load it, which
|
|
53
|
+
raised `NameError: uninitialized constant Logger` under Faraday 1.x.
|
|
54
|
+
- JSON responses are parsed regardless of `use_json`, restoring 0.6.0 behaviour where
|
|
55
|
+
`ParseJson` was registered unconditionally. `use_json` selects the request encoding only -
|
|
56
|
+
JSON body versus multipart/url-encoded. In `1.0.0-rc2` response parsing was moved inside the
|
|
57
|
+
`use_json` branch, so clients on the default `use_json: false` received an unparsed `String`
|
|
58
|
+
body and raised `NoMethodError` on `with_indifferent_access`.
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
- GitHub Actions workflow running the suite against Ruby 3.2, 3.3, 3.4 and 4.0.
|
|
62
|
+
- Dev container definition for local development.
|
|
63
|
+
- Substantially expanded specs covering the error hierarchy, file handling, configuration
|
|
64
|
+
isolation between base and derived clients, and round trips that exercise the real Faraday
|
|
65
|
+
middleware stack rather than a stubbed connection.
|
|
66
|
+
- Contributor documentation (`CLAUDE.md`, Copilot instructions) and Faraday 2.x migration notes
|
|
67
|
+
under `context/`.
|
|
68
|
+
|
|
69
|
+
## [0.6.0] - 2024-11-13
|
|
70
|
+
### Added
|
|
71
|
+
- Adds support for Ruby 3.2
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## [0.4.0] - 2019-03-05
|
|
75
|
+
### Added
|
|
76
|
+
- Set original file names for multipart Happi files
|
|
77
|
+
|
|
78
|
+
[Unreleased]: https://github.com/rdytech/happi/compare/v1.0.0...HEAD
|
|
79
|
+
[1.0.0]: https://github.com/rdytech/happi/compare/v0.6.0...v1.0.0
|
|
80
|
+
[0.6.0]: https://github.com/rdytech/happi/compare/v0.4.0...v0.6.0
|
|
81
|
+
[0.4.0]: https://github.com/rdytech/happi/compare/v0.3.0...v0.4.0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## [0.3.0] - 2018-02-23
|
|
85
|
+
### Added
|
|
86
|
+
- Ability to set `token_type` in configuration and pass this into `FaradayMiddleware::OAuth2` to avoid `faraday_middleware` 0.11.0 warning about using the default token type. Set `token_type: 'bearer'` to only pass the oauth token as an Authorization header instead of both a header and query string parameter.
|
|
87
|
+
- This CHANGELOG.
|
|
88
|
+
|
|
89
|
+
[Unreleased]: https://github.com/rdytech/happi/compare/v0.3.0...HEAD
|
|
90
|
+
[0.3.0]: https://github.com/rdytech/happi/compare/v0.2.0...v0.3.0
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`happi` is a small Ruby gem (~200 LOC in `lib/`) wrapping Faraday for RESTful APIs whose
|
|
8
|
+
URLs look like `https://host/api/v1/resource`. It is a library, not an app — there is
|
|
9
|
+
nothing to run, only specs.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bundle install
|
|
15
|
+
bundle exec rspec # full suite
|
|
16
|
+
bundle exec rake spec # what CI runs
|
|
17
|
+
bundle exec rspec spec/client_spec.rb # single file
|
|
18
|
+
bundle exec rspec spec/client_spec.rb:105 # single example by line
|
|
19
|
+
gem build happi.gemspec # build the .gem
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Ruby 3.4.8 (`.ruby-version`); gemspec requires >= 3.2.0; GitHub Actions matrix covers
|
|
23
|
+
3.2 / 3.3 / 3.4 / 4.0. Note the system `ruby` on macOS is 2.6 and will fail — use a
|
|
24
|
+
version manager or the devcontainer (`mcr.microsoft.com/devcontainers/ruby:1-3.4-bullseye`).
|
|
25
|
+
|
|
26
|
+
No linter is wired up. `cane` is a dev dependency but has no rake task and no config.
|
|
27
|
+
SimpleCov runs automatically from `spec/spec_helper.rb` and writes to `coverage/` (gitignored).
|
|
28
|
+
|
|
29
|
+
`.buildkite/pipeline.yml` is dead — its script pins rbenv 2.1.5. CI of record is
|
|
30
|
+
`.github/workflows/ruby.yml`.
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
Five files, loaded in order by `lib/happi.rb`: `version`, `error`, `configuration`,
|
|
35
|
+
`client`, `file`.
|
|
36
|
+
|
|
37
|
+
### Config resolution is class-ivar based, and does NOT inherit
|
|
38
|
+
|
|
39
|
+
`Happi::Client.config` memoizes into `@global_config`, a **class-level instance variable**.
|
|
40
|
+
Subclasses get their own `@global_config`, initialized from `Happi::Configuration.defaults`
|
|
41
|
+
— they do *not* copy the parent's configured values. `Happi::Client#config` is
|
|
42
|
+
`self.class.config.dup` (shallow), so per-instance overrides via
|
|
43
|
+
`MyClient.new(host: ..., oauth_token: ...)` never leak back to the class.
|
|
44
|
+
|
|
45
|
+
Consequence, and the reason README leads with it: configuring `Happi::Client` itself is
|
|
46
|
+
global state shared by every consumer that hasn't subclassed. Always subclass. The
|
|
47
|
+
isolation matrix in `spec/client_spec.rb` (base vs derived × class vs instance) exists to
|
|
48
|
+
pin this down — preserve it when touching `config`.
|
|
49
|
+
|
|
50
|
+
### Request path
|
|
51
|
+
|
|
52
|
+
`get/post/patch/delete` → `param_check(params)` → `call` → `connection.send(method, ...)`
|
|
53
|
+
→ raise on mapped status → `.body.with_indifferent_access`.
|
|
54
|
+
|
|
55
|
+
- `url(resource)` hardcodes the `/api/#{config.version}/` prefix. Callers pass only the
|
|
56
|
+
resource path.
|
|
57
|
+
- `param_check` recurses hashes and swaps any value responding to `#multipart` for
|
|
58
|
+
`value.multipart` — this is the sole hook that turns a `Happi::File` into a
|
|
59
|
+
`Faraday::UploadIO`. Duck-typed on purpose; don't narrow it to `is_a?(Happi::File)`.
|
|
60
|
+
- `connection` is memoized per instance. Mutating `client.config` after the first request
|
|
61
|
+
has no effect.
|
|
62
|
+
|
|
63
|
+
### Faraday 2 migration (recent, load-bearing)
|
|
64
|
+
|
|
65
|
+
`FaradayMiddleware` is gone as of 1.0.0-rc2 — it was never compatible with Faraday 2.x and
|
|
66
|
+
was never actually a declared dependency. The OAuth2 Authorization header is now set by
|
|
67
|
+
hand in `connection`, only when `config.oauth_token.present?`, as
|
|
68
|
+
`"#{config.token_type.presence || 'Bearer'} #{config.oauth_token}"`. JSON uses Faraday's
|
|
69
|
+
built-in `:json` request/response middleware. `oauth2`, `multi_json`,
|
|
70
|
+
`faraday-follow_redirects`, and `faraday-http` were dropped; `activemodel` became
|
|
71
|
+
`activesupport`. Do not reintroduce `faraday_middleware`.
|
|
72
|
+
|
|
73
|
+
`token_type` is now interpolated verbatim as the auth scheme. In 0.6.0 it was a *mode*
|
|
74
|
+
selector for `FaradayMiddleware::OAuth2`, where `'param'` meant "send the token as an
|
|
75
|
+
`access_token` query parameter plus a `Token token="…"` header". That mode is gone —
|
|
76
|
+
`token_type: 'param'` now just emits the malformed header `Authorization: param <token>`.
|
|
77
|
+
Restoring it was considered for 1.0.0 and deliberately rejected; the breaking change is
|
|
78
|
+
documented in `CHANGELOG.md` for consumers to absorb. `context/TOKEN_TYPE_PARAM.md` is the
|
|
79
|
+
research behind that decision, not a work order.
|
|
80
|
+
|
|
81
|
+
`context/` holds the research notes from that migration (`faraday_2.x.md`,
|
|
82
|
+
`oauth2.md`, `FARADAY_2_COMPATIBILITY_REPORT.md`). Background reading, not specs.
|
|
83
|
+
|
|
84
|
+
## Known traps
|
|
85
|
+
|
|
86
|
+
- **`config.port` and `config.timeout` are inert.** `connection` calls
|
|
87
|
+
`Faraday.new(config.host)` with no options hash. Port must be embedded in `host`.
|
|
88
|
+
- **Response parsing is gated on an exact `application/json` content type.** `connection`
|
|
89
|
+
registers `f.response :json, content_type: 'application/json'` unconditionally — that
|
|
90
|
+
part is not affected by `use_json`, which selects the *request* encoding only. But the
|
|
91
|
+
match is string equality against the bare type, so a server answering
|
|
92
|
+
`application/vnd.api+json`, `text/json`, or no `Content-Type` leaves the body a `String`,
|
|
93
|
+
and `String` has no `with_indifferent_access` — every `get`/`post`/`patch`/`delete` then
|
|
94
|
+
raises `NoMethodError`. Widen the `content_type:` option rather than reaching for
|
|
95
|
+
`use_json`.
|
|
96
|
+
- **Error messages are static.** `Happi::Error` subclasses override `#message` with fixed
|
|
97
|
+
strings, discarding the API body passed to `new`. The real payload is only reachable via
|
|
98
|
+
`error.response`. Also `TooManyRequests` (429) is nested under `ServerError`, not
|
|
99
|
+
`ClientError`.
|
|
100
|
+
- **`Happi::Error::ServiceableErrors` is unreachable code.** `included` references
|
|
101
|
+
`::NestedError` (top-level) while the class is defined at
|
|
102
|
+
`ServiceableErrors::NestedError`, and its `initialize` defaults `original` to `$1`.
|
|
103
|
+
Nothing includes it; leave it alone unless deliberately fixing it.
|
|
104
|
+
- **`.github/copilot-instructions.md` is stale.** It still documents `oauth2`,
|
|
105
|
+
`activemodel`, and `FaradayMiddleware::OAuth2` as current. Prefer this file and the
|
|
106
|
+
gemspec.
|
|
107
|
+
- `Gemfile.lock` is gitignored (library gem) though present locally. `Gemfile.rails32` /
|
|
108
|
+
`Gemfile.rails41` are historical and unusable on the supported Ruby versions.
|
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Happi
|
|
2
2
|
|
|
3
|
-
Happi -
|
|
3
|
+
Happi is a pre-configured Faraday client designed for easy access to RESTful
|
|
4
|
+
HTTP APIs. It assumes URLS of the form `https://hostname.com/api/v1/something`.
|
|
4
5
|
|
|
5
6
|
## Installation
|
|
6
7
|
|
|
@@ -12,61 +13,80 @@ And then execute:
|
|
|
12
13
|
|
|
13
14
|
$ bundle
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
## Usage
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
Happi stores its configuration as class-level state, so it's important to
|
|
19
|
+
derive your own client from `Happi::Client` rather than using it directly,
|
|
20
|
+
which can cause requests to be sent to the wrong endpoint.
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
```ruby
|
|
23
|
+
require 'happi'
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
class MyClient < Happi::Client
|
|
26
|
+
end
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
MyClient.configure do |config|
|
|
29
|
+
config.host = 'http://localhost:3000'
|
|
30
|
+
end
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
client = Myclient.new(
|
|
33
|
+
oauth_token: '63ba06720acf97959f5ba3e3fe1020bf69a7596e2fe3091f821a35cdfe615ceb')
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
templates = client.get('templates')[:templates]
|
|
36
|
+
templates.each do |template|
|
|
37
|
+
puts template[:name]
|
|
38
|
+
end
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
response = client.post('templates', template: {name: 'test',
|
|
41
|
+
file: Happi::File.new(File.join(File.dirname(__FILE__), 'spec/fixtures/award.docx')) } )
|
|
42
|
+
template = response[:template]
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
templates = client.get('templates')[:templates]
|
|
45
|
+
template = client.get('templates/1')[:template]
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
template = client.patch("templates/#{template[:id]}",
|
|
48
|
+
template: {id: template[:id],
|
|
49
|
+
name: 'test' }
|
|
50
|
+
)[:template]
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
client.get('documents')[:documents].each do |document|
|
|
53
|
+
puts document[:name]
|
|
54
|
+
end
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
document = client.post('documents',
|
|
57
|
+
document: {template_id: template[:id],
|
|
58
|
+
name: 'Test Document',
|
|
59
|
+
params: JSON.dump({name: 'Test'}) } )[:document]
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
puts document[:id]
|
|
62
|
+
```
|
|
57
63
|
|
|
58
64
|
## Configuration
|
|
59
65
|
|
|
60
66
|
```ruby
|
|
61
|
-
|
|
67
|
+
MyClient.configure do |config|
|
|
62
68
|
config.host = 'http://localhost:8080'
|
|
63
69
|
config.port = 443
|
|
64
70
|
config.timeout = 60
|
|
65
71
|
config.version = 'v1'
|
|
66
72
|
config.use_json = false
|
|
73
|
+
config.log_level = :debug
|
|
67
74
|
end
|
|
68
75
|
```
|
|
69
76
|
|
|
77
|
+
A class deriving from `Happi::Client` can be configured with the following
|
|
78
|
+
paramters:
|
|
79
|
+
|
|
80
|
+
- **config.host** - the hostname of the server
|
|
81
|
+
- **config.port** - the TCP port to which requests will be sent.
|
|
82
|
+
- **config.timeout** - the maximum time to allow requests to take.
|
|
83
|
+
- **config.version** - the API version to interpolate in the URL
|
|
84
|
+
- **config.use_json** - when `true`, encode requests as JSON, and intepret
|
|
85
|
+
responses as JSON.
|
|
86
|
+
- **config.log_level** - when set to `:debug`, will log full request bodies and
|
|
87
|
+
paramaters, but will only log URL otherwise. Be aware: setting this to `:debug`
|
|
88
|
+
in data-heavy applications can lead to very large log files.
|
|
89
|
+
|
|
70
90
|
## Contributing
|
|
71
91
|
|
|
72
92
|
1. Fork it
|
data/happi.gemspec
CHANGED
|
@@ -10,22 +10,25 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
spec.email = ["john.dagostino@gmail.com"]
|
|
11
11
|
spec.description = %q{Simple faraday client preconfigured}
|
|
12
12
|
spec.summary = %q{Simple faraday client wrapper preconfigured for specific usecase}
|
|
13
|
-
spec.homepage = "
|
|
13
|
+
spec.homepage = "https://github.com/rdytech/happi"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = '>= 3.2.0'
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
# Ship only what a consumer needs at runtime. Specs (and their 2MB fixture),
|
|
18
|
+
# research notes and CI/editor config stay in the repo but out of the gem.
|
|
19
|
+
excluded_prefixes = %w[spec/ context/ .github/ .devcontainer/]
|
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
21
|
+
f.start_with?(*excluded_prefixes) || f.end_with?('.gem')
|
|
22
|
+
end
|
|
17
23
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
24
|
spec.require_paths = ["lib"]
|
|
20
25
|
|
|
21
|
-
spec.add_dependency 'faraday', '
|
|
22
|
-
spec.add_dependency '
|
|
23
|
-
spec.add_dependency '
|
|
24
|
-
spec.add_dependency '
|
|
25
|
-
spec.add_dependency 'mime-types', '>= 1.1.6'
|
|
26
|
-
spec.add_dependency 'multi_json', '~> 1.3'
|
|
26
|
+
spec.add_dependency 'faraday', '>= 1.10.0'
|
|
27
|
+
spec.add_dependency 'faraday-multipart', '~> 1.1'
|
|
28
|
+
spec.add_dependency 'activesupport', '>= 6.0'
|
|
29
|
+
spec.add_dependency 'mime-types', '>= 2.4.0'
|
|
27
30
|
|
|
28
|
-
spec.add_development_dependency 'bundler', '
|
|
31
|
+
spec.add_development_dependency 'bundler', '>= 2.0'
|
|
29
32
|
spec.add_development_dependency 'simplecov'
|
|
30
33
|
spec.add_development_dependency 'cane'
|
|
31
34
|
spec.add_development_dependency 'rake'
|
data/lib/happi/client.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
require 'logger'
|
|
1
2
|
require 'faraday'
|
|
2
|
-
require '
|
|
3
|
+
require 'faraday/multipart'
|
|
3
4
|
require 'active_support/core_ext/string/inflections'
|
|
4
5
|
require 'active_support/core_ext/hash'
|
|
5
6
|
|
|
@@ -94,13 +95,19 @@ class Happi::Client
|
|
|
94
95
|
|
|
95
96
|
def connection
|
|
96
97
|
@connection ||= Faraday.new(config.host) do |f|
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
# Set OAuth2 Authorization header
|
|
99
|
+
if config.oauth_token.present?
|
|
100
|
+
token_type = config.token_type.presence || 'Bearer'
|
|
101
|
+
f.headers['Authorization'] = "#{token_type} #{config.oauth_token}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Responses are parsed regardless of use_json, matching the unconditional
|
|
105
|
+
# FaradayMiddleware::ParseJson of 0.6.0. use_json only selects the request
|
|
106
|
+
# encoding - JSON body vs multipart/url-encoded.
|
|
107
|
+
f.response :json, content_type: 'application/json'
|
|
99
108
|
|
|
100
109
|
if self.config.use_json
|
|
101
|
-
f.use FaradayMiddleware::EncodeJson
|
|
102
110
|
f.request :json
|
|
103
|
-
f.response :json
|
|
104
111
|
else
|
|
105
112
|
f.request :multipart
|
|
106
113
|
f.request :url_encoded
|
data/lib/happi/configuration.rb
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
class Happi::Configuration
|
|
2
2
|
def self.defaults
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
{
|
|
4
|
+
host: 'http://localhost:8080',
|
|
5
|
+
port: 443,
|
|
6
|
+
timeout: 60,
|
|
7
|
+
version: 'v1',
|
|
8
|
+
use_json: false,
|
|
9
|
+
log_level: :info
|
|
10
10
|
}
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
attr_accessor :oauth_token,
|
|
13
|
+
attr_accessor :oauth_token,
|
|
14
|
+
:host,
|
|
15
|
+
:port,
|
|
16
|
+
:timeout,
|
|
17
|
+
:version,
|
|
18
|
+
:use_json,
|
|
19
|
+
:log_level,
|
|
20
|
+
:token_type
|
|
14
21
|
|
|
15
22
|
def initialize(options = {})
|
|
16
23
|
self.class.defaults.merge(options).each do |key, value|
|
data/lib/happi/error.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
class Happi::Error < StandardError
|
|
2
|
-
|
|
3
|
-
attr_reader :response
|
|
2
|
+
attr_reader :response
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
def initialize(msg = nil, response = nil)
|
|
5
|
+
super(msg)
|
|
6
|
+
@response = response
|
|
7
|
+
end
|
|
9
8
|
|
|
9
|
+
class ClientError < self
|
|
10
10
|
def message
|
|
11
11
|
"A client error occurred"
|
|
12
12
|
end
|
data/lib/happi/file.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
require 'mime/types'
|
|
2
|
+
require 'base64'
|
|
3
|
+
require 'faraday/multipart'
|
|
2
4
|
|
|
3
5
|
class Happi::File
|
|
4
6
|
attr_accessor :file_name
|
|
5
7
|
attr_accessor :mime_type
|
|
8
|
+
attr_accessor :original_filename
|
|
6
9
|
|
|
7
10
|
def initialize(file)
|
|
8
11
|
if file.is_a?(String)
|
|
@@ -11,15 +14,16 @@ class Happi::File
|
|
|
11
14
|
else
|
|
12
15
|
@mime_type = file.content_type
|
|
13
16
|
@file_name = file.path
|
|
17
|
+
@original_filename = file.original_filename
|
|
14
18
|
end
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def exists?
|
|
18
|
-
File.
|
|
22
|
+
File.exist?(file_name)
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
def multipart
|
|
22
|
-
Faraday::UploadIO.new(file_name, mime_type) if exists?
|
|
26
|
+
Faraday::UploadIO.new(file_name, mime_type, original_filename) if exists?
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
def encode_file
|
data/lib/happi/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,113 +1,84 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: happi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John D'Agostino
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: faraday
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
18
|
+
version: 1.10.0
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
|
-
- - "
|
|
23
|
+
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
25
|
+
version: 1.10.0
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
27
|
+
name: faraday-multipart
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
30
|
- - "~>"
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
32
|
+
version: '1.1'
|
|
34
33
|
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
39
|
+
version: '1.1'
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
41
|
+
name: activesupport
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
44
43
|
requirements:
|
|
45
44
|
- - ">="
|
|
46
45
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
46
|
+
version: '6.0'
|
|
48
47
|
type: :runtime
|
|
49
48
|
prerelease: false
|
|
50
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
50
|
requirements:
|
|
52
51
|
- - ">="
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: oauth2
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - "~>"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '1.0'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - "~>"
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '1.0'
|
|
53
|
+
version: '6.0'
|
|
69
54
|
- !ruby/object:Gem::Dependency
|
|
70
55
|
name: mime-types
|
|
71
56
|
requirement: !ruby/object:Gem::Requirement
|
|
72
57
|
requirements:
|
|
73
58
|
- - ">="
|
|
74
59
|
- !ruby/object:Gem::Version
|
|
75
|
-
version:
|
|
60
|
+
version: 2.4.0
|
|
76
61
|
type: :runtime
|
|
77
62
|
prerelease: false
|
|
78
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
64
|
requirements:
|
|
80
65
|
- - ">="
|
|
81
66
|
- !ruby/object:Gem::Version
|
|
82
|
-
version:
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: multi_json
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '1.3'
|
|
90
|
-
type: :runtime
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '1.3'
|
|
67
|
+
version: 2.4.0
|
|
97
68
|
- !ruby/object:Gem::Dependency
|
|
98
69
|
name: bundler
|
|
99
70
|
requirement: !ruby/object:Gem::Requirement
|
|
100
71
|
requirements:
|
|
101
|
-
- - "
|
|
72
|
+
- - ">="
|
|
102
73
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
74
|
+
version: '2.0'
|
|
104
75
|
type: :development
|
|
105
76
|
prerelease: false
|
|
106
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
78
|
requirements:
|
|
108
|
-
- - "
|
|
79
|
+
- - ">="
|
|
109
80
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '
|
|
81
|
+
version: '2.0'
|
|
111
82
|
- !ruby/object:Gem::Dependency
|
|
112
83
|
name: simplecov
|
|
113
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -186,7 +157,9 @@ extensions: []
|
|
|
186
157
|
extra_rdoc_files: []
|
|
187
158
|
files:
|
|
188
159
|
- ".gitignore"
|
|
189
|
-
- ".
|
|
160
|
+
- ".ruby-version"
|
|
161
|
+
- CHANGELOG.md
|
|
162
|
+
- CLAUDE.md
|
|
190
163
|
- Gemfile
|
|
191
164
|
- Gemfile.rails32
|
|
192
165
|
- Gemfile.rails41
|
|
@@ -200,16 +173,10 @@ files:
|
|
|
200
173
|
- lib/happi/error.rb
|
|
201
174
|
- lib/happi/file.rb
|
|
202
175
|
- lib/happi/version.rb
|
|
203
|
-
|
|
204
|
-
- spec/configuration_spec.rb
|
|
205
|
-
- spec/file_spec.rb
|
|
206
|
-
- spec/fixtures/award.docx
|
|
207
|
-
- spec/spec_helper.rb
|
|
208
|
-
homepage: http://github.com/jobready/happi
|
|
176
|
+
homepage: https://github.com/rdytech/happi
|
|
209
177
|
licenses:
|
|
210
178
|
- MIT
|
|
211
179
|
metadata: {}
|
|
212
|
-
post_install_message:
|
|
213
180
|
rdoc_options: []
|
|
214
181
|
require_paths:
|
|
215
182
|
- lib
|
|
@@ -217,22 +184,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
217
184
|
requirements:
|
|
218
185
|
- - ">="
|
|
219
186
|
- !ruby/object:Gem::Version
|
|
220
|
-
version:
|
|
187
|
+
version: 3.2.0
|
|
221
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
189
|
requirements:
|
|
223
190
|
- - ">="
|
|
224
191
|
- !ruby/object:Gem::Version
|
|
225
192
|
version: '0'
|
|
226
193
|
requirements: []
|
|
227
|
-
|
|
228
|
-
rubygems_version: 2.4.7
|
|
229
|
-
signing_key:
|
|
194
|
+
rubygems_version: 4.0.6
|
|
230
195
|
specification_version: 4
|
|
231
196
|
summary: Simple faraday client wrapper preconfigured for specific usecase
|
|
232
|
-
test_files:
|
|
233
|
-
- spec/client_spec.rb
|
|
234
|
-
- spec/configuration_spec.rb
|
|
235
|
-
- spec/file_spec.rb
|
|
236
|
-
- spec/fixtures/award.docx
|
|
237
|
-
- spec/spec_helper.rb
|
|
238
|
-
has_rdoc:
|
|
197
|
+
test_files: []
|
data/.travis.yml
DELETED
data/spec/client_spec.rb
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
class DerivedClient < Happi::Client; end
|
|
4
|
-
|
|
5
|
-
describe Happi::Client do
|
|
6
|
-
describe '.new' do
|
|
7
|
-
let(:default_port) { Happi::Configuration.defaults[:port] }
|
|
8
|
-
let(:class_level_port) { 80 }
|
|
9
|
-
let(:instance_level_port) { 8080 }
|
|
10
|
-
let(:derived_class_level_port) { 8081 }
|
|
11
|
-
let(:config_port) { described_class.new.config.port }
|
|
12
|
-
let(:derived_config_port) { DerivedClient.new.config.port }
|
|
13
|
-
|
|
14
|
-
context 'with no supplied config' do
|
|
15
|
-
it 'uses the default config for the base class' do
|
|
16
|
-
expect(config_port).to eq(default_port)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it 'uses the default config for the derived class' do
|
|
20
|
-
expect(derived_config_port).to eq(default_port)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
context 'with class level config for the base class' do
|
|
25
|
-
before do
|
|
26
|
-
described_class.configure { |config| config.port = class_level_port }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it 'overrides the default config for the base class' do
|
|
30
|
-
expect(config_port).to eq(class_level_port)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'does not override the default config for the derived class' do
|
|
34
|
-
expect(derived_config_port).to_not eq(class_level_port)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
context 'with class level config for the derived class' do
|
|
39
|
-
before do
|
|
40
|
-
DerivedClient.configure { |config| config.port = derived_class_level_port }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'does not affect the config for the base class' do
|
|
44
|
-
expect(config_port).to_not eq(derived_class_level_port)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it 'overrides the default config for the derived class' do
|
|
48
|
-
expect(derived_config_port).to eq(derived_class_level_port)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
context 'with instance level config for the base class' do
|
|
53
|
-
before do
|
|
54
|
-
described_class.configure { |config| config.port = class_level_port }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it 'overrides the class config for only that base class instance' do
|
|
58
|
-
expect(Happi::Client.new(port: instance_level_port).config.port).to eq(instance_level_port)
|
|
59
|
-
expect(config_port).to_not eq(instance_level_port)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'does not affect the config for an instance of the derived class' do
|
|
63
|
-
expect(derived_config_port).to_not eq(instance_level_port)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
context 'with instance level config for the derived class' do
|
|
68
|
-
before do
|
|
69
|
-
described_class.configure { |config| config.port = class_level_port }
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
it 'does not affect the config for an instance of the base class' do
|
|
73
|
-
expect(config_port).to_not eq(instance_level_port)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it 'overrides the class config for only that derived class instance' do
|
|
77
|
-
expect(DerivedClient.new(port: instance_level_port).config.port).to eq(instance_level_port)
|
|
78
|
-
expect(derived_config_port).to_not eq(instance_level_port)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
data/spec/configuration_spec.rb
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Happi::Configuration do
|
|
4
|
-
describe 'options' do
|
|
5
|
-
specify { expect(Happi::Configuration.new(host: 'http://www.google.com').host).to eql('http://www.google.com') }
|
|
6
|
-
specify { expect(Happi::Configuration.new(port: 80).port).to eql(80) }
|
|
7
|
-
|
|
8
|
-
specify do
|
|
9
|
-
Happi::Client.configure { |config| config.host = 'http://localhost:3000' }
|
|
10
|
-
expect(Happi::Client.config.host).to eq 'http://localhost:3000'
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
data/spec/file_spec.rb
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'rack/test'
|
|
3
|
-
|
|
4
|
-
describe Happi::File do
|
|
5
|
-
context "with a file name" do
|
|
6
|
-
subject { Happi::File.new(__FILE__) }
|
|
7
|
-
|
|
8
|
-
describe '#encode_file' do
|
|
9
|
-
encoded = Base64.encode64(File.read(__FILE__))
|
|
10
|
-
specify { expect(subject.encode_file).to eql(encoded) }
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
describe '#mime_type' do
|
|
14
|
-
specify { expect(subject.mime_type).to eql('application/x-ruby') }
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
describe '#multipart' do
|
|
18
|
-
# specify { expect(subject.multipart).to }
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
describe '#exists?' do
|
|
22
|
-
specify { expect(subject.exists?).to eq(true) }
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
context "with an ActionDispatch::Http::UploadedFile" do
|
|
27
|
-
subject { Happi::File.new(Rack::Test::UploadedFile.new(__FILE__, 'application/x-ruby')) }
|
|
28
|
-
|
|
29
|
-
describe '#encode_file' do
|
|
30
|
-
encoded = Base64.encode64(File.read(__FILE__))
|
|
31
|
-
specify { expect(subject.encode_file).to eql(encoded) }
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
describe '#mime_type' do
|
|
35
|
-
specify { expect(subject.mime_type).to eql('application/x-ruby') }
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
describe '#multipart' do
|
|
39
|
-
# specify { expect(subject.multipart).to }
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
describe '#exists?' do
|
|
43
|
-
specify { expect(subject.exists?).to eq(true) }
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
data/spec/fixtures/award.docx
DELETED
|
Binary file
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
require 'simplecov'
|
|
2
|
-
SimpleCov.start
|
|
3
|
-
|
|
4
|
-
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|
5
|
-
|
|
6
|
-
require 'rspec'
|
|
7
|
-
require 'happi'
|
|
8
|
-
|
|
9
|
-
Dir[File.expand_path('../../spec/support/**/*.rb', __FILE__)].map(&method(:require))
|
|
10
|
-
|
|
11
|
-
RSpec.configure do |config|
|
|
12
|
-
end
|