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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Rakefile +1 -0
  4. data/doc/client-examples.md +96 -0
  5. data/haveapi.gemspec +1 -1
  6. data/lib/haveapi/client_example.rb +47 -1
  7. data/lib/haveapi/client_examples/dynamic_helpers.rb +129 -0
  8. data/lib/haveapi/client_examples/go_client.rb +37 -0
  9. data/lib/haveapi/client_examples/js_async_client.rb +14 -0
  10. data/lib/haveapi/client_examples/js_client.rb +179 -135
  11. data/lib/haveapi/client_examples/python_async_client.rb +14 -0
  12. data/lib/haveapi/client_examples/python_client.rb +172 -0
  13. data/lib/haveapi/client_examples/rust_client.rb +59 -0
  14. data/lib/haveapi/client_examples/ts_async_client.rb +14 -0
  15. data/lib/haveapi/client_examples/ts_client.rb +14 -0
  16. data/lib/haveapi/documentation_clients.rb +126 -0
  17. data/lib/haveapi/documentation_context.rb +47 -0
  18. data/lib/haveapi/public/js/highlight-LICENSE +29 -0
  19. data/lib/haveapi/public/js/highlight.pack.js +2 -2
  20. data/lib/haveapi/public/js/highlighter.js +16 -6
  21. data/lib/haveapi/public/js/main.js +9 -1
  22. data/lib/haveapi/public/js/nojs-tabs.js +37 -3
  23. data/lib/haveapi/server.rb +34 -0
  24. data/lib/haveapi/version.rb +1 -1
  25. data/lib/haveapi/views/main_layout.erb +11 -0
  26. data/lib/haveapi/views/version_page/auth_body.erb +3 -3
  27. data/lib/haveapi/views/version_page/client_auth.erb +2 -2
  28. data/lib/haveapi/views/version_page/client_example.erb +2 -11
  29. data/lib/haveapi/views/version_page/client_init.erb +2 -2
  30. data/lib/haveapi/views/version_page/client_result.erb +5 -0
  31. data/lib/haveapi/views/version_page/resource_body.erb +3 -7
  32. data/lib/haveapi/views/version_page.erb +13 -9
  33. data/spec/client_examples/assets/highlighter_test.cjs +26 -0
  34. data/spec/client_examples/assets/tabs_test.cjs +54 -0
  35. data/spec/client_examples/context_spec.rb +68 -0
  36. data/spec/client_examples/dynamic_acceptance.md +83 -0
  37. data/spec/client_examples/dynamic_acceptance.rb +151 -0
  38. data/spec/client_examples/dynamic_acceptance_spec.rb +76 -0
  39. data/spec/client_examples/dynamic_fixture.rb +36 -0
  40. data/spec/client_examples/dynamic_js_spec.rb +195 -0
  41. data/spec/client_examples/dynamic_metadata_spec.rb +52 -0
  42. data/spec/client_examples/dynamic_mounted_paths_spec.rb +65 -0
  43. data/spec/client_examples/dynamic_python_spec.rb +123 -0
  44. data/spec/client_examples/go_spec.rb +57 -0
  45. data/spec/client_examples/profiles_spec.rb +95 -0
  46. data/spec/client_examples/rust_client_spec.rb +75 -0
  47. data/spec/client_examples/syntax_spec.rb +2 -1
  48. data/spec/documentation/auth_filtering_spec.rb +12 -0
  49. data/spec/documentation/client_examples_spec.rb +82 -0
  50. data/spec/packaging_spec.rb +13 -4
  51. data/test_support/dynamic_documentation_api.rb +171 -0
  52. data/test_support/dynamic_protocol_spec.rb +133 -0
  53. data/test_support/dynamic_python_login_spec.rb +75 -0
  54. metadata +34 -4
  55. 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.29.9
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.29.9
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.29.9
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/fs_client.rb
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