haveapi 0.29.12 → 0.30.1
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 +198 -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 +249 -0
- data/lib/haveapi/documentation_context.rb +47 -0
- data/lib/haveapi/documentation_schema.rb +73 -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 +39 -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 +196 -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/profile_mount_spec.rb +215 -0
- data/spec/client_examples/profiles_spec.rb +265 -0
- data/spec/client_examples/rust_client_spec.rb +75 -0
- data/spec/client_examples/structural_schema_spec.rb +48 -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 +92 -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 +37 -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.1
|
|
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.1
|
|
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.1
|
|
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,24 @@ 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
|
|
261
|
+
- lib/haveapi/documentation_schema.rb
|
|
251
262
|
- lib/haveapi/example.rb
|
|
252
263
|
- lib/haveapi/example_list.rb
|
|
253
264
|
- lib/haveapi/exceptions.rb
|
|
@@ -274,6 +285,7 @@ files:
|
|
|
274
285
|
- lib/haveapi/public/css/highlight.css
|
|
275
286
|
- lib/haveapi/public/doc/protocol.png
|
|
276
287
|
- lib/haveapi/public/js/bootstrap.min.js
|
|
288
|
+
- lib/haveapi/public/js/highlight-LICENSE
|
|
277
289
|
- lib/haveapi/public/js/highlight.pack.js
|
|
278
290
|
- lib/haveapi/public/js/highlighter.js
|
|
279
291
|
- lib/haveapi/public/js/jquery-1.11.1.min.js
|
|
@@ -315,6 +327,7 @@ files:
|
|
|
315
327
|
- lib/haveapi/views/version_page/client_auth.erb
|
|
316
328
|
- lib/haveapi/views/version_page/client_example.erb
|
|
317
329
|
- lib/haveapi/views/version_page/client_init.erb
|
|
330
|
+
- lib/haveapi/views/version_page/client_result.erb
|
|
318
331
|
- lib/haveapi/views/version_page/resource_body.erb
|
|
319
332
|
- lib/haveapi/views/version_sidebar.erb
|
|
320
333
|
- lib/haveapi/views/version_sidebar/auth_nav.erb
|
|
@@ -330,9 +343,26 @@ files:
|
|
|
330
343
|
- spec/authentication/token_spec.rb
|
|
331
344
|
- spec/authentication/token_version_routes_spec.rb
|
|
332
345
|
- spec/authorization_spec.rb
|
|
346
|
+
- spec/client_examples/assets/highlighter_test.cjs
|
|
347
|
+
- spec/client_examples/assets/tabs_test.cjs
|
|
348
|
+
- spec/client_examples/context_spec.rb
|
|
349
|
+
- spec/client_examples/dynamic_acceptance.md
|
|
350
|
+
- spec/client_examples/dynamic_acceptance.rb
|
|
351
|
+
- spec/client_examples/dynamic_acceptance_spec.rb
|
|
352
|
+
- spec/client_examples/dynamic_fixture.rb
|
|
353
|
+
- spec/client_examples/dynamic_js_spec.rb
|
|
354
|
+
- spec/client_examples/dynamic_metadata_spec.rb
|
|
355
|
+
- spec/client_examples/dynamic_mounted_paths_spec.rb
|
|
356
|
+
- spec/client_examples/dynamic_python_spec.rb
|
|
357
|
+
- spec/client_examples/go_spec.rb
|
|
358
|
+
- spec/client_examples/profile_mount_spec.rb
|
|
359
|
+
- spec/client_examples/profiles_spec.rb
|
|
360
|
+
- spec/client_examples/rust_client_spec.rb
|
|
361
|
+
- spec/client_examples/structural_schema_spec.rb
|
|
333
362
|
- spec/client_examples/syntax_spec.rb
|
|
334
363
|
- spec/common_spec.rb
|
|
335
364
|
- spec/documentation/auth_filtering_spec.rb
|
|
365
|
+
- spec/documentation/client_examples_spec.rb
|
|
336
366
|
- spec/documentation/current_user_html_escaping_spec.rb
|
|
337
367
|
- spec/documentation/examples_spec.rb
|
|
338
368
|
- spec/documentation/host_html_escaping_spec.rb
|
|
@@ -361,6 +391,9 @@ files:
|
|
|
361
391
|
- spec/validators/presence_spec.rb
|
|
362
392
|
- test_support/client_test_api.rb
|
|
363
393
|
- test_support/client_test_server.rb
|
|
394
|
+
- test_support/dynamic_documentation_api.rb
|
|
395
|
+
- test_support/dynamic_protocol_spec.rb
|
|
396
|
+
- test_support/dynamic_python_login_spec.rb
|
|
364
397
|
licenses:
|
|
365
398
|
- MIT
|
|
366
399
|
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
|