haveapi 0.22.0 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|