web_function 0.4.1 → 0.6.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: cc10b3e4f558c7bec819e5f9029f672df3e37e5e5c66790df2e1159d2b06acec
4
- data.tar.gz: deb73a647c14e886ef7b3be4e69a5a080fead74ef00119b56af86b57462452d5
3
+ metadata.gz: c025fe8ca089b9531bc819d3154438bb29eb0b5fa823509b212fb23858403b5f
4
+ data.tar.gz: 6e2efbc2c7a4ebaef81ef95b8485b8822666a01967ebb46a3220ddab2caf26a1
5
5
  SHA512:
6
- metadata.gz: 37b3060aefd75b936ce0c5e9f3e1ec7e20f84ab118840f262d4631b020cebba24aaa9df251c1bb37560ff881df42412c688b6c6f519bacfe377aa4a5230676d3
7
- data.tar.gz: 6240884af37b04edf66426653b9d8de19445be9817c93b624bccbb2ed0ca290ccce6dd98669ea2c2d7c114c8ec03c4b6c0fee1871c33e36fb7aeeb65d92517fb
6
+ metadata.gz: acbc56f7b1b5ec89c17eb8f44699197ded36a0c3db4e722b57087bf164c0fe1f2a3c62e15223971e488721008891ce1714d2b2ce8a82dd0267b950694e8b4a46
7
+ data.tar.gz: 9028501396902aa1adb0ebe548eaa4f16bf9065c11e3777512ae5ef31652e9d007dffa5b2bdff60c95c5bb451221145b6a5e08d0bd73a89e34befe73db534597
data/exe/wfn CHANGED
@@ -2,3 +2,45 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "web_function"
5
+ require "json"
6
+
7
+ # Usage:
8
+ # wfn call --auth token endpoint_url key1=value1 [key2=value2 ...]
9
+
10
+ auth_token = nil
11
+ auth_index = ARGV.index("--auth")
12
+ if auth_index
13
+ auth_token = ARGV[auth_index + 1]
14
+ ARGV.slice!(auth_index, 2)
15
+ end
16
+
17
+ version = nil
18
+ version_index = ARGV.index("--version")
19
+ if version_index
20
+ version = ARGV[version_index + 1]
21
+ ARGV.slice!(version_index, 2)
22
+ end
23
+
24
+ command = ARGV[0]
25
+
26
+ case command
27
+ when "call"
28
+ endpoint_url = ARGV[1]
29
+ args = JSON.parse(ARGV[2] || "{}")
30
+
31
+ begin
32
+ response = WebFunction::Request.execute(endpoint_url, bearer_auth: auth_token, version: version, args: args)
33
+ puts JSON.pretty_generate(response)
34
+ rescue WebFunction::Error => e
35
+ puts e
36
+ warn "error: [#{e.code}] #{e.message}"
37
+ warn ""
38
+ if e.details
39
+ warn JSON.pretty_generate(e.details).lines.map { |line| "| #{line}" }.join
40
+ end
41
+ exit 2
42
+ end
43
+ else
44
+ warn "Usage:\n wfn docs package_url endpoint_name\n wfn call --auth token endpoint_url key1=value1 [key2=value2 ...]"
45
+ exit 1
46
+ end
@@ -1,29 +1,135 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFunction
4
+ # Arguments are used to define Web Function {Endpoint} request parameters.
5
+ #
6
+ # See the [arguments section][0] on the Web Function website for more details.
7
+ #
8
+ # [0]: https://webfunction.org/package#arguments
9
+ #
4
10
  class Argument
5
- def initialize(argument)
6
- @argument = argument
7
- end
11
+ include Flaggable
8
12
 
9
- def name
10
- @argument["name"]
13
+ def initialize(name:, type:, hint: nil, group: nil, choices: [], flags: [], docs: nil)
14
+ @name = name
15
+ @type = type
16
+ @hint = hint
17
+ @group = group
18
+ @choices = choices
19
+ @flags = flags
20
+ @docs = docs.to_s
11
21
  end
12
22
 
13
- def type
14
- @argument["type"]
15
- end
23
+ class << self
24
+ # Instantiate a new Argument from a hash, typically coming from a {Package}.
25
+ #
26
+ # @param argument [Hash]
27
+ #
28
+ # @return [Argument, nil]
29
+ #
30
+ def from_hash(argument)
31
+ unless argument.is_a?(Hash)
32
+ return
33
+ end
34
+
35
+ unless argument["name"]
36
+ return
37
+ end
16
38
 
