haveapi-go-client 0.30.2 → 0.31.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 +4 -4
- data/README.md +104 -57
- data/bin/haveapi-go-client +1 -1
- data/haveapi-go-client.gemspec +1 -1
- data/lib/haveapi/go_client/cli.rb +74 -32
- data/lib/haveapi/go_client/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/cli_spec.rb +83 -0
- data/spec/unit/generation_spec.rb +51 -0
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d4316e27400e3f4ef8ea4be0a7ea2cce84e55eb6c8db05b0ac42d0f2ca1631f
|
|
4
|
+
data.tar.gz: 5c010035b496b01576000055f63af17770f24255ca2bfa67364f1cb2e854eb78
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 445d8c7cf7d9e5271d81c6d05a39502c7b50c38668bb974657b1b7a8710540212cb5780cb0483ba1a6b7be2b364007f0b4d7bb92ee73983e42542e167dda5bf0
|
|
7
|
+
data.tar.gz: 918c4cba48ad68f3c3cb79c298fcff7520384499757714036d737e116f42f451f8c77befd0178c0924ef47b83ee13fac6ff8017f1a23450dc5381c9baeac70bf
|
data/README.md
CHANGED
|
@@ -1,85 +1,132 @@
|
|
|
1
|
-
|
|
2
|
-
-------------------------------
|
|
3
|
-
HaveAPI-Go-Client generates a client library for accessing selected APIs built
|
|
4
|
-
with the [HaveAPI framework](https://github.com/vpsfreecz/haveapi).
|
|
1
|
+
# Go client generator
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
Generate a Go client for one API version from a saved [HaveAPI](https://github.com/vpsfreecz/haveapi)
|
|
4
|
+
protocol 2 description. Capture the schema once, review it, then generate offline.
|
|
5
|
+
The resulting client has fixed resource names and routes; it does not discover
|
|
6
|
+
API changes at runtime.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Prepare the source tools
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
able to work with. The generated client uses one selected API version. When
|
|
13
|
-
the API server changes, the client has to be regenerated.
|
|
10
|
+
From the HaveAPI checkout root, enter its Nix environment and prepare the Go
|
|
11
|
+
component's Ruby dependencies:
|
|
14
12
|
|
|
13
|
+
```sh
|
|
14
|
+
nix develop
|
|
15
|
+
BUNDLE_GEMFILE="$PWD/clients/go/Gemfile" bundle install
|
|
15
16
|
```
|
|
16
|
-
$ haveapi-go-client -h
|
|
17
|
-
Usage: haveapi-go-client [options] <api url> <destination>
|
|
18
|
-
--version VERSION Use specified API version
|
|
19
|
-
--module MODULE Name of the generated Go module
|
|
20
|
-
--package PKG Name of the generated Go package
|
|
21
|
-
```
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
Keep that checkout and resolved component lock with your generation inputs.
|
|
19
|
+
The component bundle uses the Ruby client from the same checkout. Go must also
|
|
20
|
+
be on `PATH` for `go fmt`.
|
|
21
|
+
|
|
22
|
+
## Fetch, save and generate
|
|
23
|
+
|
|
24
|
+
The examples assume an API at `https://example.test/v1.0/` with a `Widget#Show`
|
|
25
|
+
action at `/v1.0/widgets/{widget_id}` and a string `name` output. Replace the
|
|
26
|
+
example URL, version and resource names with those of your API.
|
|
27
|
+
|
|
28
|
+
From the HaveAPI root:
|
|
24
29
|
|
|
30
|
+
```sh
|
|
31
|
+
mkdir -p /path/to/widget-client/schema
|
|
32
|
+
BUNDLE_GEMFILE="$PWD/clients/go/Gemfile" bundle exec ruby \
|
|
33
|
+
clients/go/bin/haveapi-go-client fetch https://example.test/v1.0/ \
|
|
34
|
+
--api-version 1.0 --profile public \
|
|
35
|
+
--output /path/to/widget-client/schema/api.json
|
|
36
|
+
BUNDLE_GEMFILE="$PWD/clients/go/Gemfile" bundle exec ruby \
|
|
37
|
+
clients/go/bin/haveapi-go-client generate \
|
|
38
|
+
--schema /path/to/widget-client/schema/api.json --api-version 1.0 \
|
|
39
|
+
--output /path/to/widget-client --module example.test/widget-client --package client
|
|
25
40
|
```
|
|
26
|
-
|
|
41
|
+
|
|
42
|
+
The output directory for the saved schema must already exist. Fetch sends OPTIONS
|
|
43
|
+
at the exact version URL, including any mount prefix; it never selects a server
|
|
44
|
+
default. Generation reads the saved schema, uses the existing Go emitter and
|
|
45
|
+
runs `go fmt`. It does not contact the API or install dependencies. The generated
|
|
46
|
+
module contains the `client` package; keep handwritten application files elsewhere.
|
|
47
|
+
|
|
48
|
+
Public capture is the default. For Basic authentication, set `WIDGET_PASSWORD`
|
|
49
|
+
in your environment, then run:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
BUNDLE_GEMFILE="$PWD/clients/go/Gemfile" bundle exec ruby \
|
|
53
|
+
clients/go/bin/haveapi-go-client fetch https://example.test/v1.0/ \
|
|
54
|
+
--api-version 1.0 --profile basic-user --basic-user demo \
|
|
55
|
+
--basic-password-env WIDGET_PASSWORD \
|
|
56
|
+
--output /path/to/widget-client/schema/api.json
|
|
27
57
|
```
|
|
28
58
|
|
|
29
|
-
|
|
30
|
-
The generated library can then be imported in your projects. For example,
|
|
31
|
-
in a project with the following `go.mod`:
|
|
59
|
+
For an existing HaveAPI token, set `WIDGET_TOKEN` and use:
|
|
32
60
|
|
|
61
|
+
```sh
|
|
62
|
+
BUNDLE_GEMFILE="$PWD/clients/go/Gemfile" bundle exec ruby \
|
|
63
|
+
clients/go/bin/haveapi-go-client fetch https://example.test/v1.0/ \
|
|
64
|
+
--api-version 1.0 --profile token-user --token-env WIDGET_TOKEN \
|
|
65
|
+
--output /path/to/widget-client/schema/api.json
|
|
33
66
|
```
|
|
34
|
-
module foo
|
|
35
|
-
```
|
|
36
67
|
|
|
37
|
-
The
|
|
68
|
+
The two authentication choices are mutually exclusive. Token capture first reads
|
|
69
|
+
anonymous discovery at the same version URL, then uses the advertised token
|
|
70
|
+
header. Supply a token you have already acquired. `--profile` labels the capture;
|
|
71
|
+
it does not change authentication. A snapshot covers the resources visible during
|
|
72
|
+
capture, which depend on permissions and enabled plugins.
|
|
73
|
+
Review saved defaults and data before sharing the schema. Credentials and tokens
|
|
74
|
+
must stay out of snapshots. Failed capture preserves an existing output file.
|
|
75
|
+
|
|
76
|
+
## Use the generated client
|
|
77
|
+
|
|
78
|
+
In a separate application, use the generated module locally:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
go mod init example.test/widget-app
|
|
82
|
+
go mod edit -require=example.test/widget-client@v0.0.0
|
|
83
|
+
go mod edit -replace=example.test/widget-client=/path/to/widget-client
|
|
84
|
+
```
|
|
38
85
|
|
|
39
86
|
```go
|
|
40
87
|
package main
|
|
41
88
|
|
|
42
89
|
import (
|
|
43
|
-
|
|
44
|
-
|
|
90
|
+
"fmt"
|
|
91
|
+
"log"
|
|
92
|
+
"example.test/widget-client/client"
|
|
45
93
|
)
|
|
46
94
|
|
|
47
95
|
func main() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
fmt.Printf("%+v\n", resp)
|
|
61
|
-
fmt.Printf("%+v\n", resp.Response)
|
|
62
|
-
fmt.Printf("%+v\n", resp.Response.Cluster)
|
|
63
|
-
fmt.Printf("%+v\n", resp.Output)
|
|
96
|
+
api := client.New("https://example.test")
|
|
97
|
+
action := api.Widget.Show.Prepare().SetPathParamInt("widget_id", 1)
|
|
98
|
+
resp, err := action.Call()
|
|
99
|
+
if err != nil {
|
|
100
|
+
log.Fatal(err)
|
|
101
|
+
}
|
|
102
|
+
if !resp.Status {
|
|
103
|
+
log.Fatal(resp.Message)
|
|
104
|
+
}
|
|
105
|
+
if resp.Output != nil {
|
|
106
|
+
fmt.Println(resp.Output.Name)
|
|
107
|
+
}
|
|
64
108
|
}
|
|
65
109
|
```
|
|
66
110
|
|
|
67
|
-
|
|
111
|
+
Give `client.New` only the API origin, such as `https://example.test`, without
|
|
112
|
+
a trailing slash. Generated action paths already contain the mount prefix and
|
|
113
|
+
API version. Authentication helpers
|
|
114
|
+
are emitted for advertised providers; a matching schema can use
|
|
115
|
+
`SetBasicAuthentication(user, password)` or `SetExistingTokenAuth(token)`.
|
|
116
|
+
Permissions and server validation still apply. `resp` retains the response
|
|
117
|
+
envelope; `resp.Output` provides the action output directly.
|
|
68
118
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```go
|
|
73
|
-
api := client.New("https://api.vpsfree.cz")
|
|
74
|
-
api.SetLanguage("cs")
|
|
75
|
-
```
|
|
119
|
+
Set `api.SetLanguage("cs")` to send `Accept-Language`.
|
|
120
|
+
`SetLanguageHeader("X-Language")` selects a custom header; `GetLanguage()` and
|
|
121
|
+
`GetLanguageHeader()` return the configured values.
|
|
76
122
|
|
|
77
|
-
|
|
78
|
-
`SetLanguageHeader("X-Language")` when the API is configured with a custom
|
|
79
|
-
language header. Read the configured values with `GetLanguage()` and
|
|
80
|
-
`GetLanguageHeader()`.
|
|
123
|
+
## Legacy invocation
|
|
81
124
|
|
|
82
|
-
|
|
125
|
+
The positional `haveapi-go-client [options] URL DESTINATION` command remains
|
|
126
|
+
available. Its optional `--version` still defaults to the server's default
|
|
127
|
+
version; `--basic-user` and raw `--basic-password` retain their old meaning.
|
|
128
|
+
Use the saved-schema workflow above for explicit, repeatable generation and
|
|
129
|
+
secrets supplied through environment variables.
|
|
83
130
|
|
|
84
|
-
The Ruby server can render examples
|
|
85
|
-
|
|
131
|
+
The Ruby server can render examples for configured generated clients; see
|
|
132
|
+
[documentation profiles](docs/documentation.md).
|
data/bin/haveapi-go-client
CHANGED
data/haveapi-go-client.gemspec
CHANGED
|
@@ -1,48 +1,90 @@
|
|
|
1
|
-
require 'haveapi/go_client'
|
|
2
1
|
require 'optparse'
|
|
3
2
|
|
|
4
|
-
module HaveAPI
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
module HaveAPI
|
|
4
|
+
module GoClient
|
|
5
|
+
class Cli
|
|
6
|
+
def self.run(argv = ARGV, out: $stdout, err: $stderr)
|
|
7
|
+
argv = argv.dup
|
|
8
|
+
if %w[fetch generate].include?(argv.first)
|
|
9
|
+
require_shared_schema
|
|
10
|
+
return HaveAPI::Schema::FetchCommand.run(argv.drop(1), out:, err:, command_name: 'haveapi-go-client') if argv.first == 'fetch'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
opts.on('--version VERSION', 'Use specified API version') do |v|
|
|
15
|
-
options[:version] = v
|
|
12
|
+
generate_saved(argv.drop(1), out:)
|
|
13
|
+
else
|
|
14
|
+
legacy(argv, out:)
|
|
16
15
|
end
|
|
16
|
+
0
|
|
17
|
+
rescue OptionParser::ParseError, SystemCallError, ArgumentError => e
|
|
18
|
+
err.puts("haveapi-go-client: #{e.message}")
|
|
19
|
+
1
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
raise unless defined?(HaveAPI::Schema::Error) && e.is_a?(HaveAPI::Schema::Error)
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
err.puts("haveapi-go-client: #{e.message}")
|
|
24
|
+
1
|
|
25
|
+
end
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
def self.require_shared_schema
|
|
28
|
+
require 'haveapi/schema'
|
|
29
|
+
return if defined?(HaveAPI::Schema::Snapshot) && defined?(HaveAPI::Schema::FetchCommand)
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
raise ArgumentError, 'loaded haveapi-client does not provide the required shared schema API'
|
|
32
|
+
rescue LoadError
|
|
33
|
+
raise ArgumentError, 'shared haveapi-client schema API is unavailable; prepare the component bundle from this source release'
|
|
34
|
+
end
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
def self.generate_saved(argv, out:)
|
|
37
|
+
options = { package: 'client' }
|
|
38
|
+
parser = OptionParser.new do |opts|
|
|
39
|
+
opts.banner = 'Usage: haveapi-go-client generate --schema FILE --api-version VERSION --output DIR [--module MODULE] [--package NAME]'
|
|
40
|
+
opts.on('--schema FILE') { |value| options[:schema] = value }
|
|
41
|
+
opts.on('--api-version VERSION') { |value| options[:version] = value }
|
|
42
|
+
opts.on('--output DIR') { |value| options[:output] = value }
|
|
43
|
+
opts.on('--module MODULE') { |value| options[:module] = value }
|
|
44
|
+
opts.on('--package NAME') { |value| options[:package] = value }
|
|
45
|
+
opts.on('-h', '--help') do
|
|
46
|
+
out.puts(opts)
|
|
47
|
+
return 0
|
|
48
|
+
end
|
|
32
49
|
end
|
|
33
|
-
|
|
50
|
+
parser.parse!(argv)
|
|
51
|
+
raise ArgumentError, 'generate requires --schema, --output and no positional arguments' unless argv.empty? && options[:schema] && options[:output]
|
|
34
52
|
|
|
35
|
-
|
|
53
|
+
HaveAPI::Schema::Snapshot.version!(options[:version])
|
|
54
|
+
snapshot = HaveAPI::Schema::Snapshot.load(options[:schema])
|
|
55
|
+
raise ArgumentError, 'requested API version differs from snapshot' unless snapshot.fetch('api_version') == options[:version]
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
require 'haveapi/go_client'
|
|
58
|
+
# The existing generator consumes the symbol-key JSON representation used
|
|
59
|
+
# by the dynamic Ruby communicator, including nested validator/Custom keys.
|
|
60
|
+
description = JSON.parse(JSON.generate(snapshot.fetch('description')), symbolize_names: true)
|
|
61
|
+
generator = Generator.from_description(description, options[:output], options)
|
|
62
|
+
generator.generate
|
|
63
|
+
generator.go_fmt
|
|
41
64
|
end
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
def self.legacy(argv, out:)
|
|
67
|
+
options = { package: 'client' }
|
|
68
|
+
parser = OptionParser.new do |opts|
|
|
69
|
+
opts.banner = 'Usage: haveapi-go-client [options] <api url> <destination>'
|
|
70
|
+
opts.on('--version VERSION', 'Use specified API version (otherwise server default)') { |value| options[:version] = value }
|
|
71
|
+
opts.on('--module MODULE') { |value| options[:module] = value }
|
|
72
|
+
opts.on('--package PKG') { |value| options[:package] = value }
|
|
73
|
+
opts.on('--basic-user USER') { |value| options[:basic_user] = value }
|
|
74
|
+
opts.on('--basic-password PASSWORD') { |value| options[:basic_password] = value }
|
|
75
|
+
opts.on('-h', '--help') do
|
|
76
|
+
out.puts(opts)
|
|
77
|
+
return 0
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
parser.parse!(argv)
|
|
81
|
+
raise ArgumentError, 'legacy mode requires an API URL and destination' unless argv.length == 2
|
|
82
|
+
|
|
83
|
+
require 'haveapi/go_client'
|
|
84
|
+
generator = Generator.new(argv[0], argv[1], options)
|
|
85
|
+
generator.generate
|
|
86
|
+
generator.go_fmt
|
|
87
|
+
end
|
|
46
88
|
end
|
|
47
89
|
end
|
|
48
90
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Deliberately avoid the component spec_helper: offline CLI checks need no server.
|
|
2
|
+
require 'stringio'
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
require 'haveapi/schema'
|
|
5
|
+
require 'haveapi/go_client/cli'
|
|
6
|
+
|
|
7
|
+
RSpec.describe HaveAPI::GoClient::Cli do
|
|
8
|
+
let(:err) { StringIO.new }
|
|
9
|
+
let(:generator) { double(generate: true, go_fmt: true) }
|
|
10
|
+
let(:generator_class) { Class.new }
|
|
11
|
+
let(:snapshot) do
|
|
12
|
+
HaveAPI::Schema::Snapshot.from_description(
|
|
13
|
+
api_version: '1.0', protocol_version: '2.0', source_url: 'https://example.test/v1.0/', profile: 'fixture',
|
|
14
|
+
description: {
|
|
15
|
+
'help' => '/v1.0/', 'meta' => { 'namespace' => '_meta' }, 'authentication' => {},
|
|
16
|
+
'resources' => { 'widget' => { 'actions' => { 'show' => {
|
|
17
|
+
'path' => '/v1.0/widgets/{widget_id}', 'method' => 'GET', 'aliases' => [], 'auth' => false,
|
|
18
|
+
'input' => { 'parameters' => {}, 'namespace' => 'widget', 'layout' => 'object' },
|
|
19
|
+
'output' => { 'parameters' => { 'name' => { 'type' => 'String' } }, 'namespace' => 'widget', 'layout' => 'object' },
|
|
20
|
+
'meta' => { 'global' => nil, 'object' => nil }
|
|
21
|
+
} }, 'resources' => {} } }
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
before do
|
|
27
|
+
stub_const('HaveAPI::GoClient::Generator', generator_class)
|
|
28
|
+
allow(described_class).to receive(:require).with('haveapi/schema').and_call_original
|
|
29
|
+
allow(described_class).to receive(:require).with('haveapi/go_client').and_return(true)
|
|
30
|
+
allow(generator_class).to receive_messages(new: generator, from_description: generator)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'delegates fetch options and status unchanged' do
|
|
34
|
+
args = ['https://example.test/v1.0/', '--api-version', '1.0', '--profile', 'public', '--output', 'schema.json']
|
|
35
|
+
out = StringIO.new
|
|
36
|
+
allow(HaveAPI::Schema::FetchCommand).to receive(:run).and_return(1)
|
|
37
|
+
expect(described_class.run(['fetch', *args], out:, err:)).to eq(1)
|
|
38
|
+
expect(HaveAPI::Schema::FetchCommand).to have_received(:run).with(args, out:, err:, command_name: 'haveapi-go-client')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'rejects missing and mismatched versions before generation or output mutation' do
|
|
42
|
+
Dir.mktmpdir do |root|
|
|
43
|
+
path = File.join(root, 'schema.json')
|
|
44
|
+
File.write(path, HaveAPI::Schema::Snapshot.canonical(snapshot))
|
|
45
|
+
args = ['generate', '--schema', path, '--output', File.join(root, 'out')]
|
|
46
|
+
expect(described_class.run(args, err:)).to eq(1)
|
|
47
|
+
expect(err.string).to include('explicit numeric API version')
|
|
48
|
+
expect(described_class.run(args + ['--api-version', '2.0'], err:)).to eq(1)
|
|
49
|
+
expect(err.string).to include('differs from snapshot')
|
|
50
|
+
expect(generator_class).not_to have_received(:from_description)
|
|
51
|
+
expect(Dir.children(root)).to eq(['schema.json'])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'passes recursively symbolized saved description to the existing generator then formats' do
|
|
56
|
+
Dir.mktmpdir do |root|
|
|
57
|
+
path = File.join(root, 'schema.json')
|
|
58
|
+
output = File.join(root, 'out')
|
|
59
|
+
File.write(path, HaveAPI::Schema::Snapshot.canonical(snapshot))
|
|
60
|
+
args = ['generate', '--schema', path, '--api-version', '1.0', '--output', output, '--module', 'example.test/widget', '--package', 'widgets']
|
|
61
|
+
expected = JSON.parse(JSON.generate(snapshot.fetch('description')), symbolize_names: true)
|
|
62
|
+
expect(described_class.run(args, err:)).to eq(0)
|
|
63
|
+
expect(generator_class).to have_received(:from_description).with(expected, output, hash_including(version: '1.0', module: 'example.test/widget', package: 'widgets'))
|
|
64
|
+
expect(generator).to have_received(:generate)
|
|
65
|
+
expect(generator).to have_received(:go_fmt)
|
|
66
|
+
expect(args.first).to eq('generate')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'retains positional legacy default-version and raw Basic options' do
|
|
71
|
+
expect(described_class.run(['--basic-user', 'demo', '--basic-password', 'test-only', 'https://example.test/', 'out'], err:)).to eq(0)
|
|
72
|
+
expect(generator_class).to have_received(:new).with('https://example.test/', 'out', { package: 'client', basic_user: 'demo', basic_password: 'test-only' })
|
|
73
|
+
expect(generator).to have_received(:generate)
|
|
74
|
+
expect(generator).to have_received(:go_fmt)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'retains the explicit legacy version and rejects raw password in saved mode' do
|
|
78
|
+
expect(described_class.run(['--version', '1.0', 'https://example.test/', 'out'], err:)).to eq(0)
|
|
79
|
+
expect(generator_class).to have_received(:new).with('https://example.test/', 'out', { package: 'client', version: '1.0' })
|
|
80
|
+
expect(described_class.run(['generate', '--basic-password', 'test-only'], err:)).to eq(1)
|
|
81
|
+
expect(err.string).to include('invalid option')
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Exercise the real emitter offline without the component's live-server helper.
|
|
2
|
+
require 'tmpdir'
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require 'haveapi/schema'
|
|
5
|
+
require 'haveapi/go_client'
|
|
6
|
+
|
|
7
|
+
RSpec.describe HaveAPI::GoClient::Generator do
|
|
8
|
+
it 'matches existing from_description bytes, formats Go and never describes a live API' do
|
|
9
|
+
description = {
|
|
10
|
+
'help' => '/v1.0/', 'meta' => { 'namespace' => '_meta' }, 'authentication' => {},
|
|
11
|
+
'resources' => { 'widget' => { 'actions' => { 'show' => {
|
|
12
|
+
'path' => '/v1.0/widgets/{widget_id}', 'method' => 'GET', 'aliases' => [], 'auth' => false,
|
|
13
|
+
'input' => { 'parameters' => {}, 'namespace' => 'widget', 'layout' => 'object' },
|
|
14
|
+
'output' => { 'parameters' => { 'name' => { 'type' => 'String' } }, 'namespace' => 'widget', 'layout' => 'object' },
|
|
15
|
+
'meta' => { 'global' => nil, 'object' => nil }
|
|
16
|
+
} }, 'resources' => {} } }
|
|
17
|
+
}
|
|
18
|
+
schema = HaveAPI::Schema::Snapshot.from_description(
|
|
19
|
+
description:, api_version: '1.0', protocol_version: '2.0', source_url: 'https://example.test/v1.0/', profile: 'fixture'
|
|
20
|
+
)
|
|
21
|
+
allow(HaveAPI::Client::Communicator).to receive(:new).and_raise('offline generation attempted discovery')
|
|
22
|
+
Dir.mktmpdir('haveapi-go-saved-') do |root|
|
|
23
|
+
path = File.join(root, 'schema.json')
|
|
24
|
+
File.write(path, HaveAPI::Schema::Snapshot.canonical(schema))
|
|
25
|
+
cli_output = File.join(root, 'cli')
|
|
26
|
+
direct_output = File.join(root, 'direct')
|
|
27
|
+
err = StringIO.new
|
|
28
|
+
status = HaveAPI::GoClient::Cli.run([
|
|
29
|
+
'generate', '--schema', path, '--api-version', '1.0', '--output', cli_output,
|
|
30
|
+
'--module', 'example.test/widget-client', '--package', 'client'
|
|
31
|
+
], err:)
|
|
32
|
+
expect(status).to eq(0), err.string
|
|
33
|
+
direct = described_class.from_description(
|
|
34
|
+
JSON.parse(JSON.generate(description), symbolize_names: true), direct_output,
|
|
35
|
+
module: 'example.test/widget-client', package: 'client'
|
|
36
|
+
)
|
|
37
|
+
direct.generate
|
|
38
|
+
direct.go_fmt
|
|
39
|
+
expect(file_bytes(cli_output)).to eq(file_bytes(direct_output))
|
|
40
|
+
sources = file_bytes(cli_output).values.join
|
|
41
|
+
expect(sources).to include('Widget *ResourceWidget', 'Show *ActionWidgetShow', 'func (inv *ActionWidgetShowInvocation) SetPathParamInt', 'Name string')
|
|
42
|
+
expect(HaveAPI::Client::Communicator).not_to have_received(:new)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def file_bytes(root)
|
|
47
|
+
Dir.glob('**/*', base: root).select { |path| File.file?(File.join(root, path)) }.to_h do |path|
|
|
48
|
+
[path, File.binread(File.join(root, path))]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haveapi-go-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.31.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakub Skokan
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.31.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.31.0
|
|
26
26
|
description: Go client generator
|
|
27
27
|
email:
|
|
28
28
|
- jakub.skokan@vpsfree.cz
|
|
@@ -73,7 +73,9 @@ files:
|
|
|
73
73
|
- spec/support/documentation_fixture.rb
|
|
74
74
|
- spec/support/documentation_profile_fixture.rb
|
|
75
75
|
- spec/support/test_server.rb
|
|
76
|
+
- spec/unit/cli_spec.rb
|
|
76
77
|
- spec/unit/documentation_spec.rb
|
|
78
|
+
- spec/unit/generation_spec.rb
|
|
77
79
|
- template/action.go.erb
|
|
78
80
|
- template/authentication.go.erb
|
|
79
81
|
- template/authentication/basic.go.erb
|