web_function 0.6.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c025fe8ca089b9531bc819d3154438bb29eb0b5fa823509b212fb23858403b5f
4
- data.tar.gz: 6e2efbc2c7a4ebaef81ef95b8485b8822666a01967ebb46a3220ddab2caf26a1
3
+ metadata.gz: 4f6b3fc6e012055cb40b8d2e51d4118b0d6d6cff8fdb62abde22790774cf787d
4
+ data.tar.gz: 6f795bf7794369f8fa4699c31f50af7c768b2fbd287069f3d3aca29c2da89174
5
5
  SHA512:
6
- metadata.gz: acbc56f7b1b5ec89c17eb8f44699197ded36a0c3db4e722b57087bf164c0fe1f2a3c62e15223971e488721008891ce1714d2b2ce8a82dd0267b950694e8b4a46
7
- data.tar.gz: 9028501396902aa1adb0ebe548eaa4f16bf9065c11e3777512ae5ef31652e9d007dffa5b2bdff60c95c5bb451221145b6a5e08d0bd73a89e34befe73db534597
6
+ metadata.gz: 57b90f3d4d8205511ee78a0b963b35a72a535681ddb5cb6d53d942f1429222f55d8e51f48c04ee4278cb75f8766b4f3b805988e5e55c94fa0aacf14d0a803b61
7
+ data.tar.gz: 1c71b7c581ecfd14435c1b57251d10c8bb4199ae82b5c0d1b88e41ff85afcc072faea3b968e6f1441379740de4cd904d65414fb71eb9ac4aff93184178592a8a
data/README.md CHANGED
@@ -1,39 +1,460 @@
1
- # WebFunction
1
+ # web_function
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
3
+ A [Web Function](https://webfunction.org) client for Ruby.
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/web_function`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ Web Function is a way to design APIs. There are no verbs and no nested URLs. You
6
+ call an endpoint with a POST request, the path names the action, and the JSON
7
+ body carries the data. This gem lets you call those endpoints from Ruby.
8
+
9
+ ```ruby
10
+ client = WebFunction::Client.from_package_endpoint("https://api.example.com/package")
11
+
12
+ client.find_user(id: "123")
13
+ # => { "id" => "123", "name" => "Ada" }
14
+ ```
15
+
16
+ ## Table of contents
17
+
18
+ - [Why Web Function](#why-web-function)
19
+ - [Installation](#installation)
20
+ - [Quick start](#quick-start)
21
+ - [Clients](#clients)
22
+ - [Calling endpoints](#calling-endpoints)
23
+ - [Authentication](#authentication)
24
+ - [Versioning](#versioning)
25
+ - [Inspecting a package](#inspecting-a-package)
26
+ - [Error handling](#error-handling)
27
+ - [Pipelining](#pipelining)
28
+ - [Custom HTTP client](#custom-http-client)
29
+ - [Low-level requests](#low-level-requests)
30
+ - [Command line tool](#command-line-tool)
31
+ - [Development](#development)
32
+ - [Contributing](#contributing)
33
+ - [License](#license)
34
+
35
+ ## Why Web Function
36
+
37
+ A Web Function API has a few simple rules:
38
+
39
+ - Every call is an HTTP POST.
40
+ - The request body is a JSON object.
41
+ - The response body is any JSON value.
42
+ - A `200` status means success and the body is the return value.
43
+ - A `400` status means the request was bad and the body explains why.
44
+ - Any other status is an error you handle yourself.
45
+
46
+ On top of that, an API can publish a **package**. A package is a JSON document
47
+ that lists the endpoints, their arguments, their return types, and their docs.
48
+ This gem reads a package and gives you a client that calls those endpoints as if
49
+ they were Ruby methods.
50
+
51
+ You can read the full specification at [webfunction.org](https://webfunction.org).
6
52
 
7
53
  ## Installation
8
54
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
55
+ Add the gem to your project:
56
+
57
+ ```bash
58
+ bundle add web_function
59
+ ```
10
60
 
11
- Install the gem and add to the application's Gemfile by executing:
61
+ Or install it on its own:
12
62
 
13
63
  ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
64
+ gem install web_function
65
+ ```
66
+
67
+ The gem needs Ruby 3.1 or newer.
68
+
69
+ ## Quick start
70
+
71
+ Point a client at a package URL and start calling endpoints:
72
+
73
+ ```ruby
74
+ require "web_function"
75
+
76
+ client = WebFunction::Client.from_package_endpoint("https://api.example.com/package")
77
+
78
+ # An endpoint named "list-items" becomes the method "list_items".
79
+ items = client.list_items(limit: 10)
80
+ # => [{ "id" => 1 }, { "id" => 2 }]
81
+
82
+ # Pass a bearer token for endpoints that need authentication.
83
+ secure = WebFunction::Client.from_package_endpoint(
84
+ "https://api.example.com/package",
85
+ bearer_auth: "my-token",
86
+ )
87
+
88
+ secure.create_item(name: "Notebook")
89
+ # => { "id" => 3, "name" => "Notebook" }
90
+ ```
91
+
92
+ ## Clients
93
+
94
+ A `WebFunction::Client` wraps a package and turns each endpoint into a method.
95
+
96
+ You usually build a client from a package URL. The gem fetches the package,
97
+ reads its endpoints, and returns a ready client:
98
+
99
+ ```ruby
100
+ client = WebFunction::Client.from_package_endpoint("https://api.example.com/package")
101
+ ```
102
+
103
+ If you already have a package in memory, build the client from that instead.
104
+ This avoids the extra request:
105
+
106
+ ```ruby
107
+ package = WebFunction::Package.from_hash(
108
+ "base_url" => "https://api.example.com/",
109
+ "endpoints" => [
110
+ { "name" => "list-items" },
111
+ { "name" => "create-item" },
112
+ ],
113
+ )
114
+
115
+ client = WebFunction::Client.from_package(package)
116
+ ```
117
+
118
+ Both builders accept the same options:
119
+
120
+ | Option | Description |
121
+ | --- | --- |
122
+ | `bearer_auth` | A bearer token sent with every call. |
123
+ | `version` | A version string sent in the `Api-Version` header. |
124
+ | `pipelined` | When `true`, calls are batched into one request. See [Pipelining](#pipelining). |
125
+
126
+ You can also overwrite these using the `Client#bearer_auth=`, `Client#version=`
127
+ and `Client#pipeline=` attribute writers after having instantiated a client.
128
+
129
+ ## Calling endpoints
130
+
131
+ Endpoint names use dashes, like `list-items`. The client exposes them as Ruby
132
+ methods with underscores, like `list_items`. You pass arguments as keywords:
133
+
134
+ ```ruby
135
+ client.list_items(limit: 10, offset: 20)
136
+ ```
137
+
138
+ The return value is the parsed JSON response. It can be a hash, an array, a
139
+ string, a number, a boolean, or `nil`.
140
+
141
+ ```ruby
142
+ client.get_count # => 42
143
+ client.list_items # => [{ "id" => 1 }]
144
+ client.find_user(id: "1") # => { "id" => "1", "name" => "Ada" }
145
+ ```
146
+
147
+ If you prefer to call an endpoint by its name, use `call`:
148
+
149
+ ```ruby
150
+ client.call("list-items", limit: 10)
151
+ ```
152
+
153
+ Calling an endpoint that the package does not define raises `NoMethodError`:
154
+
155
+ ```ruby
156
+ client.does_not_exist
157
+ # => NoMethodError
158
+ ```
159
+
160
+ ## Authentication
161
+
162
+ Some endpoints need a bearer token. Pass it when you build the client and the
163
+ gem adds an `Authorization: Bearer <token>` header to every call:
164
+
165
+ ```ruby
166
+ client = WebFunction::Client.from_package_endpoint(
167
+ "https://api.example.com/package",
168
+ bearer_auth: "my-token",
169
+ )
170
+
171
+ client.list_orders
15
172
  ```
16
173
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
174
+ The gem does not handle login. How you obtain the token is up to you. To find
175
+ out whether an endpoint needs a token, check its `bearer_auth?` flag:
176
+
177
+ ```ruby
178
+ endpoint = client.package.endpoint("list-orders")
179
+ endpoint.bearer_auth? # => true
180
+ endpoint.capture_bearer? # => false
181
+ ```
182
+
183
+ ## Versioning
184
+
185
+ A versioned package selects its version through the `Api-Version` header. Pass a
186
+ version string when you build the client:
187
+
188
+ ```ruby
189
+ client = WebFunction::Client.from_package_endpoint(
190
+ "https://api.example.com/package",
191
+ version: "2024-01-01",
192
+ )
193
+ ```
194
+
195
+ You can ask a package whether it is versioned and which versions it offers:
196
+
197
+ ```ruby
198
+ package = client.package
199
+ package.versioned? # => true
200
+ package.version # => "2024-01-01"
201
+ package.versions # => ["2023-06-01", "2024-01-01"]
202
+ ```
203
+
204
+ ## Inspecting a package
205
+
206
+ A package describes itself. You can read its metadata, walk its endpoints, and
207
+ look at the arguments and outputs of each one. This is useful for building docs
208
+ or for checking a call before you make it.
209
+
210
+ ```ruby
211
+ package = client.package
212
+
213
+ package.name # => "Example API"
214
+ package.base_url # => "https://api.example.com/"
215
+ package.docs # => "Markdown documentation for the package."
216
+ package.endpoints # => [#<WebFunction::Endpoint ...>, ...]
217
+ ```
218
+
219
+ Look up a single endpoint by name. Underscores and dashes both work:
220
+
221
+ ```ruby
222
+ endpoint = package.endpoint("find-user")
223
+ # Same as:
224
+ endpoint = package.endpoint(:find_user)
225
+
226
+ endpoint.name # => "find-user"
227
+ endpoint.docs # => "Retrieves user data."
228
+ endpoint.returns # => ["object"]
229
+ endpoint.group # => "Users"
230
+ ```
231
+
232
+ Each endpoint lists the arguments it takes:
233
+
234
+ ```ruby
235
+ endpoint.arguments
236
+ # => [#<WebFunction::Argument ...>]
237
+
238
+ id = endpoint.argument("id")
239
+ id.name # => "id"
240
+ id.type # => "string"
241
+ id.required? # => true
242
+ id.optional? # => false
243
+ id.choices # => []
244
+ id.docs # => "Identifier of the user."
245
+ ```
246
+
247
+ It also lists the attributes it returns when the return type is an object:
248
+
249
+ ```ruby
250
+ name = endpoint.attribute("name")
251
+ name.name # => "name"
252
+ name.type # => "string"
253
+ name.nullable? # => false
254
+ name.values # => []
255
+ ```
256
+
257
+ You can call an endpoint object directly once it belongs to a client:
258
+
259
+ ```ruby
260
+ endpoint = client.package.endpoint("find-user")
261
+ endpoint.call(id: "123")
262
+ # => { "id" => "123", "name" => "Ada" }
263
+ ```
264
+
265
+ ## Error handling
266
+
267
+ Every error this gem raises inherits from `WebFunction::Error`. Each error
268
+ carries a `code` and optional `details`.
269
+
270
+ ```ruby
271
+ begin
272
+ client.find_user(id: "missing")
273
+ rescue WebFunction::Error => e
274
+ e.code # => "USER_NOT_FOUND"
275
+ e.message # => "No user with that id."
276
+ e.details # => { "id" => "missing" }
277
+ end
278
+ ```
279
+
280
+ These are the error classes:
281
+
282
+ | Class | Raised when |
283
+ | --- | --- |
284
+ | `WebFunction::BadRequestError` | The server replied with status `400`. |
285
+ | `WebFunction::UnexpectedStatusCodeError` | The server replied with a status other than `200` or `400`. |
286
+ | `WebFunction::JsonParseError` | The response body was not valid JSON. |
287
+ | `WebFunction::UnresolvedPromiseError` | A pipeline promise was read before it resolved. |
288
+
289
+ When the server returns a `400`, the body is an error triple. A triple is a
290
+ JSON array with three parts: a code, a message, and details. The gem reads the
291
+ triple and fills in the error:
292
+
293
+ ```json
294
+ ["USER_NOT_FOUND", "No user with that id.", { "id": "missing" }]
295
+ ```
296
+
297
+ ```ruby
298
+ rescue WebFunction::BadRequestError => e
299
+ e.code # => "USER_NOT_FOUND"
300
+ e.message # => "No user with that id."
301
+ e.details # => { "id" => "missing" }
302
+ ```
303
+
304
+ If the body is not a triple, the gem still raises `BadRequestError` with the
305
+ code `WFN_BAD_REQUEST_ERROR` and puts the raw body in `details`.
306
+
307
+ An endpoint can document the errors it may return. Read them when the endpoint
308
+ uses the `error_triple` flag:
309
+
310
+ ```ruby
311
+ endpoint.errors
312
+ # => [#<WebFunction::DocumentedError code="USER_NOT_FOUND" ...>]
313
+
314
+ error = endpoint.error("USER_NOT_FOUND")
315
+ error.code # => "USER_NOT_FOUND"
316
+ error.docs # => "Returned when no user matches the id."
317
+ ```
318
+
319
+ The package can document shared errors too:
320
+
321
+ ```ruby
322
+ package.errors
323
+ package.error("RATE_LIMITED")
324
+ ```
325
+
326
+ ## Pipelining
327
+
328
+ Pipelining sends several calls in one HTTP request. The server runs them in
329
+ order and you can feed the output of one call into the next. This cuts the
330
+ number of round trips.
331
+
332
+ Build a pipelined client and each call returns a `WebFunction::Promise` instead
333
+ of a value. A promise stands in for a result that does not exist yet:
334
+
335
+ ```ruby
336
+ client = WebFunction::Client.from_package_endpoint(
337
+ "https://api.example.com/package",
338
+ pipelined: true,
339
+ )
340
+
341
+ user = client.find_user(id: "123") # => a Promise
342
+ order = client.create_order(user_id: user["id"]) # uses the first result
343
+
344
+ order.resolve
345
+ # => { "id" => "order-1", "user_id" => "123" }
346
+ ```
347
+
348
+ Reading `user["id"]` before the call runs does not return a value. It returns a
349
+ path into the future result. The gem sends that path to the server, and the
350
+ server fills it in when it runs the second call. Calling `resolve` runs the
351
+ whole pipeline and returns the value.
352
+
353
+ Once a pipeline runs, every promise from that batch holds its value:
354
+
355
+ ```ruby
356
+ user.resolve # runs the pipeline
357
+ order.value # already available, no extra request
358
+ ```
359
+
360
+ You can also drive a pipeline by hand with `WebFunction::Pipeline`. Each step is
361
+ a hash with a `url`, `headers`, and `body`:
362
+
363
+ ```ruby
364
+ pipeline = WebFunction::Pipeline.new("https://api.example.com/run-pipeline")
365
+
366
+ pipeline.add_step(url: "https://api.example.com/a", headers: {}, body: {})
367
+ pipeline.add_step(url: "https://api.example.com/b", headers: {}, body: {})
368
+
369
+ pipeline.execute(returns: :all)
370
+ # => [{ "a" => 1 }, { "b" => 2 }]
371
+ ```
372
+
373
+ The `returns` option controls what comes back:
374
+
375
+ - `:all` returns every step result as an array. This is the default.
376
+ - `:last` returns only the last step result.
377
+ - A JSONPath string returns the value at that path, for example `"$[0].id"`.
378
+
379
+ ## Custom HTTP client
380
+
381
+ By default the gem makes requests with [Excon](https://github.com/excon/excon).
382
+ You can swap in any HTTP client by setting `WebFunction::Request.http_client` to
383
+ an object that responds to `call`.
384
+
385
+ The object receives the URL, the headers, and the JSON body. It must return a
386
+ two-element array of the status code and the raw response body:
387
+
388
+ ```ruby
389
+ WebFunction::Request.http_client = ->(url, headers, body) do
390
+ response = MyHttp.post(url, headers: headers, body: body)
391
+ [response.status, response.body]
392
+ end
393
+ ```
394
+
395
+ This is also handy in tests, where you can return a canned response without
396
+ making a real request:
397
+
398
+ ```ruby
399
+ WebFunction::Request.http_client = ->(_url, _headers, _body) do
400
+ [200, JSON.generate({ "id" => "123" })]
401
+ end
402
+ ```
403
+
404
+ ## Low-level requests
405
+
406
+ If you do not need a package, you can call a single endpoint URL directly with
407
+ `WebFunction::Request`:
408
+
409
+ ```ruby
410
+ WebFunction::Request.execute(
411
+ "https://api.example.com/find-user",
412
+ bearer_auth: "my-token",
413
+ version: "2024-01-01",
414
+ args: { id: "123" },
415
+ )
416
+ # => { "id" => "123", "name" => "Ada" }
417
+ ```
418
+
419
+ The request adds the standard headers, posts the JSON body, and parses the
420
+ response. It raises the same errors described in [Error handling](#error-handling).
421
+
422
+ ## Command line tool
423
+
424
+ The gem ships with a `wfn` command. Use it to call an endpoint from the shell.
425
+ Arguments go in a JSON string:
18
426
 
19
427
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
428
+ wfn call https://api.example.com/find-user '{"id":"123"}'
21
429
  ```
22
430
 
23
- ## Usage
431
+ Pass a bearer token with `--auth` and a version with `--version`:
432
+
433
+ ```bash
434
+ wfn call --auth my-token --version 2024-01-01 \
435
+ https://api.example.com/list-orders '{}'
436
+ ```
24
437
 
25
- TODO: Write usage instructions here
438
+ The command prints the response as formatted JSON. On an error it prints the
439
+ code, the message, and the details, then exits with a non-zero status.
26
440
 
27
441
  ## Development
28
442
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
443
+ After you check out the repo, run `bin/setup` to install dependencies. Then run
444
+ `rake test` to run the tests. Run `bin/console` for a prompt where you can
445
+ experiment with the code.
30
446
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
447
+ To install the gem on your machine, run `bundle exec rake install`. To release a
448
+ new version, update the version in `lib/web_function/version.rb`, then run
449
+ `bundle exec rake release`. This creates a git tag, pushes the commits and the
450
+ tag, and pushes the `.gem` file to [rubygems.org](https://rubygems.org).
32
451
 
33
452
  ## Contributing
34
453
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/web_function.
454
+ Bug reports and pull requests are welcome on GitHub at
455
+ [github.com/robinclart/web_function](https://github.com/robinclart/web_function).
36
456
 
37
457
  ## License
38
458
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
459
+ This gem is open source under the terms of the
460
+ [MIT License](https://opensource.org/licenses/MIT).
@@ -10,10 +10,9 @@ module WebFunction
10
10
  class Argument
11
11
  include Flaggable
12
12
 
13
- def initialize(name:, type:, hint: nil, group: nil, choices: [], flags: [], docs: nil)
13
+ def initialize(name:, type:, group: nil, choices: [], flags: [], docs: nil)
14
14
  @name = name
15
- @type = type
16
- @hint = hint
15
+ @type = Type.parse(type)
17
16
  @group = group
18
17
  @choices = choices
19
18
  @flags = flags
@@ -43,7 +42,6 @@ module WebFunction
43
42
  new(
44
43
  name: argument["name"],
45
44
  type: argument["type"],
46
- hint: argument["hint"],
47
45
  group: argument["group"],
48
46
  choices: [*argument["choices"]],
49
47
  flags: Utils.normalize_array_of_strings(argument["flags"]),
@@ -83,16 +81,6 @@ module WebFunction
83
81
  #
84
82
  attr_reader :type
85
83
 
86
- # A hint that further defines what kind of value to expect for an argument.
87
- #
88
- # See the [hints section][1] on the Web Function website for the full list of possible hints.
89
- #
90
- # @return [String]
91
- #
92
- # [1]: https://webfunction.org/package#hints
93
- #
94
- attr_reader :hint
95
-
96
84
  # A name used to categorize or group similar arguments together. This should be used by documentation tools to
97
85
  # organize related arguments.
98
86
  #
@@ -12,10 +12,9 @@ module WebFunction
12
12
  class Attribute
13
13
  include Flaggable
14
14
 
15
- def initialize(name:, type:, hint: nil, values: [], flags: [], docs: nil)
15
+ def initialize(name:, type:, values: [], flags: [], docs: nil)
16
16
  @name = name
17
- @type = type
18
- @hint = hint
17
+ @type = Type.parse(type)
19
18
  @values = values
20
19
  @flags = flags
21
20
  @docs = docs.to_s
@@ -44,7 +43,6 @@ module WebFunction
44
43
  new(
45
44
  name: attribute["name"],
46
45
  type: attribute["type"],
47
- hint: attribute["hint"],
48
46
  values: [*attribute["values"]],
49
47
  flags: Utils.normalize_array_of_strings(attribute["flags"]),
50
48
  docs: attribute["docs"],
@@ -88,15 +86,6 @@ module WebFunction
88
86
  #
89
87
  attr_reader :type
90
88
 
91
- # A string hinting about the semantics of this attribute. See the [hints section][1] for possible values and
92
- # documentation tooling guidance.
93
- #
94
- # [1]: https://webfunction.org/package#hints
95
- #
96
- # @return [String, nil]
97
- #
98
- attr_reader :hint
99
-
100
89
  # An array specifying the exact, case-sensitive values that may be returned for this attribute. Each value in the
101
90
  # values array must conform to the data type specified in the "type" key.
102
91
  #
@@ -18,7 +18,7 @@ module WebFunction
18
18
  end
19
19
 
20
20
  class << self
21
- # Creates a new {Client} from an url.
21
+ # Creates a new {Client} from an endpoint.
22
22
  #
23
23
  # @param url [String] The URL of the package endpoint
24
24
  # @param bearer_auth [String] The bearer authentication token
@@ -34,6 +34,22 @@ module WebFunction
34
34
  from_package(package, bearer_auth: bearer_auth, version: version, pipelined: pipelined)
35
35
  end
36
36
 
37
+ # Creates a new {Client} from an url.
38
+ #
39
+ # @param url [String] The URL of the package endpoint
40
+ # @param bearer_auth [String] The bearer authentication token
41
+ # @param version [String] The API version to use
42
+ # @param pipelined [Boolean] Whether to have the client use call pipelining
43
+ #
44
+ # @return [Client]
45
+ #
46
+ def from_url(url, bearer_auth: nil, version: nil, pipelined: false)
47
+ body = ::WebFunction::Utils.get_body_from_url(url, extra_query_params: { api_version: version })
48
+ package = ::WebFunction::Package.from_hash(::JSON.parse(body))
49
+
50
+ from_package(package, bearer_auth: bearer_auth, version: version, pipelined: pipelined)
51
+ end
52
+
37
53
  # Creates a new {Client} from a {Package}.
38
54
  #
39
55
  # @param package [Package] A package
@@ -95,10 +111,32 @@ module WebFunction
95
111
  #
96
112
  attr_reader :package
97
113
 
114
+ # The bearer authentication token.
115
+ #
116
+ # @param bearer_auth [String] The bearer authentication token
117
+ #
118
+ attr_writer :bearer_auth
119
+
120
+ # The API version to use.
121
+ #
122
+ # @param version [String] The API version to use
123
+ #
124
+ attr_writer :version
125
+
126
+ # The pipeline to use.
127
+ #
128
+ # @param pipeline [Pipeline] The pipeline to use
129
+ #
130
+ attr_writer :pipeline
131
+
98
132
  def methods # :nodoc:
99
133
  @endpoints.keys
100
134
  end
101
135
 
136
+ def nil? # :nodoc:
137
+ false
138
+ end
139
+
102
140
  def respond_to_missing?(method_name, include_private = false) # :nodoc:
103
141
  @endpoints[method_name]
104
142
  end
@@ -25,10 +25,9 @@ module WebFunction
25
25
  class Endpoint
26
26
  include Flaggable
27
27
 
28
- def initialize(name:, returns: [], hints: [], flags: [], group: nil, docs: nil, arguments: [], attributes: [], errors: [])
28
+ def initialize(name:, returns:, flags: [], group: nil, docs: nil, arguments: [], attributes: [], errors: [])
29
29
  @name = name
30
- @returns = returns
31
- @hints = hints
30
+ @returns = Type.parse(returns)
32
31
  @flags = flags
33
32
  @group = group
34
33
  @docs = docs
@@ -66,10 +65,13 @@ module WebFunction
66
65
  return
67
66
  end
68
67
 
68
+ unless endpoint["returns"]
69
+ return
70
+ end
71
+
69
72
  new(
70
73
  name: endpoint["name"],
71
- returns: Utils.normalize_array_of_strings(endpoint["returns"]),
72
- hints: Utils.normalize_array_of_strings(endpoint["hints"]),
74
+ returns: endpoint["returns"],
73
75
  flags: Utils.normalize_array_of_strings(endpoint["flags"]),
74
76
  group: endpoint["group"],
75
77
  docs: endpoint["docs"].to_s,
@@ -119,16 +121,6 @@ module WebFunction
119
121
  #
120
122
  attr_reader :returns
121
123
 
122
- # Hints for the endpoint's return value. Each hint's base JSON type matches one of the endpoint's {#returns} types,
123
- # and at most one hint is supplied per base JSON type. See the [hints section][1] on the Web Function website for
124
- # the complete list of allowed hints.
125
- #
126
- # @return [Array<String>]
127
- #
128
- # [1]: https://webfunction.org/package#hints
129
- #
130
- attr_reader :hints
131
-
132
124
  # A name used to categorize or group similar endpoints together. This should be used by documentation tools to
133
125
  # organize related endpoints.
134
126
  #