17
- def choices
18
- @argument["choices"]
39
+ unless argument["type"]
40
+ return
41
+ end
42
+
43
+ new(
44
+ name: argument["name"],
45
+ type: argument["type"],
46
+ hint: argument["hint"],
47
+ group: argument["group"],
48
+ choices: [*argument["choices"]],
49
+ flags: Utils.normalize_array_of_strings(argument["flags"]),
50
+ docs: argument["docs"],
51
+ )
52
+ end
53
+
54
+ # Instantiate a collection of Argument from an array of hash, typically coming from a {Package}. Uses
55
+ # {Argument#from_hash} under the hood.
56
+ #
57
+ # @param arguments [Array<Hash>]
58
+ #
59
+ # @return [Array<Argument>]
60
+ #
61
+ def from_array(arguments)
62
+ Utils.normalize_array arguments do |argument|
63
+ from_hash(argument)
64
+ end
65
+ end
19
66
  end
20
67
 
21
- def flags
22
- @argument["flags"]
68
+ # The name of the argument.
69
+ #
70
+ # @return [String]
71
+ #
72
+ attr_reader :name
73
+
74
+ # The type of the argument. It must be one of:
75
+ #
76
+ # - object
77
+ # - array
78
+ # - string
79
+ # - number
80
+ # - boolean
81
+ #
82
+ # @return [String]
83
+ #
84
+ attr_reader :type
85
+
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
+ # A name used to categorize or group similar arguments together. This should be used by documentation tools to
97
+ # organize related arguments.
98
+ #
99
+ # @return [String]
100
+ #
101
+ attr_reader :group
102
+
103
+ # An array specifying the exact, case-sensitive values that are permitted for this argument. Each value in the
104
+ # choices array must conform to the data type specified in the argument's type key.
105
+ #
106
+ # Note that if the argument type is array, choices may contain strings or numbers representing the allowed values
107
+ # that can be included in the array.
108
+ #
109
+ # @return [Array]
110
+ #
111
+ attr_reader :choices
112
+
113
+ # Description of the argument. It must be formatted as markdown.
114
+ #
115
+ # @return [String]
116
+ #
117
+ attr_reader :docs
118
+
119
+ # Whether the argument is required.
120
+ #
121
+ # @return [Boolean]
122
+ #
123
+ def required?
124
+ flag?("required")
23
125
  end
24
126
 
25
- def docs
26
- @argument["docs"]
127
+ # Whether the argument is optional.
128
+ #
129
+ # @return [Boolean]
130
+ #
131
+ def optional?
132
+ !required?
27
133
  end
28
134
  end
29
135
  end
@@ -1,29 +1,124 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFunction
4
+ # Attributes define output fields that may be produced and returned by a Web Function {Endpoint} when the type of the
5
+ # return is `object`.
6
+ #
7
+ # See the [attributes section][0] on the Web Function website for more details about attribute definitions,
8
+ # recognized keys, and usage.
9
+ #
10
+ # [0]: https://webfunction.org/package#attributes
11
+ #
4
12
  class Attribute
5
- def initialize(attribute)
6
- @attribute = attribute
7
- end
13
+ include Flaggable
8
14
 
9
- def name
10
- @attribute["name"]
15
+ def initialize(name:, type:, hint: nil, values: [], flags: [], docs: nil)
16
+ @name = name
17
+ @type = type
18
+ @hint = hint
19
+ @values = values
20
+ @flags = flags
21
+ @docs = docs.to_s
11
22
  end
12
23
 
13
- def type
14
- @attribute["type"]
15
- end
24
+ class << self
25
+ # Creates a new Attribute from a hash. Typically coming from a {Package}.
26
+ #
27
+ # @param attribute [Hash] The attribute hash
28
+ #
29
+ # @return [Attribute] A new Attribute instance
30
+ #
31
+ def from_hash(attribute)
32
+ unless attribute.is_a?(Hash)
33
+ return
34
+ end
16
35
 
17
- def values
18
- @attribute["values"]
19
- end
36
+ unless attribute["name"]
37
+ return
38
+ end
39
+
40
+ unless attribute["type"]
41
+ return
42
+ end
43
+
44
+ new(
45
+ name: attribute["name"],
46
+ type: attribute["type"],
47
+ hint: attribute["hint"],
48
+ values: [*attribute["values"]],
49
+ flags: Utils.normalize_array_of_strings(attribute["flags"]),
50
+ docs: attribute["docs"],
51
+ )
52
+ end
20
53
 
