haveapi 0.29.9 → 0.30.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/.gitignore +1 -0
- data/Rakefile +1 -0
- data/doc/client-examples.md +96 -0
- data/haveapi.gemspec +1 -1
- data/lib/haveapi/client_example.rb +47 -1
- data/lib/haveapi/client_examples/dynamic_helpers.rb +129 -0
- data/lib/haveapi/client_examples/go_client.rb +37 -0
- data/lib/haveapi/client_examples/js_async_client.rb +14 -0
- data/lib/haveapi/client_examples/js_client.rb +179 -135
- data/lib/haveapi/client_examples/python_async_client.rb +14 -0
- data/lib/haveapi/client_examples/python_client.rb +172 -0
- data/lib/haveapi/client_examples/rust_client.rb +59 -0
- data/lib/haveapi/client_examples/ts_async_client.rb +14 -0
- data/lib/haveapi/client_examples/ts_client.rb +14 -0
- data/lib/haveapi/documentation_clients.rb +126 -0
- data/lib/haveapi/documentation_context.rb +47 -0
- data/lib/haveapi/public/js/highlight-LICENSE +29 -0
- data/lib/haveapi/public/js/highlight.pack.js +2 -2
- data/lib/haveapi/public/js/highlighter.js +16 -6
- data/lib/haveapi/public/js/main.js +9 -1
- data/lib/haveapi/public/js/nojs-tabs.js +37 -3
- data/lib/haveapi/server.rb +34 -0
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/main_layout.erb +11 -0
- data/lib/haveapi/views/version_page/auth_body.erb +3 -3
- data/lib/haveapi/views/version_page/client_auth.erb +2 -2
- data/lib/haveapi/views/version_page/client_example.erb +2 -11
- data/lib/haveapi/views/version_page/client_init.erb +2 -2
- data/lib/haveapi/views/version_page/client_result.erb +5 -0
- data/lib/haveapi/views/version_page/resource_body.erb +3 -7
- data/lib/haveapi/views/version_page.erb +13 -9
- data/spec/client_examples/assets/highlighter_test.cjs +26 -0
- data/spec/client_examples/assets/tabs_test.cjs +54 -0
- data/spec/client_examples/context_spec.rb +68 -0
- data/spec/client_examples/dynamic_acceptance.md +83 -0
- data/spec/client_examples/dynamic_acceptance.rb +151 -0
- data/spec/client_examples/dynamic_acceptance_spec.rb +76 -0
- data/spec/client_examples/dynamic_fixture.rb +36 -0
- data/spec/client_examples/dynamic_js_spec.rb +195 -0
- data/spec/client_examples/dynamic_metadata_spec.rb +52 -0
- data/spec/client_examples/dynamic_mounted_paths_spec.rb +65 -0
- data/spec/client_examples/dynamic_python_spec.rb +123 -0
- data/spec/client_examples/go_spec.rb +57 -0
- data/spec/client_examples/profiles_spec.rb +95 -0
- data/spec/client_examples/rust_client_spec.rb +75 -0
- data/spec/client_examples/syntax_spec.rb +2 -1
- data/spec/documentation/auth_filtering_spec.rb +12 -0
- data/spec/documentation/client_examples_spec.rb +82 -0
- data/spec/packaging_spec.rb +13 -4
- data/test_support/dynamic_documentation_api.rb +171 -0
- data/test_support/dynamic_protocol_spec.rb +133 -0
- data/test_support/dynamic_python_login_spec.rb +75 -0
- metadata +34 -4
- data/lib/haveapi/client_examples/fs_client.rb +0 -119
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'rack/mock'
|
|
4
|
+
require_relative 'dynamic_documentation_api'
|
|
5
|
+
require_relative '../spec/client_examples/dynamic_fixture'
|
|
6
|
+
|
|
7
|
+
RSpec.describe HaveAPI::ClientExamples::PythonClient do
|
|
8
|
+
include DynamicExampleFixture
|
|
9
|
+
|
|
10
|
+
[described_class, HaveAPI::ClientExamples::PythonAsyncClient].each do |renderer|
|
|
11
|
+
it "preserves actual required token input in the rendered #{renderer.id} mapping and native builder" do
|
|
12
|
+
request = Rack::MockRequest.new(DynamicDocumentationAPI.build.app)
|
|
13
|
+
response = request.options('http://example.test/mount/v2/')
|
|
14
|
+
expect(response.status).to eq(200), response.body
|
|
15
|
+
envelope = JSON.parse(response.body)
|
|
16
|
+
description = envelope.fetch('response')
|
|
17
|
+
context = dynamic_context(description:)
|
|
18
|
+
rendered = renderer.render(:auth, context:, auth_method: :token, auth_description: description.fetch('authentication').fetch('token'))
|
|
19
|
+
expect(rendered[:status]).to eq(:code), rendered.inspect
|
|
20
|
+
script = <<~PY
|
|
21
|
+
import ast
|
|
22
|
+
import json
|
|
23
|
+
import sys
|
|
24
|
+
from types import SimpleNamespace
|
|
25
|
+
from haveapi_client._protocol import CallContext, build_request, parse_description, parse_resources
|
|
26
|
+
from haveapi_client.exceptions import ValidationError
|
|
27
|
+
|
|
28
|
+
fixture = json.load(sys.stdin)
|
|
29
|
+
tree = ast.parse(fixture["source"])
|
|
30
|
+
calls = [node for node in ast.walk(tree) if isinstance(node, ast.Call)
|
|
31
|
+
and isinstance(node.func, ast.Attribute) and node.func.attr == "begin_token_login"]
|
|
32
|
+
assert len(calls) == 1
|
|
33
|
+
environment = {
|
|
34
|
+
"HAVEAPI_AUTH_user": json.dumps("fixture-user"),
|
|
35
|
+
"HAVEAPI_AUTH_password": json.dumps("fixture-password"),
|
|
36
|
+
"HAVEAPI_AUTH_scope": json.dumps("all"),
|
|
37
|
+
"HAVEAPI_AUTH_lifetime": json.dumps("fixed"),
|
|
38
|
+
}
|
|
39
|
+
expression = ast.Expression(body=calls[0].args[0])
|
|
40
|
+
params = eval(compile(expression, "<rendered-login-input>", "eval"),
|
|
41
|
+
{"json": json, "os": SimpleNamespace(environ=environment)})
|
|
42
|
+
description = parse_description(fixture["envelope"])
|
|
43
|
+
action = parse_resources(description.authentication["token"]["resources"])["token"].actions["request"]
|
|
44
|
+
assert action.input.parameters["lifetime"].required
|
|
45
|
+
assert "fixed" in action.input.parameters["lifetime"].raw["validators"]["include"]["values"]
|
|
46
|
+
assert not action.input.parameters["interval"].required
|
|
47
|
+
context = CallContext(action, (), {}, 1)
|
|
48
|
+
built = build_request(description, context, base_url=fixture["base_url"], params=params)
|
|
49
|
+
body = json.loads(built.content)
|
|
50
|
+
missing = dict(params)
|
|
51
|
+
del missing["lifetime"]
|
|
52
|
+
try:
|
|
53
|
+
build_request(description, context, base_url=fixture["base_url"], params=missing)
|
|
54
|
+
except ValidationError as error:
|
|
55
|
+
errors = {key: list(value) for key, value in error.errors.items()}
|
|
56
|
+
else:
|
|
57
|
+
raise AssertionError("Missing required lifetime was accepted")
|
|
58
|
+
print(json.dumps({"params": params, "body": body, "url": built.url, "missing_errors": errors}))
|
|
59
|
+
PY
|
|
60
|
+
root = File.expand_path('../../..', __dir__)
|
|
61
|
+
output, status = Open3.capture2e(
|
|
62
|
+
{ 'PYTHONPATH' => File.join(root, 'clients/python/src'), 'PYTHONDONTWRITEBYTECODE' => '1' },
|
|
63
|
+
File.join(root, 'clients/python/.venv/bin/python'), '-c', script,
|
|
64
|
+
stdin_data: JSON.generate({ envelope:, source: rendered.fetch(:code), base_url: context.base_url })
|
|
65
|
+
)
|
|
66
|
+
expect(status).to be_success, output
|
|
67
|
+
actual = JSON.parse(output)
|
|
68
|
+
expected = { 'user' => 'fixture-user', 'password' => 'fixture-password', 'scope' => 'all', 'lifetime' => 'fixed' }
|
|
69
|
+
expect(actual.fetch('params')).to eq(expected)
|
|
70
|
+
expect(actual.fetch('body')).to eq('token' => expected)
|
|
71
|
+
expect(actual.fetch('url')).to eq('https://api.example.test/mount/_auth/token/tokens')
|
|
72
|
+
expect(actual.fetch('missing_errors')).to eq('lifetime' => ['required parameter is missing'])
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haveapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.30.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakub Skokan
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.
|
|
32
|
+
version: 0.30.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.
|
|
39
|
+
version: 0.30.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: i18n
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -210,6 +210,7 @@ files:
|
|
|
210
210
|
- LICENSE.txt
|
|
211
211
|
- README.md
|
|
212
212
|
- Rakefile
|
|
213
|
+
- doc/client-examples.md
|
|
213
214
|
- doc/create-client.md
|
|
214
215
|
- doc/hooks.erb
|
|
215
216
|
- doc/index.md
|
|
@@ -240,14 +241,23 @@ files:
|
|
|
240
241
|
- lib/haveapi/authorization.rb
|
|
241
242
|
- lib/haveapi/client_example.rb
|
|
242
243
|
- lib/haveapi/client_examples/curl.rb
|
|
243
|
-
- lib/haveapi/client_examples/
|
|
244
|
+
- lib/haveapi/client_examples/dynamic_helpers.rb
|
|
245
|
+
- lib/haveapi/client_examples/go_client.rb
|
|
244
246
|
- lib/haveapi/client_examples/http.rb
|
|
247
|
+
- lib/haveapi/client_examples/js_async_client.rb
|
|
245
248
|
- lib/haveapi/client_examples/js_client.rb
|
|
246
249
|
- lib/haveapi/client_examples/php_client.rb
|
|
250
|
+
- lib/haveapi/client_examples/python_async_client.rb
|
|
251
|
+
- lib/haveapi/client_examples/python_client.rb
|
|
247
252
|
- lib/haveapi/client_examples/ruby_cli.rb
|
|
248
253
|
- lib/haveapi/client_examples/ruby_client.rb
|
|
254
|
+
- lib/haveapi/client_examples/rust_client.rb
|
|
255
|
+
- lib/haveapi/client_examples/ts_async_client.rb
|
|
256
|
+
- lib/haveapi/client_examples/ts_client.rb
|
|
249
257
|
- lib/haveapi/common.rb
|
|
250
258
|
- lib/haveapi/context.rb
|
|
259
|
+
- lib/haveapi/documentation_clients.rb
|
|
260
|
+
- lib/haveapi/documentation_context.rb
|
|
251
261
|
- lib/haveapi/example.rb
|
|
252
262
|
- lib/haveapi/example_list.rb
|
|
253
263
|
- lib/haveapi/exceptions.rb
|
|
@@ -274,6 +284,7 @@ files:
|
|
|
274
284
|
- lib/haveapi/public/css/highlight.css
|
|
275
285
|
- lib/haveapi/public/doc/protocol.png
|
|
276
286
|
- lib/haveapi/public/js/bootstrap.min.js
|
|
287
|
+
- lib/haveapi/public/js/highlight-LICENSE
|
|
277
288
|
- lib/haveapi/public/js/highlight.pack.js
|
|
278
289
|
- lib/haveapi/public/js/highlighter.js
|
|
279
290
|
- lib/haveapi/public/js/jquery-1.11.1.min.js
|
|
@@ -315,6 +326,7 @@ files:
|
|
|
315
326
|
- lib/haveapi/views/version_page/client_auth.erb
|
|
316
327
|
- lib/haveapi/views/version_page/client_example.erb
|
|
317
328
|
- lib/haveapi/views/version_page/client_init.erb
|
|
329
|
+
- lib/haveapi/views/version_page/client_result.erb
|
|
318
330
|
- lib/haveapi/views/version_page/resource_body.erb
|
|
319
331
|
- lib/haveapi/views/version_sidebar.erb
|
|
320
332
|
- lib/haveapi/views/version_sidebar/auth_nav.erb
|
|
@@ -330,9 +342,24 @@ files:
|
|
|
330
342
|
- spec/authentication/token_spec.rb
|
|
331
343
|
- spec/authentication/token_version_routes_spec.rb
|
|
332
344
|
- spec/authorization_spec.rb
|
|
345
|
+
- spec/client_examples/assets/highlighter_test.cjs
|
|
346
|
+
- spec/client_examples/assets/tabs_test.cjs
|
|
347
|
+
- spec/client_examples/context_spec.rb
|
|
348
|
+
- spec/client_examples/dynamic_acceptance.md
|
|
349
|
+
- spec/client_examples/dynamic_acceptance.rb
|
|
350
|
+
- spec/client_examples/dynamic_acceptance_spec.rb
|
|
351
|
+
- spec/client_examples/dynamic_fixture.rb
|
|
352
|
+
- spec/client_examples/dynamic_js_spec.rb
|
|
353
|
+
- spec/client_examples/dynamic_metadata_spec.rb
|
|
354
|
+
- spec/client_examples/dynamic_mounted_paths_spec.rb
|
|
355
|
+
- spec/client_examples/dynamic_python_spec.rb
|
|
356
|
+
- spec/client_examples/go_spec.rb
|
|
357
|
+
- spec/client_examples/profiles_spec.rb
|
|
358
|
+
- spec/client_examples/rust_client_spec.rb
|
|
333
359
|
- spec/client_examples/syntax_spec.rb
|
|
334
360
|
- spec/common_spec.rb
|
|
335
361
|
- spec/documentation/auth_filtering_spec.rb
|
|
362
|
+
- spec/documentation/client_examples_spec.rb
|
|
336
363
|
- spec/documentation/current_user_html_escaping_spec.rb
|
|
337
364
|
- spec/documentation/examples_spec.rb
|
|
338
365
|
- spec/documentation/host_html_escaping_spec.rb
|
|
@@ -361,6 +388,9 @@ files:
|
|
|
361
388
|
- spec/validators/presence_spec.rb
|
|
362
389
|
- test_support/client_test_api.rb
|
|
363
390
|
- test_support/client_test_server.rb
|
|
391
|
+
- test_support/dynamic_documentation_api.rb
|
|
392
|
+
- test_support/dynamic_protocol_spec.rb
|
|
393
|
+
- test_support/dynamic_python_login_spec.rb
|
|
364
394
|
licenses:
|
|
365
395
|
- MIT
|
|
366
396
|
metadata: {}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
require 'haveapi/client_example'
|
|
2
|
-
|
|
3
|
-
module HaveAPI::ClientExamples
|
|
4
|
-
class FsClient < HaveAPI::ClientExample
|
|
5
|
-
label 'File system'
|
|
6
|
-
code :bash
|
|
7
|
-
order 40
|
|
8
|
-
|
|
9
|
-
def init
|
|
10
|
-
"# Mount the file system\n$ haveapi-fs #{base_url} #{mountpoint} -o version=#{version}"
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def auth(method, desc)
|
|
14
|
-
case method
|
|
15
|
-
when :basic
|
|
16
|
-
<<~END
|
|
17
|
-
# Provide credentials as file system options
|
|
18
|
-
#{init} -o auth_method=basic,user=myuser,password=secret
|
|
19
|
-
|
|
20
|
-
# If username or password isn't provided, the user is asked on stdin
|
|
21
|
-
#{init} -o auth_method=basic,user=myuser
|
|
22
|
-
Password: secret
|
|
23
|
-
END
|
|
24
|
-
|
|
25
|
-
when :token
|
|
26
|
-
<<~END
|
|
27
|
-
# Authenticate using username and password
|
|
28
|
-
#{init} -o auth_method=token,user=myuser
|
|
29
|
-
Password: secret
|
|
30
|
-
|
|
31
|
-
# If you have generated a token, you can use it
|
|
32
|
-
#{init} -o auth_method=token,token=yourtoken
|
|
33
|
-
|
|
34
|
-
# Note that the file system can read config file from haveapi-client, so if
|
|
35
|
-
# you set up authentication there, the file system will use it.
|
|
36
|
-
END
|
|
37
|
-
|
|
38
|
-
when :oauth2
|
|
39
|
-
'# OAuth2 is not supported by haveapi-fs.'
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def example(sample)
|
|
44
|
-
cmd = [init]
|
|
45
|
-
|
|
46
|
-
path = [mountpoint].concat(resource_path)
|
|
47
|
-
|
|
48
|
-
unless class_action?
|
|
49
|
-
if !sample[:path_params] || sample[:path_params].empty?
|
|
50
|
-
raise "example {#{sample}} of action #{resource_path.join('.')}.#{action_name} is for an instance action but does not include URL parameters"
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
path << sample[:path_params].first.to_s
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
path << 'actions' << action_name
|
|
57
|
-
|
|
58
|
-
cmd << "\n# Change to action directory"
|
|
59
|
-
cmd << "$ cd #{File.join(path)}"
|
|
60
|
-
|
|
61
|
-
if sample[:request] && !sample[:request].empty?
|
|
62
|
-
cmd << "\n# Prepare input parameters"
|
|
63
|
-
|
|
64
|
-
sample[:request].each do |k, v|
|
|
65
|
-
cmd << "$ echo '#{v}' > input/#{k}"
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
cmd << "\n# Execute the action"
|
|
70
|
-
cmd << '$ echo 1 > exec'
|
|
71
|
-
|
|
72
|
-
cmd << "\n# Query the action's result"
|
|
73
|
-
cmd << '$ cat status'
|
|
74
|
-
cmd << (sample[:status] ? '1' : '0')
|
|
75
|
-
|
|
76
|
-
if sample[:status]
|
|
77
|
-
if sample[:response] && !sample[:response].empty? \
|
|
78
|
-
&& %i[hash object].include?(action[:output][:layout])
|
|
79
|
-
cmd << "\n# Query the output parameters"
|
|
80
|
-
|
|
81
|
-
sample[:response].each do |k, v|
|
|
82
|
-
cmd << "$ cat output/#{k}"
|
|
83
|
-
|
|
84
|
-
cmd << if v === true
|
|
85
|
-
'1'
|
|
86
|
-
|
|
87
|
-
elsif v === false
|
|
88
|
-
'0'
|
|
89
|
-
|
|
90
|
-
else
|
|
91
|
-
v.to_s
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
cmd << "\n"
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
else
|
|
99
|
-
cmd << "\n# Get the error message"
|
|
100
|
-
cmd << '$ cat message'
|
|
101
|
-
cmd << sample[:message]
|
|
102
|
-
|
|
103
|
-
cmd << "\n# Parameter errors can be seen in the `errors` directory"
|
|
104
|
-
cmd << '$ ls errors'
|
|
105
|
-
cmd << (sample[:errors] || {}).keys.join("\n")
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
cmd.join("\n")
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def mountpoint
|
|
112
|
-
"/mnt/#{host}"
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def class_action?
|
|
116
|
-
action[:path].index(/:[a-zA-Z\-_]+/).nil?
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|