haveapi 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +21 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +277 -0
- data/Rakefile +7 -0
- data/doc/create-client.md +98 -0
- data/doc/index.md +6 -0
- data/doc/protocol.md +355 -0
- data/haveapi.gemspec +30 -0
- data/lib/haveapi/version.rb +1 -1
- metadata +12 -4
- data/lib/haveapi/extensions/resource_prefetch.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fa0da1f1b314ff7278f68c3f6388579d963b21c
|
4
|
+
data.tar.gz: 7df0e7f2124286d109535b860e53f64c44a06b1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 945c9596473a913973fc06bc1432a5c8c6cb6b921a780ab420eddea9036b281d81b117aabbaf911e3844c5f0391da728a45e00674a482dcd398ac5e3a02ac961
|
7
|
+
data.tar.gz: dd965f92e0c67a799e053fa9b3f5817e3c1b899127884d81372f3f40872df2751450001d4bfbfc525ed6b1f9d7100dd3f46a1ee78e77ad1ec3e1b7946691a6be
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Jakub Skokan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
HaveAPI
|
2
|
+
=======
|
3
|
+
A framework for creating self-describing APIs in Ruby.
|
4
|
+
|
5
|
+
Note: HaveAPI is under heavy development. It is not stable, its interface may change.
|
6
|
+
|
7
|
+
## What is a self-describing API?
|
8
|
+
A self-describing API responds to HTTP method `OPTIONS` and returns description
|
9
|
+
of available resources and their actions. The description contains
|
10
|
+
full list of parameters, their labels, text notes, data types, validators
|
11
|
+
and example usage.
|
12
|
+
|
13
|
+
Clients use the self-description to learn how to communicate with the API,
|
14
|
+
which they otherwise know nothing about.
|
15
|
+
|
16
|
+
## Main features
|
17
|
+
- Creates RESTful APIs
|
18
|
+
- Handles network communication, input/output formats and parameters
|
19
|
+
on both server and client, you need only to define resources and actions
|
20
|
+
- By writing the code you get the documentation which is available to all clients
|
21
|
+
- Auto-generated online HTML documentation
|
22
|
+
- Generic interface for clients - one client can be used to access all APIs
|
23
|
+
using this framework
|
24
|
+
- Ruby, PHP and JavaScript clients already available
|
25
|
+
- A change in the API is immediately reflected in all clients
|
26
|
+
- Supports API versioning
|
27
|
+
- Ready for ActiveRecord - validators from models are included in the
|
28
|
+
self-description
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
This text might not be complete or up-to-date, as things still often change.
|
32
|
+
Full use of HaveAPI may be seen
|
33
|
+
in [vpsadminapi](https://github.com/vpsfreecz/vpsadminapi), which may serve
|
34
|
+
as an example of how are things meant to be used.
|
35
|
+
|
36
|
+
All resources and actions are represented by classes. They all must be stored
|
37
|
+
in a module, whose name is later given to HaveAPI.
|
38
|
+
|
39
|
+
HaveAPI then searches all classes in that module and constructs your API.
|
40
|
+
|
41
|
+
For the purposes of this document, all resources will be in module `MyAPI`.
|
42
|
+
|
43
|
+
### Example
|
44
|
+
This is a basic example, it does not show all options and functions.
|
45
|
+
|
46
|
+
Let's assume a model:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class User < ActiveRecord::Base
|
50
|
+
validates :login, :full_name, :role, presence: true
|
51
|
+
validates :login, format: {
|
52
|
+
with: /[a-zA-Z\.\-]{3,30}/,
|
53
|
+
message: 'not a valid login'
|
54
|
+
}, uniqueness: true
|
55
|
+
validates :role, inclusion: {
|
56
|
+
in: %w(admin user),
|
57
|
+
message '%{value} is not a valid role'
|
58
|
+
}
|
59
|
+
|
60
|
+
# An example authentication with plain text password
|
61
|
+
def self.authenticate(username, password)
|
62
|
+
u = User.find_by(login: username)
|
63
|
+
|
64
|
+
if u
|
65
|
+
u if u.password == password
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Resource user might look like this:
|
72
|
+
```ruby
|
73
|
+
module MyAPI
|
74
|
+
class User < HaveAPI::Resource
|
75
|
+
# This resource belongs to version 1.
|
76
|
+
# It is also possible to put resource to multiple versions, e.g. [1, 2]
|
77
|
+
version 1
|
78
|
+
|
79
|
+
# Provide description for this resource
|
80
|
+
desc 'Manage users'
|
81
|
+
|
82
|
+
# ActiveRecord model to load validators from
|
83
|
+
model ::User
|
84
|
+
|
85
|
+
# Require authentication, this is the default
|
86
|
+
auth true
|
87
|
+
|
88
|
+
# Create a named group of shared params, that may be later included
|
89
|
+
# by actions.
|
90
|
+
params(:id) do
|
91
|
+
id :id, label: 'User ID'
|
92
|
+
end
|
93
|
+
|
94
|
+
params(:common) do
|
95
|
+
string :login, label: 'Login', desc: 'Used for authentication'
|
96
|
+
string :full_name, label: 'Full name'
|
97
|
+
string :role, label: 'User role', desc: 'admin or user'
|
98
|
+
end
|
99
|
+
|
100
|
+
# Actions
|
101
|
+
# Module HaveAPI::Actions::Default contains helper classes that define
|
102
|
+
# HTTP methods and routes for generic actions.
|
103
|
+
class Index < HaveAPI::Actions::Default::Index
|
104
|
+
desc 'List all users'
|
105
|
+
|
106
|
+
# There are no input parameters
|
107
|
+
|
108
|
+
# Output parameters
|
109
|
+
output(:object_list) do
|
110
|
+
use :id
|
111
|
+
use :common
|
112
|
+
end
|
113
|
+
|
114
|
+
# Determine if current user can use this action.
|
115
|
+
# allow/deny immediately returns from this block.
|
116
|
+
# Default rule is deny.
|
117
|
+
authorize do |u|
|
118
|
+
allow if u.role == 'admin'
|
119
|
+
deny # deny is implicit, so it may be omitted
|
120
|
+
end
|
121
|
+
|
122
|
+
# Provide example usage
|
123
|
+
example do
|
124
|
+
request({})
|
125
|
+
response({
|
126
|
+
users: [
|
127
|
+
{
|
128
|
+
id: 1,
|
129
|
+
login: 'myuser',
|
130
|
+
full_name: 'My Very Name'
|
131
|
+
}
|
132
|
+
]
|
133
|
+
})
|
134
|
+
comment 'Get a list of all users like this'
|
135
|
+
end
|
136
|
+
|
137
|
+
# Helper method returning a query for all users
|
138
|
+
def query
|
139
|
+
::User.all
|
140
|
+
end
|
141
|
+
|
142
|
+
# This method is called if the request has meta[:count] = true
|
143
|
+
def count
|
144
|
+
query.count
|
145
|
+
end
|
146
|
+
|
147
|
+
# Execute action, return the list
|
148
|
+
def exec
|
149
|
+
query.limit(input[:limit]).offset(input[:offset])
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Create < HaveAPI::Actions::Default::Create
|
154
|
+
desc 'Create new user'
|
155
|
+
|
156
|
+
input do
|
157
|
+
use :common
|
158
|
+
end
|
159
|
+
|
160
|
+
output do
|
161
|
+
use :id
|
162
|
+
use :common
|
163
|
+
end
|
164
|
+
|
165
|
+
authorize do |u|
|
166
|
+
allow if u.role == 'admin'
|
167
|
+
deny
|
168
|
+
end
|
169
|
+
|
170
|
+
example do
|
171
|
+
request({
|
172
|
+
user: {
|
173
|
+
login: 'anotherlogin',
|
174
|
+
full_name: 'My Very New Name'
|
175
|
+
}
|
176
|
+
})
|
177
|
+
response({
|
178
|
+
user: {
|
179
|
+
id: 2
|
180
|
+
}
|
181
|
+
})
|
182
|
+
comment 'Create new user like this'
|
183
|
+
end
|
184
|
+
|
185
|
+
def exec
|
186
|
+
user = ::User.new(input)
|
187
|
+
|
188
|
+
if user.save
|
189
|
+
ok(user)
|
190
|
+
else
|
191
|
+
error('save failed', user.errors.to_hash)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
### What you get
|
200
|
+
From this piece of code, HaveAPI will generate a self-describing API.
|
201
|
+
It will contain resource `User` with actions `Index` and `Create`,
|
202
|
+
using which you can list existing users and create new ones.
|
203
|
+
|
204
|
+
You can use any of the available clients to work with the API.
|
205
|
+
|
206
|
+
### Run the example
|
207
|
+
```ruby
|
208
|
+
api = HaveAPI::Server.new(MyAPI)
|
209
|
+
|
210
|
+
# Use HTTP basic auth
|
211
|
+
class BasicAuth < HaveAPI::Authentication::Basic::Provider
|
212
|
+
def find_user(request, username, password)
|
213
|
+
User.authenticate(username, password)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
api.use_version(:all)
|
218
|
+
api.set_default_version(1)
|
219
|
+
api.auth_chain << BasicAuth
|
220
|
+
api.mount('/')
|
221
|
+
|
222
|
+
api.start!
|
223
|
+
```
|
224
|
+
|
225
|
+
This should start the application using WEBrick. Check
|
226
|
+
[http://localhost:4567](http://localhost:4567).
|
227
|
+
|
228
|
+
- `GET /` - a list of API versions
|
229
|
+
- `GET /doc` - HaveAPI documentation
|
230
|
+
- `GET /v1/` - documentation for version 1
|
231
|
+
- `OPTIONS /` - description for the whole API
|
232
|
+
- `OPTIONS /v1/` - description for API version 1
|
233
|
+
|
234
|
+
and more.
|
235
|
+
|
236
|
+
### Run with rackup
|
237
|
+
Use the same code as above, only the last line would be
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
run api.app
|
241
|
+
```
|
242
|
+
|
243
|
+
## Authentication
|
244
|
+
HaveAPI defines an interface for creating authentication providers.
|
245
|
+
HTTP basic auth and token providers are built-in.
|
246
|
+
|
247
|
+
Authentication options are self-described. A client can choose what authentication
|
248
|
+
method it understands and wants to use.
|
249
|
+
|
250
|
+
## Authorization
|
251
|
+
HaveAPI provides means for authorizing user access to actions. This process
|
252
|
+
is not self-described.
|
253
|
+
|
254
|
+
If the user is authenticated when requesting self-description, only allowed
|
255
|
+
resources, actions and parameters will be returned.
|
256
|
+
|
257
|
+
## Available clients
|
258
|
+
These clients completely rely on the API description and can be used for all
|
259
|
+
APIs that are using HaveAPI.
|
260
|
+
|
261
|
+
- Ruby client library and CLI: https://github.com/vpsfreecz/haveapi-client
|
262
|
+
- PHP client: https://github.com/vpsfreecz/haveapi-client-php
|
263
|
+
- JavaScript client: https://github.com/vpsfreecz/haveapi-client-js
|
264
|
+
|
265
|
+
or [create your own client](doc/create-client.md).
|
266
|
+
|
267
|
+
## Read more
|
268
|
+
- [Protocol definition](doc/protocol.md)
|
269
|
+
- [How to create a client](doc/create-client.md)
|
270
|
+
|
271
|
+
## Contributing
|
272
|
+
|
273
|
+
1. Fork it ( https://github.com/vpsfreecz/haveapi/fork )
|
274
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
275
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
276
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
277
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,98 @@
|
|
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`.
|
data/doc/index.md
ADDED
data/doc/protocol.md
ADDED
@@ -0,0 +1,355 @@
|
|
1
|
+
# Protocol definition
|
2
|
+
HaveAPI defines the format for the self-description and URLs where the self-description
|
3
|
+
can be found.
|
4
|
+
|
5
|
+
## Self-description
|
6
|
+
The API is self-describing. It documents itself. Clients use the self-description
|
7
|
+
to work with the API. The Self-description contains access URLs, HTTP methods,
|
8
|
+
input and output parameters and their validators.
|
9
|
+
A part of description is also an example usage and text notes.
|
10
|
+
|
11
|
+
The API responds to ``OPTIONS /``, which returns description of whole
|
12
|
+
API, containing all its versions. To get description only of selected version,
|
13
|
+
use e.g. ``OPTIONS /v1/``.
|
14
|
+
|
15
|
+
Every action also responds to HTTP method ``OPTIONS``,
|
16
|
+
with which you can get description for selected action. To distinguish actions with
|
17
|
+
the same URL, use parameter ``?method=HTTP_METHOD``.
|
18
|
+
|
19
|
+
Thanks to this ability, API changes immediately reflects in all clients without
|
20
|
+
changing a single line of code. A client can also be used on all APIs with compatible
|
21
|
+
self-describing format, without any changes at all.
|
22
|
+
|
23
|
+
## Envelope
|
24
|
+
In addition to output format specified below, every API response
|
25
|
+
is wrapped in an envelope.
|
26
|
+
The envelope reports if action succeeded or failed, provides return value or error
|
27
|
+
messages.
|
28
|
+
|
29
|
+
{
|
30
|
+
"status": true if action succeeded or false if error occured,
|
31
|
+
"response": return value,
|
32
|
+
"message": error message, if status is false,
|
33
|
+
"errors: {
|
34
|
+
"parameter1": ["list", "of", "errors"],
|
35
|
+
"parameter2": ["and", "so", "on"]
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
## Description format
|
40
|
+
In this document, the self-description is encoded in JSON. However, it can
|
41
|
+
be encoded in any of the supported output formats.
|
42
|
+
|
43
|
+
### Version
|
44
|
+
Version is described as:
|
45
|
+
|
46
|
+
{
|
47
|
+
"authentication": {
|
48
|
+
... authentication methods ...
|
49
|
+
},
|
50
|
+
"resources": {
|
51
|
+
... resources ...
|
52
|
+
},
|
53
|
+
"meta": {
|
54
|
+
"namespace": "_meta"
|
55
|
+
},
|
56
|
+
"help": "/<version>/"
|
57
|
+
}
|
58
|
+
|
59
|
+
See appropriate section for detailed description of each section.
|
60
|
+
|
61
|
+
### Authentication
|
62
|
+
HaveAPI defines an interface for implementing custom authentication methods.
|
63
|
+
HTTP basic and token authentication is built-in.
|
64
|
+
|
65
|
+
Authentication methods can be set per API version. They are a part of
|
66
|
+
the self-description, but must be understood by the client.
|
67
|
+
The client can choose whichever available authentication method he prefers.
|
68
|
+
|
69
|
+
#### HTTP basic authentication
|
70
|
+
HTTP basic authentication needs no other configuration, only informs about its presence.
|
71
|
+
|
72
|
+
"basic": {}
|
73
|
+
|
74
|
+
#### Token authentication
|
75
|
+
Token authentication contains a resource ``token``, that is used
|
76
|
+
to acquire and revoke token.
|
77
|
+
|
78
|
+
Token is acquired by action ``request``. The client provides login and password and gets a token
|
79
|
+
that is used afterwards. Token has a validity period, which may also be infinity.
|
80
|
+
|
81
|
+
Token can be revoked by calling the ``revoke`` action.
|
82
|
+
|
83
|
+
"token": {
|
84
|
+
"http_header": "<name of HTTP header to transfer token in, by default X-HaveAPI-Auth-Token>",
|
85
|
+
"query_parameter": "<name of query parameter for token, by default auth_token>",
|
86
|
+
"resources": {
|
87
|
+
"actions": {
|
88
|
+
"request": {
|
89
|
+
...
|
90
|
+
"input": {
|
91
|
+
...
|
92
|
+
"parameters": {
|
93
|
+
"login": ...
|
94
|
+
"password": ...
|
95
|
+
"lifetime": ...
|
96
|
+
"interval": ...
|
97
|
+
},
|
98
|
+
...
|
99
|
+
},
|
100
|
+
"output": {
|
101
|
+
...
|
102
|
+
"parameters": {
|
103
|
+
"token": ...
|
104
|
+
"valid_to": ...
|
105
|
+
},
|
106
|
+
...
|
107
|
+
},
|
108
|
+
...
|
109
|
+
}
|
110
|
+
"revoke": ...
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
The format for ``resources`` section is the same as for any other resource.
|
116
|
+
|
117
|
+
### Resources
|
118
|
+
Each resource is described as:
|
119
|
+
|
120
|
+
"<resource_name>": {
|
121
|
+
"description": "Some description that explains everything",
|
122
|
+
"actions": {
|
123
|
+
... actions ...
|
124
|
+
},
|
125
|
+
"resources": {
|
126
|
+
... nested resources ...
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
### Actions
|
131
|
+
Every action is described as:
|
132
|
+
|
133
|
+
"<action_name>": {
|
134
|
+
"auth": true|false,
|
135
|
+
"description": "Describe what this action does",
|
136
|
+
"aliases": ["list", "of", "aliases"],
|
137
|
+
"input": {
|
138
|
+
"layout": "layout type",
|
139
|
+
"namespace": "namespace name",
|
140
|
+
"parameters": {
|
141
|
+
... parameters ...
|
142
|
+
}
|
143
|
+
},
|
144
|
+
"output": {
|
145
|
+
"layout": "layout type",
|
146
|
+
"namespace": "namespace name",
|
147
|
+
"parameters": {
|
148
|
+
... parameters ...
|
149
|
+
}
|
150
|
+
},
|
151
|
+
"examples": [
|
152
|
+
... list of examples ...
|
153
|
+
],
|
154
|
+
"meta": ... metadata ...,
|
155
|
+
"url": "URL for this action",
|
156
|
+
"method": "HTTP method to be used",
|
157
|
+
"help": "URL to get this very description of the action"
|
158
|
+
}
|
159
|
+
|
160
|
+
#### Layouts
|
161
|
+
Layout type is specified for input/output parameters. Thanks to the layout type,
|
162
|
+
clients know how to send the request and how to interpret the response.
|
163
|
+
|
164
|
+
Defined layout types:
|
165
|
+
|
166
|
+
- object - mainly the response is to be treated as an instance of a resource
|
167
|
+
- object_list - list of objects
|
168
|
+
- hash - simply a hash of parameters, it is to be treated as such
|
169
|
+
- hash_list - list of hashes
|
170
|
+
|
171
|
+
In client libraries, the ``object`` layout output usually results in returning
|
172
|
+
an object that represents the instance of the resource. The parameters are defined
|
173
|
+
as object properties and the like.
|
174
|
+
|
175
|
+
#### Namespace
|
176
|
+
All input/output parameters are put in a namespace, which is usually
|
177
|
+
the name of the resource.
|
178
|
+
|
179
|
+
For example:
|
180
|
+
|
181
|
+
{
|
182
|
+
"user": {
|
183
|
+
... parameters ...
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
### Parameters
|
188
|
+
There are two parameter types.
|
189
|
+
|
190
|
+
#### Data types
|
191
|
+
The type can be one of:
|
192
|
+
|
193
|
+
- String
|
194
|
+
- Text
|
195
|
+
- Boolean
|
196
|
+
- Integer
|
197
|
+
- Float
|
198
|
+
- Datetime
|
199
|
+
|
200
|
+
"<parameter_name>": {
|
201
|
+
"required": true/false/null,
|
202
|
+
"label": "Label for this parameter",
|
203
|
+
"description": "Describe it's meaning",
|
204
|
+
"type": "<one of the data types>",
|
205
|
+
"choices": a list or a hash of accepted values
|
206
|
+
"validators": ... validators ...,
|
207
|
+
"default": "default value that is used if the parameter is omitted"
|
208
|
+
}
|
209
|
+
|
210
|
+
If the choices are in a list, than it is a list of accepted values.
|
211
|
+
If the choices are in a hash, the keys of that hash are accepted values,
|
212
|
+
values in that hash are to be shown in UI.
|
213
|
+
|
214
|
+
#### Resource association
|
215
|
+
This is used for associations between resources, e.g. car has a wheel.
|
216
|
+
|
217
|
+
"<parameter_name>": {
|
218
|
+
"required": true/false/null,
|
219
|
+
"label": "Label for this parameter",
|
220
|
+
"description": "Describe it's meaning",
|
221
|
+
"type": "Resource",
|
222
|
+
"resource": ["path", "to", "resource"],
|
223
|
+
"value_id": "<name of a parameter that is used as an id>",
|
224
|
+
"value_label": "<name of a parameter that is used as a value>",
|
225
|
+
"value": {
|
226
|
+
"url": "URL to 'show' action of associated resource",
|
227
|
+
"method": "HTTP method to use",
|
228
|
+
"help": "URL to get the associated resource's 'show' description"
|
229
|
+
},
|
230
|
+
"choices": {
|
231
|
+
"url": "URL to action that returns a list of possible associations",
|
232
|
+
"method": "HTTP method to use",
|
233
|
+
"help": "URL to description of the list action"
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
The _resource_ type also has a different output in action response. It returns
|
238
|
+
a hash containing associated resource ID and its label, so that clients
|
239
|
+
can show the human-friendly label instead of just an ID.
|
240
|
+
|
241
|
+
"<parameter_name>": {
|
242
|
+
"<value of value_id from description>": <resource id>,
|
243
|
+
"<value of value_label from description>": "<label>"
|
244
|
+
}
|
245
|
+
|
246
|
+
### Examples
|
247
|
+
Examples are described in a generic way, so that every client can
|
248
|
+
render them according to its syntax.
|
249
|
+
|
250
|
+
{
|
251
|
+
"title": "A title",
|
252
|
+
"request": {
|
253
|
+
... a hash of request parameters ...
|
254
|
+
},
|
255
|
+
"response": {
|
256
|
+
... a hash of response parameters ...
|
257
|
+
},
|
258
|
+
"comment": "Description of the example"
|
259
|
+
}
|
260
|
+
|
261
|
+
### Metadata
|
262
|
+
Metadata can be global and per-object. Global metadata are sent once for each
|
263
|
+
response, where as per-object are sent with each object that is a part of the
|
264
|
+
response.
|
265
|
+
|
266
|
+
{
|
267
|
+
"global": {
|
268
|
+
"input": ... parameters or null ...,
|
269
|
+
"output: ... parameters or null ...
|
270
|
+
} or null,
|
271
|
+
|
272
|
+
"object": {
|
273
|
+
"input": ... parameters or null ...,
|
274
|
+
"output: ... parameters or null ...
|
275
|
+
} or null,
|
276
|
+
}
|
277
|
+
|
278
|
+
### List API versions
|
279
|
+
Send request ``OPTIONS /?describe=versions``. The description format:
|
280
|
+
|
281
|
+
{
|
282
|
+
"versions": [1, 2, 3, ... list of versions],
|
283
|
+
"default": <which version is default>
|
284
|
+
}
|
285
|
+
|
286
|
+
### Describe default version
|
287
|
+
Send request ``OPTIONS /?describe=default`` the get the description
|
288
|
+
of the default version.
|
289
|
+
|
290
|
+
### Describe the whole API
|
291
|
+
It is possible to get self-description of all versions at once.
|
292
|
+
|
293
|
+
Send request ``OPTIONS /``. The description format:
|
294
|
+
|
295
|
+
{
|
296
|
+
"default_version": <which version is default>,
|
297
|
+
"versions": {
|
298
|
+
"default": ... full version self-description ...,
|
299
|
+
"<version>": ... full version self-description,
|
300
|
+
... all other versions ...
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
## Authorization
|
305
|
+
Actions may require different levels of authorization. HaveAPI provides means for
|
306
|
+
implementing authorization, but it is not self-described.
|
307
|
+
|
308
|
+
If the user is authenticated when requesting self-description, only allowed
|
309
|
+
resources/actions/parameters will be returned.
|
310
|
+
|
311
|
+
## Input/output formats
|
312
|
+
For now, the only supported input format is JSON.
|
313
|
+
|
314
|
+
Output format can be chosen by a client. However, no other format than JSON is built-in.
|
315
|
+
The output format can be chosen with HTTP header ``Accept``.
|
316
|
+
|
317
|
+
## Request
|
318
|
+
Action URL and HTTP method the client learns from the self-description.
|
319
|
+
|
320
|
+
Example request:
|
321
|
+
|
322
|
+
POST /users HTTP/1.1
|
323
|
+
Content-Type: application/json
|
324
|
+
Accept: application/json
|
325
|
+
Connection: Close
|
326
|
+
|
327
|
+
{
|
328
|
+
"user": {
|
329
|
+
"login": "mylogin",
|
330
|
+
"name": "Very Name",
|
331
|
+
"role": "admin"
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
## Response
|
336
|
+
Clients know how to interpret the response thanks to the layout type they learn
|
337
|
+
from the self-description.
|
338
|
+
|
339
|
+
Example response to the request above:
|
340
|
+
|
341
|
+
Content-Type: application/json
|
342
|
+
|
343
|
+
{
|
344
|
+
"status": true,
|
345
|
+
"response": {
|
346
|
+
"user": {
|
347
|
+
"id": 1,
|
348
|
+
"login": "mylogin",
|
349
|
+
"name": "Very Name",
|
350
|
+
"role": "admin"
|
351
|
+
}
|
352
|
+
},
|
353
|
+
"message": null,
|
354
|
+
"errors: null
|
355
|
+
}
|
data/haveapi.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'haveapi/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'haveapi'
|
7
|
+
s.version = HaveAPI::VERSION
|
8
|
+
s.date = '2015-08-13'
|
9
|
+
s.summary =
|
10
|
+
s.description = 'Framework for creating self-describing APIs'
|
11
|
+
s.authors = 'Jakub Skokan'
|
12
|
+
s.email = 'jakub.skokan@vpsfree.cz'
|
13
|
+
s.files = `git ls-files -z`.split("\x0")
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.required_ruby_version = '~> 2.0'
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'activerecord', '~> 4.1.6'
|
19
|
+
s.add_runtime_dependency 'require_all'
|
20
|
+
s.add_runtime_dependency 'json'
|
21
|
+
s.add_runtime_dependency 'sinatra'
|
22
|
+
s.add_runtime_dependency 'mysql'
|
23
|
+
s.add_runtime_dependency 'sinatra-activerecord'
|
24
|
+
s.add_runtime_dependency 'rake'
|
25
|
+
s.add_runtime_dependency 'github-markdown', '~> 0.6.6'
|
26
|
+
|
27
|
+
s.add_development_dependency 'rspec'
|
28
|
+
s.add_development_dependency 'railties'
|
29
|
+
s.add_development_dependency 'rack-test'
|
30
|
+
end
|
data/lib/haveapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: haveapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Skokan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -170,6 +170,15 @@ executables: []
|
|
170
170
|
extensions: []
|
171
171
|
extra_rdoc_files: []
|
172
172
|
files:
|
173
|
+
- .gitignore
|
174
|
+
- Gemfile
|
175
|
+
- LICENSE.txt
|
176
|
+
- README.md
|
177
|
+
- Rakefile
|
178
|
+
- doc/create-client.md
|
179
|
+
- doc/index.md
|
180
|
+
- doc/protocol.md
|
181
|
+
- haveapi.gemspec
|
173
182
|
- lib/haveapi.rb
|
174
183
|
- lib/haveapi/action.rb
|
175
184
|
- lib/haveapi/actions/default.rb
|
@@ -186,7 +195,6 @@ files:
|
|
186
195
|
- lib/haveapi/example.rb
|
187
196
|
- lib/haveapi/extensions/action_exceptions.rb
|
188
197
|
- lib/haveapi/extensions/base.rb
|
189
|
-
- lib/haveapi/extensions/resource_prefetch.rb
|
190
198
|
- lib/haveapi/hooks.rb
|
191
199
|
- lib/haveapi/metadata.rb
|
192
200
|
- lib/haveapi/model_adapter.rb
|
@@ -235,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
235
243
|
version: '0'
|
236
244
|
requirements: []
|
237
245
|
rubyforge_project:
|
238
|
-
rubygems_version: 2.2.
|
246
|
+
rubygems_version: 2.2.5
|
239
247
|
signing_key:
|
240
248
|
specification_version: 4
|
241
249
|
summary: Framework for creating self-describing APIs
|