21
- def flags
22
- @attribute["flags"]
54
+ # Creates a new Attribute from an array of hashes. Typically coming from a {Package}. Uses {Attribute#from_hash}
55
+ # under the hood.
56
+ #
57
+ # @param attributes [Array<Hash>] The attribute array of hashes
58
+ #
59
+ # @return [Array<Attribute>] A new array of Attribute instances
60
+ #
61
+ def from_array(attributes)
62
+ Utils.normalize_array attributes do |attribute|
63
+ from_hash(attribute)
64
+ end
65
+ end
23
66
  end
24
67
 
25
- def docs
26
- @attribute["docs"]
68
+ # The name of the attribute as it will appear in the endpoint's output
69
+ # object.
70
+ #
71
+ # This is required for the argument to be valid.
72
+ #
73
+ # @return [String]
74
+ #
75
+ attr_reader :name
76
+
77
+ # The type of value returned for this attribute. Must be one of:
78
+ #
79
+ # - object
80
+ # - array
81
+ # - string
82
+ # - number
83
+ # - boolean
84
+ #
85
+ # This is required for the argument to be valid.
86
+ #
87
+ # @return [String]
88
+ #
89
+ attr_reader :type
90
+
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
+ # An array specifying the exact, case-sensitive values that may be returned for this attribute. Each value in the
101
+ # values array must conform to the data type specified in the "type" key.
102
+ #
103
+ # This is useful for attributes that can only take a select set of values (enums or constants).
104
+ #
105
+ # @return [Array]
106
+ #
107
+ attr_reader :values
108
+
109
+ # A markdown string describing this attribute and its purpose in the output object. Used by documentation tools,
110
+ # and highly recommended.
111
+ #
112
+ # @return [String]
113
+ #
114
+ attr_reader :docs
115
+
116
+ # Whether the attribute can be null.
117
+ #
118
+ # @return [Boolean]
119
+ #
120
+ def nullable?
121
+ flag?("nullable")
27
122
  end
28
123
  end
29
124
  end
@@ -1,52 +1,116 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFunction
4
- class Client
5
- def initialize(package, bearer_auth: nil, pipeline: nil)
4
+ # A {Client} is a wrapper around a Web Function {Package} that provides a convenient interface for invoking endpoints.
5
+ #
6
+ # @example
7
+ # client = WebFunction::Client.from_package_endpoint("https://api.webfunction.com/package")
8
+ # client.list_items(a: "b") # => { "c" => "d" }
9
+ #
10
+ class Client < BasicObject
11
+ def initialize(base_url:, endpoints: [], package: nil, bearer_auth: nil, version: nil, pipeline: nil)
6
12
  @package = package
7
- @endpoints = package.endpoints.to_h { |e| [e.name.gsub("-", "_").to_sym, e] }
13
+ @base_url = base_url
14
+ @endpoints = endpoints.to_h { |e| [e.gsub("-", "_").to_sym, e] }
8
15
  @bearer_auth = bearer_auth
16
+ @version = version
9
17
  @pipeline = pipeline
10
18
  end
11
19
 
12
- attr_reader :package
20
+ class << self
21
+ # Creates a new {Client} from an url.
22
+ #
23
+ # @param url [String] The URL of the package endpoint
24
+ # @param bearer_auth [String] The bearer authentication token
25
+ # @param version [String] The API version to use
26
+ # @param pipelined [Boolean] Whether to have the client use call pipelining
27
+ #
28
+ # @return [Client]
29
+ #
30
+ def from_package_endpoint(url, bearer_auth: nil, version: nil, pipelined: false)
31
+ response = ::WebFunction::Request.execute(url, bearer_auth: bearer_auth, version: version)
32
+ package = ::WebFunction::Package.from_hash(response)
33
+
34
+ from_package(package, bearer_auth: bearer_auth, version: version, pipelined: pipelined)
35
+ end
36
+
37
+ # Creates a new {Client} from a {Package}.
38
+ #
39
+ # @param package [Package] A package
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_package(package, bearer_auth: nil, version: nil, pipelined: nil)
47
+ pipeline = nil
13
48
 
14
- def self.from_package_endpoint(url, bearer_auth: nil, pipeline_url: nil, pipeline: nil)
15
- response = Endpoint.invoke(url, bearer_auth: bearer_auth)
16
- package = Package.new(response)
49
+ if pipelined
50
+ pipeline = package.pipeline
51
+ end
17
52
 
18
- if pipeline.is_a?(String)
19
- pipeline = WebFunction::Pipeline.new(pipeline)
53
+ client = new(
54
+ package: package,
55
+ base_url: package.base_url,
56
+ endpoints: package.endpoints.map(&:name),
57
+ bearer_auth: bearer_auth,
58
+ version: version,
59
+ pipeline: pipeline,
60
+ )
61
+
62
+ package.endpoints.each do |endpoint|
63
+ endpoint.client = client
64
+ end
65
+
66
+ client
20
67
  end
