haveapi 0.22.0 → 0.23.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/haveapi.gemspec +1 -1
- data/lib/haveapi/client_examples/php_client.rb +6 -9
- data/lib/haveapi/client_examples/ruby_client.rb +1 -1
- data/lib/haveapi/resource.rb +2 -2
- data/lib/haveapi/resources/action_state.rb +2 -5
- data/lib/haveapi/server.rb +18 -0
- data/lib/haveapi/spec/mock_action.rb +2 -2
- data/lib/haveapi/spec/spec_methods.rb +2 -2
- data/lib/haveapi/version.rb +1 -1
- metadata +3 -8
- data/doc/Hooks.md +0 -81
- data/doc/create-client.md +0 -107
- data/doc/json-schema.html +0 -1189
- data/doc/protocol.md +0 -535
- data/doc/protocol.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c66cdf4f10a5d8d65b70c4d934cd4ab98b96d89fd5af283cdd1d0cd21cbb4b8
|
4
|
+
data.tar.gz: d3beeb71e1da7049735864c44e76abdd512fe22fbec0df056d10f2f0ad9dc409
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a139c2b9bfc85f476a77c8e113d8e9d769dc505261028d807a4804dc108e1dee07dfa65f54220988b28bc78d4893fde0cbad37fee9e0eae01d697a3c58719b88
|
7
|
+
data.tar.gz: 18525da72a705a5bd8f955b5597f87035bde722fe3520abd2db09e6fac9066cb01eafffb39dbc1f480504568b60852388144e4245861a3d2d2590ba69f7eb790
|
data/haveapi.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.add_runtime_dependency 'activesupport', '>= 7.1'
|
18
18
|
s.add_runtime_dependency 'github-markdown'
|
19
|
-
s.add_runtime_dependency 'haveapi-client', '~> 0.
|
19
|
+
s.add_runtime_dependency 'haveapi-client', '~> 0.23.0'
|
20
20
|
s.add_runtime_dependency 'json'
|
21
21
|
s.add_runtime_dependency 'mail'
|
22
22
|
s.add_runtime_dependency 'nesty', '~> 1.0'
|
@@ -147,15 +147,12 @@ module HaveAPI::ClientExamples
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def format_parameters(dir, params, prefix = '')
|
150
|
-
ret =
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
else
|
157
|
-
"#{prefix} \"#{k}\" => #{value(v)}"
|
158
|
-
end
|
150
|
+
ret = params.map do |k, v|
|
151
|
+
if action[dir][:parameters][k][:type] == 'Custom'
|
152
|
+
"#{prefix} \"#{k}\" => custom type}"
|
153
|
+
else
|
154
|
+
"#{prefix} \"#{k}\" => #{value(v)}"
|
155
|
+
end
|
159
156
|
end
|
160
157
|
|
161
158
|
"#{prefix}[\n#{ret.join(",\n")}\n#{prefix}]"
|
@@ -80,7 +80,7 @@ module HaveAPI::ClientExamples
|
|
80
80
|
out << "# reply is an instance of HaveAPI::Client::ResourceInstance\n"
|
81
81
|
|
82
82
|
(sample[:response] || {}).each do |pn, pv|
|
83
|
-
param = action[:output][:parameters][
|
83
|
+
param = action[:output][:parameters][pn]
|
84
84
|
|
85
85
|
if param[:type] == 'Resource'
|
86
86
|
out << "# reply.#{pn} = HaveAPI::Client::ResourceInstance("
|
data/lib/haveapi/resource.rb
CHANGED
@@ -116,7 +116,7 @@ module HaveAPI
|
|
116
116
|
cls
|
117
117
|
end
|
118
118
|
|
119
|
-
def self.define_action(name, superclass: Action, &
|
119
|
+
def self.define_action(name, superclass: Action, &)
|
120
120
|
return false if const_defined?(name)
|
121
121
|
|
122
122
|
cls = Class.new(superclass)
|
@@ -124,7 +124,7 @@ module HaveAPI
|
|
124
124
|
cls.resource = self
|
125
125
|
cls.action_name = name
|
126
126
|
superclass.delayed_inherited(cls)
|
127
|
-
cls.class_exec(&
|
127
|
+
cls.class_exec(&)
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
@@ -61,7 +61,6 @@ module HaveAPI::Resources
|
|
61
61
|
authorize { allow }
|
62
62
|
|
63
63
|
def exec
|
64
|
-
ret = []
|
65
64
|
actions = @context.server.action_state.list_pending(
|
66
65
|
current_user,
|
67
66
|
input[:offset],
|
@@ -69,11 +68,9 @@ module HaveAPI::Resources
|
|
69
68
|
input[:order].to_sym
|
70
69
|
)
|
71
70
|
|
72
|
-
actions.
|
73
|
-
|
71
|
+
actions.map do |state|
|
72
|
+
state_to_hash(state)
|
74
73
|
end
|
75
|
-
|
76
|
-
ret
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
data/lib/haveapi/server.rb
CHANGED
@@ -11,6 +11,20 @@ module HaveAPI
|
|
11
11
|
|
12
12
|
include Hookable
|
13
13
|
|
14
|
+
has_hook :pre_mount,
|
15
|
+
desc: 'Called before API actions are mounted in sinatra',
|
16
|
+
args: {
|
17
|
+
server: 'HaveAPI::Server',
|
18
|
+
sinatra: 'Sinatra::Base'
|
19
|
+
}
|
20
|
+
|
21
|
+
has_hook :post_mount,
|
22
|
+
desc: 'Called after API actions are mounted in sinatra',
|
23
|
+
args: {
|
24
|
+
server: 'HaveAPI::Server',
|
25
|
+
sinatra: 'Sinatra::Base'
|
26
|
+
}
|
27
|
+
|
14
28
|
# Called after the user was authenticated (or not). The block is passed
|
15
29
|
# current user object or nil as an argument.
|
16
30
|
has_hook :post_authenticated,
|
@@ -339,12 +353,16 @@ module HaveAPI
|
|
339
353
|
|
340
354
|
@extensions.each { |e| e.enabled(self) }
|
341
355
|
|
356
|
+
call_hooks_for(:pre_mount, args: [self, @sinatra])
|
357
|
+
|
342
358
|
# Mount default version first
|
343
359
|
mount_version(@root, @default_version)
|
344
360
|
|
345
361
|
@versions.each do |v|
|
346
362
|
mount_version(version_prefix(v), v)
|
347
363
|
end
|
364
|
+
|
365
|
+
call_hooks_for(:post_mount, args: [self, @sinatra])
|
348
366
|
end
|
349
367
|
|
350
368
|
def mount_version(prefix, v)
|
@@ -8,7 +8,7 @@ module HaveAPI::Spec
|
|
8
8
|
@v = v
|
9
9
|
end
|
10
10
|
|
11
|
-
def call(input, user: nil, &
|
11
|
+
def call(input, user: nil, &)
|
12
12
|
action = @action.new(nil, @v, input, nil, HaveAPI::Context.new(
|
13
13
|
@server,
|
14
14
|
version: @v,
|
@@ -26,7 +26,7 @@ module HaveAPI::Spec
|
|
26
26
|
status, data, errors = action.safe_exec
|
27
27
|
raise(data || 'action failed') unless status
|
28
28
|
|
29
|
-
action.instance_exec(@test, &
|
29
|
+
action.instance_exec(@test, &)
|
30
30
|
data
|
31
31
|
end
|
32
32
|
end
|
@@ -74,12 +74,12 @@ module HaveAPI::Spec
|
|
74
74
|
# @param version [any] API version, if not specified, the default version is used
|
75
75
|
# @param user [any] object representing authenticated user
|
76
76
|
# @yield [self] the block is executed in the action instance
|
77
|
-
def mock_action(r_name, a_name, params, version: nil, user: nil, &
|
77
|
+
def mock_action(r_name, a_name, params, version: nil, user: nil, &)
|
78
78
|
app
|
79
79
|
v = version || @api.default_version
|
80
80
|
action, path = find_action(v, r_name, a_name)
|
81
81
|
m = MockAction.new(self, @api, action, path, v)
|
82
|
-
m.call(params, user:, &
|
82
|
+
m.call(params, user:, &)
|
83
83
|
end
|
84
84
|
|
85
85
|
# Return parsed API response.
|
data/lib/haveapi/version.rb
CHANGED
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.23.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Skokan
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.23.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.23.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,13 +204,8 @@ files:
|
|
204
204
|
- LICENSE.txt
|
205
205
|
- README.md
|
206
206
|
- Rakefile
|
207
|
-
- doc/Hooks.md
|
208
|
-
- doc/create-client.md
|
209
207
|
- doc/hooks.erb
|
210
208
|
- doc/index.md
|
211
|
-
- doc/json-schema.html
|
212
|
-
- doc/protocol.md
|
213
|
-
- doc/protocol.png
|
214
209
|
- haveapi.gemspec
|
215
210
|
- lib/haveapi.rb
|
216
211
|
- lib/haveapi/action.rb
|
data/doc/Hooks.md
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
|
2
|
-
# Hooks
|
3
|
-
|
4
|
-
##HaveAPI::Server
|
5
|
-
|
6
|
-
### post_authenticated
|
7
|
-
<table>
|
8
|
-
<tr>
|
9
|
-
<td style="vertical-align: top;">Description:</td>
|
10
|
-
<td>Called after the user was authenticated</td>
|
11
|
-
</tr>
|
12
|
-
<tr>
|
13
|
-
<td style="vertical-align: top;">Context:</td>
|
14
|
-
<td>current</td>
|
15
|
-
</tr>
|
16
|
-
<tr>
|
17
|
-
<td style="vertical-align: top;">Arguments:</td>
|
18
|
-
<td><dl><dt>current_user</dt><dd>object returned by the authentication backend</dd></dl></td>
|
19
|
-
</tr>
|
20
|
-
<tr>
|
21
|
-
<td style="vertical-align: top;">Initial value:</td>
|
22
|
-
<td>none</td>
|
23
|
-
</tr>
|
24
|
-
<tr>
|
25
|
-
<td style="vertical-align: top;">Return value:</td>
|
26
|
-
<td>none</td>
|
27
|
-
</tr>
|
28
|
-
</table>
|
29
|
-
|
30
|
-
### description_exception
|
31
|
-
<table>
|
32
|
-
<tr>
|
33
|
-
<td style="vertical-align: top;">Description:</td>
|
34
|
-
<td>Called when an exception occurs when building self-description</td>
|
35
|
-
</tr>
|
36
|
-
<tr>
|
37
|
-
<td style="vertical-align: top;">Context:</td>
|
38
|
-
<td>current</td>
|
39
|
-
</tr>
|
40
|
-
<tr>
|
41
|
-
<td style="vertical-align: top;">Arguments:</td>
|
42
|
-
<td><dl><dt>context</dt><dd>HaveAPI::Context</dd><dt>exception</dt><dd>exception instance</dd></dl></td>
|
43
|
-
</tr>
|
44
|
-
<tr>
|
45
|
-
<td style="vertical-align: top;">Initial value:</td>
|
46
|
-
<td>none</td>
|
47
|
-
</tr>
|
48
|
-
<tr>
|
49
|
-
<td style="vertical-align: top;">Return value:</td>
|
50
|
-
<td><dl><dt>http_status</dt><dd>HTTP status code to send to client</dd><dt>message</dt><dd>error message sent to the client</dd></dl></td>
|
51
|
-
</tr>
|
52
|
-
</table>
|
53
|
-
|
54
|
-
|
55
|
-
##HaveAPI::Action
|
56
|
-
|
57
|
-
### exec_exception
|
58
|
-
<table>
|
59
|
-
<tr>
|
60
|
-
<td style="vertical-align: top;">Description:</td>
|
61
|
-
<td>Called when unhandled exceptions occurs during Action.exec</td>
|
62
|
-
</tr>
|
63
|
-
<tr>
|
64
|
-
<td style="vertical-align: top;">Context:</td>
|
65
|
-
<td>current</td>
|
66
|
-
</tr>
|
67
|
-
<tr>
|
68
|
-
<td style="vertical-align: top;">Arguments:</td>
|
69
|
-
<td><dl><dt>action</dt><dd>HaveAPI::Action instance</dd><dt>exception</dt><dd>exception instance</dd></dl></td>
|
70
|
-
</tr>
|
71
|
-
<tr>
|
72
|
-
<td style="vertical-align: top;">Initial value:</td>
|
73
|
-
<td>none</td>
|
74
|
-
</tr>
|
75
|
-
<tr>
|
76
|
-
<td style="vertical-align: top;">Return value:</td>
|
77
|
-
<td><dl><dt>status</dt><dd>true or false, indicating whether error should be reported</dd><dt>message</dt><dd>error message sent to the user</dd></dl></td>
|
78
|
-
</tr>
|
79
|
-
</table>
|
80
|
-
|
81
|
-
|
data/doc/create-client.md
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
# Client definition
|
2
|
-
It is necessary to differentiate between clients for HaveAPI based APIs
|
3
|
-
and clients to work with your API instance. This document describes
|
4
|
-
the former. If you only want to use your API, you should check a list
|
5
|
-
of available clients and pick the one in the right language. Only when
|
6
|
-
there isn't a client already available in the language you want, then
|
7
|
-
continue reading.
|
8
|
-
|
9
|
-
# Design rules
|
10
|
-
The client must completely depend on the API description:
|
11
|
-
|
12
|
-
- it has no assumptions and API-specific code,
|
13
|
-
- it does not know any resources, actions and parameters,
|
14
|
-
- everything the client knows must be found out from the API description.
|
15
|
-
|
16
|
-
All clients should use a similar paradigm, so that it is possible to use
|
17
|
-
clients in different languages in the same way, as far as the language syntax
|
18
|
-
allows. Clients bundled with HaveAPI may serve as a model. A client should
|
19
|
-
use all the advantages and coding styles of the language it is written
|
20
|
-
in (e.g. use objects in object oriented languages).
|
21
|
-
|
22
|
-
A model paradigm (in no particular language):
|
23
|
-
|
24
|
-
// Create client instance
|
25
|
-
api = new HaveAPI.Client("https://your.api.tld")
|
26
|
-
|
27
|
-
// Authenticate
|
28
|
-
api.authenticate("basic", {"user": "yourname", "password": "yourpassword"})
|
29
|
-
|
30
|
-
// Access resources and actions
|
31
|
-
api.<resource>.<action>( { <parameters> } )
|
32
|
-
api.user.new({"name": "Very Name", "password": "donottellanyone"})
|
33
|
-
api.user.list()
|
34
|
-
api.nested.resource.deep.list()
|
35
|
-
|
36
|
-
// Pass IDs to resources
|
37
|
-
api.nested(1).resource(2).deep.list()
|
38
|
-
|
39
|
-
// Object-like access
|
40
|
-
user = api.user.show(1)
|
41
|
-
user.id
|
42
|
-
user.name
|
43
|
-
user.destroy()
|
44
|
-
|
45
|
-
// Logout
|
46
|
-
api.logout()
|
47
|
-
|
48
|
-
# Necessary features to implement
|
49
|
-
A client should implement all of the listed features in order to be useful.
|
50
|
-
|
51
|
-
## Resource tree
|
52
|
-
The client gives access to all listed resources and their actions.
|
53
|
-
|
54
|
-
In scripting languages, resources and actions are usually defined as dynamic
|
55
|
-
properties/objects/methods, depending on the language.
|
56
|
-
|
57
|
-
## Input/output parameters
|
58
|
-
Allow sending input parameters and accessing the response.
|
59
|
-
|
60
|
-
## Input/output formats
|
61
|
-
A client must send appropriate HTTP header `Accept`. As only JSON is by now built-in
|
62
|
-
in HaveAPI, it should send `application/json`.
|
63
|
-
|
64
|
-
## Authentication
|
65
|
-
All authentication methods that are built-in the HaveAPI should be supported
|
66
|
-
if possible. The client should choose suitable authentication method
|
67
|
-
for its purpose and allow the developer to select the authentication method
|
68
|
-
if it makes sense to do so.
|
69
|
-
|
70
|
-
It is a good practise to implement authentication methods as plugins,
|
71
|
-
so that developers may add custom authentication providers easily.
|
72
|
-
|
73
|
-
## Object-like access
|
74
|
-
A client must interpret the API response according to action output layout.
|
75
|
-
Layouts `object` and `object_list` should be handled as object instances,
|
76
|
-
if the language allows it.
|
77
|
-
|
78
|
-
Object instances represent the object fetched from the database. Received
|
79
|
-
parameters are accessed via object attributes/properties. Actions are defined
|
80
|
-
as instance methods. Objects may have associations to other resources which
|
81
|
-
must be made available and be easy to access.
|
82
|
-
|
83
|
-
# Supplemental features
|
84
|
-
Following features are supplemental. It is good to support them,
|
85
|
-
but is not necessary.
|
86
|
-
|
87
|
-
## Client-side validations
|
88
|
-
Client may make use of described validators and validate the input before
|
89
|
-
sending it to the API, to lessen the API load and make it more user-friendly.
|
90
|
-
|
91
|
-
However, as the input is validated on the server anyway, it does not have
|
92
|
-
to be implemented.
|
93
|
-
|
94
|
-
## Metadata channel
|
95
|
-
Metadata channel is currently used to specify what associated resources should
|
96
|
-
be prefetched and whether an object list should contain total number of items.
|
97
|
-
|
98
|
-
Metadata is nothing more than a hash in input parameters under key `_meta`.
|
99
|
-
|
100
|
-
## Blocking actions
|
101
|
-
Useful for APIs with long-running actions. Clients can check state of such actions
|
102
|
-
using resource `ActionState`. Because this resource is automatically present in all
|
103
|
-
APIs that use blocking actions, client libraries expose this resource to the developer
|
104
|
-
just as any other resource in the API.
|
105
|
-
|
106
|
-
However, you may wish to integrate it in your client and provide ways for the action
|
107
|
-
call to block/accept callbacks.
|