haveapi 0.29.8 → 0.29.9

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.
data/doc/protocol.md ADDED
@@ -0,0 +1,570 @@
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
+ # Protocol versioning
24
+ Protocol version defines the form and contents of
25
+
26
+ - the envelope,
27
+ - API description,
28
+ - data transfers.
29
+
30
+ Protocol version is in the form `<major>.<minor>`. `major` is incremented
31
+ whenever a change is made to the protocol that breaks backward compatibility.
32
+ When backward compatibility is kept and only some new features are added to the
33
+ protocol, `major` stays and `minor` is incremented.
34
+
35
+ # Envelope
36
+ In addition to output format specified below, every API response
37
+ is wrapped in an envelope.
38
+ The envelope reports if action succeeded or failed, provides return value or error
39
+ messages.
40
+
41
+ {
42
+ "status": true if action succeeded or false if error occured,
43
+ "response": return value,
44
+ "message": error message, if status is false,
45
+ "errors: {
46
+ "parameter1": ["list", "of", "errors"],
47
+ "parameter2": ["and", "so", "on"]
48
+ }
49
+ }
50
+
51
+ Responses for `OPTIONS` requests also send a protocol version in the envelope:
52
+
53
+ "version": <version>
54
+
55
+ ## Localization
56
+
57
+ Servers may localize user-facing response strings using the HTTP
58
+ `Accept-Language` header. Localization changes only string values, such as
59
+ `message`, parameter entries in `errors`, and validator messages in
60
+ self-description. Self-description parameter `label` and `description` values
61
+ may also be localized. The envelope shape does not change and clients must
62
+ continue to treat these values as strings.
63
+
64
+ # Description format
65
+ In this document, the self-description is encoded in JSON. However, it can
66
+ be encoded in any of the supported output formats.
67
+
68
+ ## API version
69
+
70
+ <a href="/doc/protocol.png">
71
+ <img src="/doc/protocol.png"
72
+ alt="Diagram representing the structure of an API version"
73
+ width="600">
74
+ </a>
75
+
76
+ API version is described as:
77
+
78
+ {
79
+ "authentication": {
80
+ ... authentication methods ...
81
+ },
82
+ "resources": {
83
+ ... resources ...
84
+ },
85
+ "meta": {
86
+ "namespace": "_meta"
87
+ },
88
+ "help": "/<version>/"
89
+ }
90
+
91
+ See appropriate section for detailed description of each section.
92
+
93
+ ## Authentication
94
+ HaveAPI defines an interface for implementing custom authentication methods.
95
+ HTTP basic and token authentication is built-in.
96
+
97
+ Authentication methods can be set per API version. They are a part of
98
+ the self-description, but must be understood by the client.
99
+ The client can choose whichever available authentication method he prefers.
100
+
101
+ ### HTTP basic authentication
102
+ HTTP basic authentication needs no other configuration, only informs about its presence.
103
+
104
+ "basic": {}
105
+
106
+ HTTP basic authentication does not support multi-factor authentication.
107
+
108
+ ### Token authentication
109
+ Token authentication contains resource ``token``, that is used to acquire
110
+ and revoke tokens.
111
+
112
+ Tokens are acquired by action ``request``, in which the client provides arbitrary
113
+ login credentials. If the login credentials match, the server either concludes
114
+ the authentication process, or multiple authentication steps may be necessary.
115
+
116
+ For single-step authentication processses, action ``request`` returns the
117
+ token and its validity period. If multiple authentication steps are necessary,
118
+ the server signals this by returning ``complete = true``. The client then has
119
+ to call the next authentication step, which is an action on the ``token``
120
+ resource, as returned in ``next_action``.
121
+
122
+ Tokens are returned in both cases. If the authentication is finished, the token
123
+ is then used for authenticating further requests. If the authentication continues,
124
+ the token is used to authenticate the next authentication request, which then
125
+ returns another token.
126
+
127
+ After the authentication is complete, acquired tokens can be renewed using action
128
+ ``renew`` and revoked by calling the ``revoke`` action.
129
+
130
+ "token": {
131
+ "http_header": "<name of HTTP header to transfer token in, by default X-HaveAPI-Auth-Token>",
132
+ "query_parameter": "<name of query parameter for token, by default auth_token>",
133
+ "resources": {
134
+ "actions": {
135
+ "request": {
136
+ ...
137
+ "input": {
138
+ ...
139
+ "parameters": {
140
+ "user": ...
141
+ "password": ...
142
+ "lifetime": ...
143
+ "interval": ...
144
+ },
145
+ ...
146
+ },
147
+ "output": {
148
+ ...
149
+ "parameters": {
150
+ "token": ...
151
+ "valid_to": ...
152
+ "complete": true/false
153
+ "next_action": ...
154
+ },
155
+ ...
156
+ },
157
+ ...
158
+ }
159
+ "revoke": ...
160
+ }
161
+ }
162
+ }
163
+
164
+ The format for ``resources`` section is the same as for any other resource.
165
+
166
+ ## Resources
167
+ Each resource is described as:
168
+
169
+ "<resource_name>": {
170
+ "description": "Some description that explains everything",
171
+ "actions": {
172
+ ... actions ...
173
+ },
174
+ "resources": {
175
+ ... nested resources ...
176
+ }
177
+ }
178
+
179
+ ## Actions
180
+ Every action is described as:
181
+
182
+ "<action_name>": {
183
+ "auth": true|false,
184
+ "description": "Describe what this action does",
185
+ "aliases": ["list", "of", "aliases"],
186
+ "blocking": true|false,
187
+ "input": {
188
+ "layout": "layout type",
189
+ "namespace": "namespace name",
190
+ "parameters": {
191
+ ... parameters ...
192
+ }
193
+ },
194
+ "output": {
195
+ "layout": "layout type",
196
+ "namespace": "namespace name",
197
+ "parameters": {
198
+ ... parameters ...
199
+ }
200
+ },
201
+ "examples": [
202
+ ... list of examples ...
203
+ ],
204
+ "meta": ... metadata ...,
205
+ "path": "Path for this action",
206
+ "method": "HTTP method to be used",
207
+ "help": "Path to get this very description of the action"
208
+ }
209
+
210
+ ### Layouts
211
+ Layout type is specified for input/output parameters. Thanks to the layout type,
212
+ clients know how to send the request and how to interpret the response.
213
+
214
+ Defined layout types:
215
+
216
+ - object - mainly the response is to be treated as an instance of a resource
217
+ - object_list - list of objects
218
+ - hash - simply a hash of parameters, it is to be treated as such
219
+ - hash_list - list of hashes
220
+
221
+ In client libraries, the ``object`` layout output usually results in returning
222
+ an object that represents the instance of the resource. The parameters are defined
223
+ as object properties and the like.
224
+
225
+ ### Namespace
226
+ All input/output parameters are put in a namespace, which is usually
227
+ the name of the resource.
228
+
229
+ For example:
230
+
231
+ {
232
+ "user": {
233
+ ... parameters ...
234
+ }
235
+ }
236
+
237
+ ## Parameters
238
+ There are two parameter types.
239
+
240
+ ### Data types
241
+ The type can be one of:
242
+
243
+ - String
244
+ - Text
245
+ - Boolean
246
+ - Integer
247
+ - Float
248
+ - Datetime
249
+
250
+ #### Typed input coercion and validation
251
+ Typed input parameters MUST be validated strictly by servers and clients. Query parameters arrive as
252
+ strings and must be parsed before validation. Canonical rules and examples are documented in
253
+ [Typed input validation](typed-input-validation.md).
254
+ Explicit `null` is allowed only for parameters marked `nullable` (typically optional) and MUST
255
+ be preserved by clients (send JSON `null`) so the server can distinguish between omitted and
256
+ explicitly cleared values. When using query-string input, explicit `null` is encoded as an
257
+ empty string; servers treat empty/whitespace as `null` only for nullable typed parameters.
258
+ For non-nullable parameters, empty strings are validated normally (string/text can be empty,
259
+ other types reject empty).
260
+
261
+ Example (query string, optional typed param set to `null`):
262
+
263
+ GET /resource/action?resource[dt]=
264
+
265
+ "<parameter_name>": {
266
+ "required": true/false/null,
267
+ "nullable": true/false,
268
+ "label": "Label for this parameter",
269
+ "description": "Describe it's meaning",
270
+ "type": "<one of the data types>",
271
+ "validators": ... validators ...,
272
+ "default": "default value that is used if the parameter is omitted",
273
+ "protected": true/false
274
+ }
275
+
276
+
277
+ #### Validators
278
+ Every parameter has its own validators. Any of the following validators
279
+ may be present. Input value must pass through all validators in order
280
+ to be considered valid.
281
+
282
+ ##### Acceptance
283
+ Used when a parameter must have one specific value.
284
+
285
+ "accept": {
286
+ "value": <value to accept>,
287
+ "message": "has to be <value>"
288
+ }
289
+
290
+ ##### Presence
291
+ The parameter must be present. If `empty` is `false`, leading and trailing
292
+ whitespace is stripped before the check.
293
+
294
+ "present": {
295
+ empty: true/false,
296
+ message: "must be present"
297
+ }
298
+
299
+ ##### Confirmation
300
+ Used to confirm that two parameters have either the same value or not have the
301
+ same value. The former can be used e.g. to verify that passwords are same
302
+ and the latter to give two different e-mail addresses.
303
+
304
+ "confirm": {
305
+ "equal": true/false,
306
+ "parameter": <parameter_name>,
307
+ "message": "must (or must not) be the same as <parameter_name>"
308
+ }
309
+
310
+ ##### Inclusion
311
+ The parameter can contain only one of given options.
312
+
313
+ "include": {
314
+ "values": ["list", "of", "allowed", "values"],
315
+ "message": "%{value} cannot be used"
316
+ }
317
+
318
+ If the `values` are a list, than it is a list of accepted values.
319
+ If the `values` are a hash, the keys of that hash are accepted values,
320
+ values in that hash are to be shown in UI.
321
+
322
+ "include": {
323
+ "values": {
324
+ "one": "Fancy one",
325
+ "two": "Fancy two"
326
+ },
327
+ "message": "%{value} cannot be used"
328
+ }
329
+
330
+ ##### Exclusion
331
+ The parameter can be set to anything except values listed here.
332
+
333
+ "exclude": {
334
+ "values": ["list", "of", "excluded", "values"],
335
+ "message": "%{value} cannot be used"
336
+ }
337
+
338
+ ##### Specific format
339
+ If `match` is true, the parameter must pass given regular expression.
340
+ Otherwise it must not pass the regular expression.
341
+
342
+ "format": {
343
+ "rx": "regular expression",
344
+ "match": true/false,
345
+ "description": "human-readable description of the regular expression",
346
+ "message": "%{value} is not in a valid format"
347
+ }
348
+
349
+ ##### Length
350
+ Useful only for `String` and `Text` parameters. Checks the length of given string.
351
+ It may check either
352
+
353
+ - minimum
354
+ - maximum
355
+ - minimum and maximum
356
+ - constant length
357
+
358
+ The length validator must therefore contain one or more checks, but cannot
359
+ contain both min/max and equality.
360
+
361
+ Length range:
362
+
363
+ "length": {
364
+ "min": 0,
365
+ "max": 99,
366
+ "message": "length has to be in range <0,99>"
367
+ }
368
+
369
+ Constant length:
370
+
371
+ "length": {
372
+ "equals": 10,
373
+ "message": "length has to be 10"
374
+ }
375
+
376
+ ##### Numericality
377
+ Numericality implies that the parameter must be a number, i.e. `Integer`, `Float`
378
+ or `String` containing only digits. It can check that the number is in a specified
379
+ range and can provide a step. The validator can contain one or more of these conditions.
380
+
381
+ "number": {
382
+ "min": 0,
383
+ "max": 99,
384
+ "step": 3,
385
+ "mod": 3,
386
+ "even": true/false,
387
+ "odd": true/false
388
+ }
389
+
390
+ ##### Custom validation
391
+ Custom validation cannot be documented by the API. The developer may or may not
392
+ provide information that some non-documented validation takes place. The documentation
393
+ contains only the description of the validations that may be shown to the user,
394
+ but is not evaluated client-side, only server-side.
395
+
396
+ "custom": "description of custom validation"
397
+
398
+ ### Resource association
399
+ This is used for associations between resources, e.g. car has a wheel.
400
+
401
+ "<parameter_name>": {
402
+ "required": true/false/null,
403
+ "nullable": true/false,
404
+ "label": "Label for this parameter",
405
+ "description": "Describe it's meaning",
406
+ "type": "Resource",
407
+ "resource": ["path", "to", "resource"],
408
+ "value_id": "<name of a parameter that is used as an id>",
409
+ "value_label": "<name of a parameter that is used as a value>",
410
+ "value": {
411
+ "path": "Path to 'show' action of associated resource",
412
+ "method": "HTTP method to use",
413
+ "help": "Path to get the associated resource's 'show' description"
414
+ },
415
+ "choices": {
416
+ "path": "Path to action that returns a list of possible associations",
417
+ "method": "HTTP method to use",
418
+ "help": "Path to description of the list action"
419
+ }
420
+ }
421
+
422
+ The _resource_ type also has a different output in action response. It returns
423
+ a hash containing associated resource ID and its label, so that clients
424
+ can show the human-friendly label instead of just an ID.
425
+
426
+ For input parameters of type `Resource`, empty/whitespace input is treated as
427
+ `null`/omitted only when the parameter is nullable. Required resource parameters
428
+ still reject empty/whitespace input.
429
+
430
+ "<parameter_name>": {
431
+ "<value of value_id from description>": <resource id>,
432
+ "<value of value_label from description>": "<label>"
433
+ }
434
+
435
+ ## Examples
436
+ Examples are described in a generic way, so that every client can
437
+ render them according to its syntax.
438
+
439
+ {
440
+ "title": "A title",
441
+ "path_params: [ ... array of integers ... ],
442
+ "request": {
443
+ ... a hash of request parameters ...
444
+ },
445
+ "response": {
446
+ ... a hash of response parameters ...
447
+ },
448
+ "status": true/false,
449
+ "message": "Message explaining an error if it occurs",
450
+ "errors": {
451
+ "<parameter>": [ ... array of errors ... ],
452
+ },
453
+ http_status: 200,
454
+ "comment": "Description of the example"
455
+ }
456
+
457
+ ## Metadata
458
+ Metadata can be global and per-object. Global metadata are sent once for each
459
+ response, where as per-object are sent with each object that is a part of the
460
+ response.
461
+
462
+ {
463
+ "global": {
464
+ "input": ... parameters or null ...,
465
+ "output: ... parameters or null ...
466
+ } or null,
467
+
468
+ "object": {
469
+ "input": ... parameters or null ...,
470
+ "output: ... parameters or null ...
471
+ } or null,
472
+ }
473
+
474
+ Per-object metadata may include `path_params`, an array of URL parameters that
475
+ clients can use to prepare actions on the returned object. This field is
476
+ optional. Servers may omit it when the response does not expose the identifiers
477
+ that would be needed to reconstruct those URL parameters.
478
+
479
+ ## Blocking mode
480
+ Blocking mode is for actions whose execution takes a long time. Clients can monitor
481
+ progress of such actions and even cancel their execution.
482
+
483
+ The blocking action returns its result as usual, but provides a unique identifier using
484
+ which clients can check its status via the `ActionState` resource. The state identifier
485
+ is passed through global output metadata in a parameter called `action_state_id`.
486
+ Resource `ActionState` is a part of the API's documentation and is the standard interface
487
+ for browsing and manipulating currently active blocking actions.
488
+
489
+ An action is blocking if parameter `blocking` in its description is `true` and if
490
+ `action_state_id` is present in its metadata after each call. This allows actions
491
+ to be blocking only when needed.
492
+
493
+ ## List API versions
494
+ Send request ``OPTIONS /?describe=versions``. The description format:
495
+
496
+ {
497
+ "versions": [1, 2, 3, ... list of versions],
498
+ "default": <which version is default>
499
+ }
500
+
501
+ ## Describe default version
502
+ Send request ``OPTIONS /?describe=default`` the get the description
503
+ of the default version.
504
+
505
+ ## Describe the whole API
506
+ It is possible to get self-description of all versions at once.
507
+
508
+ Send request ``OPTIONS /``. The description format:
509
+
510
+ {
511
+ "default_version": <which version is default>,
512
+ "versions": {
513
+ "default": ... full version self-description ...,
514
+ "<version>": ... full version self-description,
515
+ ... all other versions ...
516
+ }
517
+ }
518
+
519
+ # Authorization
520
+ Actions may require different levels of authorization. HaveAPI provides means for
521
+ implementing authorization, but it is not self-described.
522
+
523
+ If the user is authenticated when requesting self-description, only allowed
524
+ resources/actions/parameters will be returned.
525
+
526
+ # Input/output formats
527
+ For now, the only supported input format is JSON.
528
+
529
+ Output format can be chosen by a client. However, no other format than JSON is built-in.
530
+ The output format can be chosen with HTTP header ``Accept``.
531
+
532
+ # Request
533
+ Action path and HTTP method the client learns from the self-description.
534
+
535
+ Example request:
536
+
537
+ POST /users HTTP/1.1
538
+ Content-Type: application/json
539
+ Accept: application/json
540
+ Connection: Close
541
+
542
+ {
543
+ "user": {
544
+ "login": "mylogin",
545
+ "name": "Very Name",
546
+ "role": "admin"
547
+ }
548
+ }
549
+
550
+ # Response
551
+ Clients know how to interpret the response thanks to the layout type they learn
552
+ from the self-description.
553
+
554
+ Example response to the request above:
555
+
556
+ Content-Type: application/json
557
+
558
+ {
559
+ "status": true,
560
+ "response": {
561
+ "user": {
562
+ "id": 1,
563
+ "login": "mylogin",
564
+ "name": "Very Name",
565
+ "role": "admin"
566
+ }
567
+ },
568
+ "message": null,
569
+ "errors: null
570
+ }
data/doc/protocol.png ADDED
Binary file
@@ -0,0 +1,113 @@
1
+ # Typed input validation
2
+
3
+ ## Overview
4
+ HaveAPI parameters declare a `type` (String, Text, Integer, Float, Boolean, Datetime, plus Resource).
5
+ Servers and clients MUST validate typed input parameters strictly.
6
+ Invalid values MUST NOT be silently coerced into defaults like `0`, `0.0`, or `false`.
7
+
8
+ ## General rules
9
+ - Scalars only for scalar types: arrays/lists and objects/hashes are invalid for scalar types.
10
+ - `null`/`nil` means "value omitted" for validation purposes (server defaults may apply),
11
+ but explicit `null` is a valid value only for parameters marked `nullable` (usually optional).
12
+ Clients MUST preserve explicit `null` (send it as JSON `null`) so actions can distinguish
13
+ "omitted" vs "explicitly cleared".
14
+ - Empty strings (`""` or whitespace-only strings) are invalid for Integer, Float, Boolean, and Datetime.
15
+ For nullable typed parameters (including Resource IDs), empty/whitespace is treated as
16
+ `null`/omitted, which lets query-string inputs represent explicit `null`.
17
+ - Client-side validation is recommended; the server is authoritative.
18
+
19
+ Query-string example (explicit `null` for nullable typed param):
20
+
21
+ GET /resource/action?resource[dt]=
22
+
23
+ The empty value is treated as `null` for nullable typed parameters.
24
+
25
+ ## Per-type rules with examples
26
+
27
+ ### Integer
28
+ Accept:
29
+ - JSON integer (e.g. `12`, `-7`)
30
+ - string matching base-10 integer `^[+-]?\d+$` after trim (e.g. `"42"`, `"+5"`)
31
+ - float that is finite and integral (e.g. `12.0`)
32
+
33
+ Reject:
34
+ - non-numeric strings (`"abc"`, `"12abc"`)
35
+ - decimal strings (`"12.0"`) and non-integral floats (`12.3`)
36
+ - empty/whitespace string
37
+
38
+ ### Float
39
+ Accept:
40
+ - JSON integer or float (finite only)
41
+ - string matching a full float token after trim, including exponent (e.g. `"1e3"`, `"-0.5"`)
42
+
43
+ Reject:
44
+ - non-numeric strings
45
+ - empty/whitespace string
46
+ - non-finite values (`NaN`, `Infinity`)
47
+
48
+ Note: integers are accepted for Float and become floats.
49
+
50
+ ### Boolean
51
+ Accept:
52
+ - JSON boolean
53
+ - integer `0`/`1`
54
+ - string tokens (case-insensitive, after trim):
55
+ - truthy: `true`, `t`, `yes`, `y`, `1`
56
+ - falsy: `false`, `f`, `no`, `n`, `0`
57
+
58
+ Reject:
59
+ - empty/whitespace string
60
+ - unknown strings (`"maybe"`)
61
+ - numbers other than 0/1
62
+
63
+ ### Datetime
64
+ Accept (wire format): ISO 8601 date/datetime strings:
65
+ - `YYYY-MM-DD`
66
+ - `YYYY-MM-DDTHH:MM(:SS(.sss)?)?(Z|+HH:MM|-HH:MM|+HHMM|-HHMM)`
67
+
68
+ Examples: `"2020-01-31"`, `"2020-01-31T10:20Z"`, `"2020-01-31T10:20:30.123-0500"`.
69
+
70
+ Reject:
71
+ - empty/whitespace string
72
+ - non-ISO formats (`2020/01/01`)
73
+ - invalid calendar dates (`2020-02-30`)
74
+
75
+ Note: some clients may accept native Date/Time objects and serialize to ISO 8601.
76
+
77
+ ### String / Text (rule 6.B)
78
+ Accept:
79
+ - scalar values (string, number, boolean) and coerce to string
80
+
81
+ Reject:
82
+ - arrays/lists
83
+ - objects/hashes
84
+
85
+ ### Resource
86
+ Wire inputs are resource identifiers.
87
+
88
+ Accept:
89
+ - integer id
90
+ - digit-string id after trim
91
+ - (some clients) ResourceInstance -> uses its `id`
92
+ - empty/whitespace string is treated as `null`/omitted when the parameter is nullable
93
+
94
+ Reject:
95
+ - empty/whitespace string for required parameters
96
+ - non-digit strings
97
+ - negative ids
98
+
99
+ If the server attempts to resolve the resource and it does not exist, it MUST become a validation error
100
+ ("resource not found"), not a 500.
101
+
102
+ ## Error reporting expectations
103
+ - On the server, invalid typed values are reported as parameter validation errors ("input parameters are not valid").
104
+ - Clients should fail fast locally when possible and report per-parameter errors.
105
+
106
+ Recommended canonical messages used by clients:
107
+ - `not a valid integer`
108
+ - `not a valid float`
109
+ - `not a valid boolean`
110
+ - `not in ISO 8601 format`
111
+ - `not a valid string`
112
+ - `not a valid resource ID`
113
+ - `cannot be null`
data/haveapi.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.required_ruby_version = ">= #{File.read('../../.ruby-version').strip}"
16
16
 
17
17
  s.add_dependency 'activesupport', '>= 7.1'
18
- s.add_dependency 'haveapi-client', '~> 0.29.8'
18
+ s.add_dependency 'haveapi-client', '~> 0.29.9'
19
19
  s.add_dependency 'i18n', '>= 1.6', '< 2'
20
20
  s.add_dependency 'json'
21
21
  s.add_dependency 'mail'