68
+ end
69
+
70
+ # Call an endpoint by name with the given arguments.
71
+ #
72
+ # @param endpoint_name [String] The name of the endpoint to call
73
+ # @param args [Hash] The arguments to send to the endpoint
74
+ #
75
+ # @return [Object] The decoded response returned by the endpoint
76
+ #
77
+ def call(endpoint_name, args = {})
78
+ url = ::URI.join(@base_url, endpoint_name).to_s
79
+ request = ::WebFunction::Request.new(url,
80
+ bearer_auth: @bearer_auth,
81
+ version: @version,
82
+ args: args,
83
+ )
21
84
 
22
- new(package, bearer_auth: bearer_auth, pipeline: pipeline)
85
+ if @pipeline
86
+ @pipeline.add_step(request.as_pipeline_step)
87
+ else
88
+ request.execute
89
+ end
23
90
  end
24
91
 
25
- def methods
26
- super + @endpoints.keys
92
+ # The package that this client is wrapping.
93
+ #
94
+ # @return [Package]
95
+ #
96
+ attr_reader :package
97
+
98
+ def methods # :nodoc:
99
+ @endpoints.keys
27
100
  end
28
101
 
29
- def respond_to_missing?(method_name, include_private = false)
30
- @endpoints[method_name] || super
102
+ def respond_to_missing?(method_name, include_private = false) # :nodoc:
103
+ @endpoints[method_name]
31
104
  end
32
105
 
33
- def method_missing(method_name, *args)
34
- endpoint = @endpoints[method_name]
106
+ def method_missing(method_name, *args) # :nodoc:
107
+ endpoint_name = @endpoints[method_name]
35
108
 
36
- unless endpoint
109
+ unless endpoint_name
37
110
  super
38
111
  end
39
112
 
40
- url = URI.join(@package.base_url, endpoint.name).to_s
41
- args = args.first
42
-
43
- if @pipeline
44
- step = Endpoint.invoke(url, bearer_auth: @bearer_auth, args: args, as_step: true)
45
- promise = @pipeline.add_step(step)
46
- return promise
47
- end
48
-
49
- Endpoint.invoke(url, bearer_auth: @bearer_auth, args: args)
113
+ call(endpoint_name, args.first)
50
114
  end
51
115
  end
52
116
  end
@@ -1,15 +1,67 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebFunction
4
+ # Represents an error definition as described in a Web Function package.
5
+ #
6
+ # An error definition documents the possible errors that an endpoint might return, including a machine-readable error
7
+ # code and a human-readable description.
8
+ #
9
+ # See the [error definition documentation][0] on the Web Function website for more details, including recognized keys
10
+ # and usage recommendations.
11
+ #
12
+ # [0]: https://webfunction.org/package#error-definition
13
+ #
2
14
  class DocumentedError
3
- def initialize(error)
4
- @error = error
15
+ def initialize(code:, docs: nil)
16
+ @code = code
17
+ @docs = docs.to_s
5
18
  end
6
19
 
7
- def code
8
- @error["code"]
9
- end
20
+ class << self
21
+ # Creates a new DocumentedError from a hash.
22
+ #
23
+ # @param error [Hash] The error hash
24
+ #
25
+ # @return [DocumentedError] A new DocumentedError instance
26
+ #
27
+ def from_hash(error)
28
+ unless error.is_a?(Hash)
29
+ return
30
+ end
31
+
32
+ unless error["code"]
33
+ return
34
+ end
10
35
 
11
- def docs
12
- @error["docs"]
36
+ new(
37
+ code: error["code"],
38
+ docs: error["docs"],
39
+ )
40
+ end
41
+
42
+ # Creates a new DocumentedError from an array of hashes. Uses {DocumentedError#from_hash} under the hood.
43
+ #
44
+ # @param errors [Array<Hash>] The error array of hashes
45
+ #
46
+ # @return [Array<DocumentedError>] A new array of DocumentedError instances
47
+ #
48
+ def from_array(errors)
49
+ Utils.normalize_array errors do |error|
50
+ from_hash(error)
51
+ end
52
+ end
13
53
  end
54
+
55
+ # The machine-readable code of the error.
56
+ #
57
+ # @return [String]
58
+ #
59
+ attr_reader :code
60
+
61
+ # The documentation of the error.
62
+ #
63
+ # @return [String]
64
+ #
65
+ attr_reader :docs
14
66
  end
15
- end
67
+ end