web_function 0.5.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.
@@ -1,107 +1,123 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFunction
4
- # # Argument
5
- #
6
- # Arguments used by Web Function endpoints to define its input parameters.
4
+ # Arguments are used to define Web Function {Endpoint} request parameters.
7
5
  #
8
6
  # See the [arguments section][0] on the Web Function website for more details.
9
7
  #
10
8
  # [0]: https://webfunction.org/package#arguments
11
9
  #
12
10
  class Argument
13
- def initialize(argument)
14
- @argument = argument
11
+ include Flaggable
12
+
13
+ def initialize(name:, type:, group: nil, choices: [], flags: [], docs: nil)
14
+ @name = name
15
+ @type = Type.parse(type)
16
+ @group = group
17
+ @choices = choices
18
+ @flags = flags
19
+ @docs = docs.to_s
15
20
  end
16
21
 
17
- # ## Name
18
- #
19
- # The name of the argument.
20
- #
21
- # @return [String]
22
- #
23
- def name
24
- @argument["name"]
22
+ class << self
23
+ # Instantiate a new Argument from a hash, typically coming from a {Package}.
24
+ #
25
+ # @param argument [Hash]
26
+ #
27
+ # @return [Argument, nil]
28
+ #
29
+ def from_hash(argument)
30
+ unless argument.is_a?(Hash)
31
+ return
32
+ end
33
+
34
+ unless argument["name"]
35
+ return
36
+ end
37
+
38
+ unless argument["type"]
39
+ return
40
+ end
41
+
42
+ new(
43
+ name: argument["name"],
44
+ type: argument["type"],
45
+ group: argument["group"],
46
+ choices: [*argument["choices"]],
47
+ flags: Utils.normalize_array_of_strings(argument["flags"]),
48
+ docs: argument["docs"],
49
+ )
50
+ end
51
+
52
+ # Instantiate a collection of Argument from an array of hash, typically coming from a {Package}. Uses
53
+ # {Argument#from_hash} under the hood.
54
+ #
55
+ # @param arguments [Array<Hash>]
56
+ #
57
+ # @return [Array<Argument>]
58
+ #
59
+ def from_array(arguments)
60
+ Utils.normalize_array arguments do |argument|
61
+ from_hash(argument)
62
+ end
63
+ end
25
64
  end
26
65
 
27
- # ## Type
28
- #
29
- # The type of the argument. It must be one of:
30
- # - object
31
- # - array
32
- # - string
33
- # - number
34
- # - boolean
66
+ # The name of the argument.
35
67
  #
36
68
  # @return [String]
37
69
  #
38
- def type
39
- @argument["type"]
40
- end
70
+ attr_reader :name
41
71
 
42
- # ## Hint
43
- #
44
- # The hint of the argument
72
+ # The type of the argument. It must be one of:
45
73
  #
46
- # See the [hints section][1] on the Web Function website for the full list
47
- # of possible hints.
74
+ # - object
75
+ # - array
76
+ # - string
77
+ # - number
78
+ # - boolean
48
79
  #
49
80
  # @return [String]
50
81
  #
51
- # [1]: https://webfunction.org/package#hints
52
- #
53
- def hint
54
- @argument["hint"]
55
- end
82
+ attr_reader :type
56
83
 
57
- # ## Group
58
- #
59
- # A name used to categorize or group similar arguments together. This
60
- # should be used by documentation tools to organize related arguments.
84
+ # A name used to categorize or group similar arguments together. This should be used by documentation tools to
85
+ # organize related arguments.
61
86
  #
62
87
  # @return [String]
63
88
  #
64
- def group
65
- @argument["group"]
66
- end
89
+ attr_reader :group
67
90
 
68
- # ## Choices
69
- #
70
- # An array specifying the exact, case-sensitive values that are permitted
71
- # for this argument. Each value in the choices array must conform to the
72
- # data type specified in the argument's type key.
91
+ # An array specifying the exact, case-sensitive values that are permitted for this argument. Each value in the
92
+ # choices array must conform to the data type specified in the argument's type key.
73
93
  #
74
- # Note that if the argument type is array, choices may contain strings or
75
- # numbers representing the allowed values that can be included in the array.
94
+ # Note that if the argument type is array, choices may contain strings or numbers representing the allowed values
95
+ # that can be included in the array.
76
96
  #
77
97
  # @return [Array]
78
98
  #
79
- def choices
80
- [*@argument["choices"]]
81
- end
99
+ attr_reader :choices
82
100
 
83
- # ## Flags
101
+ # Description of the argument. It must be formatted as markdown.
84
102
  #
85
- # List of argument flags. See the [available flags section][2] on the Web
86
- # Function website for a complete list of flags available at the
87
- # argument level.
103
+ # @return [String]
88
104
  #
89
- # @return [Array<String>]
105
+ attr_reader :docs
106
+
107
+ # Whether the argument is required.
90
108
  #
91
- # [2]: https://webfunction.org/package#available-flags
109
+ # @return [Boolean]
92
110
  #
93
- def flags
94
- [*@argument["flags"]].map { |flag| flag.to_s }
111
+ def required?
112
+ flag?("required")
95
113
  end
96
114
 
97
- # ## Docs
115
+ # Whether the argument is optional.
98
116
  #
99
- # Description of the argument. It must be formatted as markdown.
100
- #
101
- # @return [String]
117
+ # @return [Boolean]
102
118
  #
103
- def docs
104
- @argument["docs"].to_s
119
+ def optional?
120
+ !required?
105
121
  end
106
122
  end
107
123
  end
@@ -1,100 +1,113 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFunction
4
- # # Attribute
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`.
5
6
  #
6
- # An Attribute defines an output field that may be produced and returned by a
7
- # Web Function endpoint.
8
- #
9
- # See the [attributes section][0] on the Web Function website for more
10
- # details about attribute definitions, recognized keys, and usage.
7
+ # See the [attributes section][0] on the Web Function website for more details about attribute definitions,
8
+ # recognized keys, and usage.
11
9
  #
12
10
  # [0]: https://webfunction.org/package#attributes
13
11
  #
14
12
  class Attribute
15
- def initialize(attribute)
16
- @attribute = attribute
13
+ include Flaggable
14
+
15
+ def initialize(name:, type:, values: [], flags: [], docs: nil)
16
+ @name = name
17
+ @type = Type.parse(type)
18
+ @values = values
19
+ @flags = flags
20
+ @docs = docs.to_s
17
21
  end
18
22
 
19
- # ## Name
20
- #
21
- # The name of the attribute as it will appear in the endpoint's output
22
- # object.
23
- #
24
- # @return [String]
25
- #
26
- def name
27
- @attribute["name"]
23
+ class << self
24
+ # Creates a new Attribute from a hash. Typically coming from a {Package}.
25
+ #
26
+ # @param attribute [Hash] The attribute hash
27
+ #
28
+ # @return [Attribute] A new Attribute instance
29
+ #
30
+ def from_hash(attribute)
31
+ unless attribute.is_a?(Hash)
32
+ return
33
+ end
34
+
35
+ unless attribute["name"]
36
+ return
37
+ end
38
+
39
+ unless attribute["type"]
40
+ return
41
+ end
42
+
43
+ new(
44
+ name: attribute["name"],
45
+ type: attribute["type"],
46
+ values: [*attribute["values"]],
47
+ flags: Utils.normalize_array_of_strings(attribute["flags"]),
48
+ docs: attribute["docs"],
49
+ )
50
+ end
51
+
52
+ # Creates a new Attribute from an array of hashes. Typically coming from a {Package}. Uses {Attribute#from_hash}
53
+ # under the hood.
54
+ #
55
+ # @param attributes [Array<Hash>] The attribute array of hashes
56
+ #
57
+ # @return [Array<Attribute>] A new array of Attribute instances
58
+ #
59
+ def from_array(attributes)
60
+ Utils.normalize_array attributes do |attribute|
61
+ from_hash(attribute)
62
+ end
63
+ end
28
64
  end
29
65
 
30
- # ## Type
31
- #
32
- # The type of value returned for this attribute. Must be one of:
33
- # - object
34
- # - array
35
- # - string
36
- # - number
37
- # - boolean
66
+ # The name of the attribute as it will appear in the endpoint's output
67
+ # object.
38
68
  #
39
- # This is a required string.
69
+ # This is required for the argument to be valid.
40
70
  #
41
71
  # @return [String]
42
72
  #
43
- def type
44
- @attribute["type"]
45
- end
73
+ attr_reader :name
46
74
 
47
- # ## Hint
75
+ # The type of value returned for this attribute. Must be one of:
48
76
  #
49
- # A string hinting about the semantics of this attribute. See the
50
- # [hints section][1] for possible values and documentation tooling guidance.
77
+ # - object
78
+ # - array
79
+ # - string
80
+ # - number
81
+ # - boolean
51
82
  #
52
- # [1]: https://webfunction.org/package#hints
83
+ # This is required for the argument to be valid.
53
84
  #
54
- # @return [String, nil]
85
+ # @return [String]
55
86
  #
56
- def hint
57
- @attribute["hint"]
58
- end
87
+ attr_reader :type
59
88
 
60
- # ## Values
61
- #
62
- # An array specifying the exact, case-sensitive values that may be returned
63
- # for this attribute. Each value in the values array must conform to the
64
- # data type specified in the "type" key.
89
+ # An array specifying the exact, case-sensitive values that may be returned for this attribute. Each value in the
90
+ # values array must conform to the data type specified in the "type" key.
65
91
  #
66
- # This is useful for attributes that can only take a select set of values
67
- # (enums or constants).
92
+ # This is useful for attributes that can only take a select set of values (enums or constants).
68
93
  #
69
94
  # @return [Array]
70
95
  #
71
- def values
72
- [*@attribute["values"]]
73
- end
96
+ attr_reader :values
74
97
 
75
- # ## Flags
76
- #
77
- # An array of attribute flags. Flags describe special characteristics or
78
- # behaviors of attributes. See the [available flags section][2] for
79
- # complete list and documentation.
98
+ # A markdown string describing this attribute and its purpose in the output object. Used by documentation tools,
99
+ # and highly recommended.
80
100
  #
81
- # [2]: https://webfunction.org/package#available-flags
82
- #
83
- # @return [Array<String>]
101
+ # @return [String]
84
102
  #
85
- def flags
86
- [*@attribute["flags"]].map(&:to_s)
87
- end
103
+ attr_reader :docs
88
104
 
89
- # ## Docs
90
- #
91
- # A markdown string describing this attribute and its purpose in the output
92
- # object. Used by documentation tools, and highly recommended.
105
+ # Whether the attribute can be null.
93
106
  #
94
- # @return [String]
107
+ # @return [Boolean]
95
108
  #
96
- def docs
97
- @attribute["docs"].to_s
109
+ def nullable?
110
+ flag?("nullable")
98
111
  end
99
112
  end
100
113
  end
@@ -1,72 +1,154 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFunction
4
- # # Client
5
- #
6
- # A Client is a wrapper around a Web Function package that provides a
7
- # convenient interface for invoking endpoints.
4
+ # A {Client} is a wrapper around a Web Function {Package} that provides a convenient interface for invoking endpoints.
8
5
  #
9
6
  # @example
10
7
  # client = WebFunction::Client.from_package_endpoint("https://api.webfunction.com/package")
11
8
  # client.list_items(a: "b") # => { "c" => "d" }
12
9
  #
13
10
  class Client < BasicObject
14
- def initialize(package, bearer_auth: nil, pipeline: nil)
11
+ def initialize(base_url:, endpoints: [], package: nil, bearer_auth: nil, version: nil, pipeline: nil)
15
12
  @package = package
16
- @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] }
17
15
  @bearer_auth = bearer_auth
16
+ @version = version
18
17
  @pipeline = pipeline
19
18
  end
20
19
 
21
- attr_reader :package
20
+ class << self
21
+ # Creates a new {Client} from an endpoint.
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 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
+
53
+ # Creates a new {Client} from a {Package}.
54
+ #
55
+ # @param package [Package] A package
56
+ # @param bearer_auth [String] The bearer authentication token
57
+ # @param version [String] The API version to use
58
+ # @param pipelined [Boolean] Whether to have the client use call pipelining
59
+ #
60
+ # @return [Client]
61
+ #
62
+ def from_package(package, bearer_auth: nil, version: nil, pipelined: nil)
63
+ pipeline = nil
64
+
65
+ if pipelined
66
+ pipeline = package.pipeline
67
+ end
68
+
69
+ client = new(
70
+ package: package,
71
+ base_url: package.base_url,
72
+ endpoints: package.endpoints.map(&:name),
73
+ bearer_auth: bearer_auth,
74
+ version: version,
75
+ pipeline: pipeline,
76
+ )
77
+
78
+ package.endpoints.each do |endpoint|
79
+ endpoint.client = client
80
+ end
81
+
82
+ client
83
+ end
84
+ end
85
+
86
+ # Call an endpoint by name with the given arguments.
87
+ #
88
+ # @param endpoint_name [String] The name of the endpoint to call
89
+ # @param args [Hash] The arguments to send to the endpoint
90
+ #
91
+ # @return [Object] The decoded response returned by the endpoint
92
+ #
93
+ def call(endpoint_name, args = {})
94
+ url = ::URI.join(@base_url, endpoint_name).to_s
95
+ request = ::WebFunction::Request.new(url,
96
+ bearer_auth: @bearer_auth,
97
+ version: @version,
98
+ args: args,
99
+ )
22
100
 
23
- # ## Instantiates a new Client from a package endpoint
101
+ if @pipeline
102
+ @pipeline.add_step(request.as_pipeline_step)
103
+ else
104
+ request.execute
105
+ end
106
+ end
107
+
108
+ # The package that this client is wrapping.
24
109
  #
25
- # Creates a new Client from a package endpoint.
110
+ # @return [Package]
111
+ #
112
+ attr_reader :package
113
+
114
+ # The bearer authentication token.
26
115
  #
27
- # @param url [String] The URL of the package endpoint
28
116
  # @param bearer_auth [String] The bearer authentication token
29
- # @param pipeline_url [String] The URL of the pipeline endpoint
30
- # @param pipeline [Pipeline] The pipeline to use
31
117
  #
32
- # @return [Client] A new Client instance
118
+ attr_writer :bearer_auth
119
+
120
+ # The API version to use.
121
+ #
122
+ # @param version [String] The API version to use
33
123
  #
34
- def self.from_package_endpoint(url, bearer_auth: nil, pipeline_url: nil, pipeline: nil)
35
- response = ::WebFunction::Endpoint.invoke(url, bearer_auth: bearer_auth)
36
- package = ::WebFunction::Package.new(response)
124
+ attr_writer :version
37
125
 
38
- if pipeline_url.is_a?(::String)
39
- pipeline = ::WebFunction::Pipeline.new(pipeline_url)
40
- end
126
+ # The pipeline to use.
127
+ #
128
+ # @param pipeline [Pipeline] The pipeline to use
129
+ #
130
+ attr_writer :pipeline
41
131
 
42
- new(package, bearer_auth: bearer_auth, pipeline: pipeline)
132
+ def methods # :nodoc:
133
+ @endpoints.keys
43
134
  end
44
135
 
45
- def methods #:nodoc:
46
- super + @endpoints.keys
136
+ def nil? # :nodoc:
137
+ false
47
138
  end
48
139
 
49
- def respond_to_missing?(method_name, include_private = false) #:nodoc:
50
- @endpoints[method_name] || super
140
+ def respond_to_missing?(method_name, include_private = false) # :nodoc:
141
+ @endpoints[method_name]
51
142
  end
52
143
 
53
- def method_missing(method_name, *args) #:nodoc:
54
- endpoint = @endpoints[method_name]
144
+ def method_missing(method_name, *args) # :nodoc:
145
+ endpoint_name = @endpoints[method_name]
55
146
 
56
- unless endpoint
147
+ unless endpoint_name
57
148
  super
58
149
  end
59
150
 
60
- url = ::URI.join(@package.base_url, endpoint.name).to_s
61
- args = args.first
62
-
63
- if @pipeline
64
- step = ::WebFunction::Endpoint.step(url, bearer_auth: @bearer_auth, args: args)
65
- promise = @pipeline.add_step(step)
66
- return promise
67
- end
68
-
69
- ::WebFunction::Endpoint.invoke(url, bearer_auth: @bearer_auth, args: args)
151
+ call(endpoint_name, args.first)
70
152
  end
71
153
  end
72
154
  end
@@ -1,39 +1,67 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebFunction
2
- # # DocumentedError
3
- #
4
4
  # Represents an error definition as described in a Web Function package.
5
5
  #
6
- # An error definition documents the possible errors that an endpoint might return,
7
- # including a machine-readable error code and a human-readable description.
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
8
  #
9
- # See the [error definition documentation][0] on the Web Function website for more details,
10
- # including recognized keys and usage recommendations.
9
+ # See the [error definition documentation][0] on the Web Function website for more details, including recognized keys
10
+ # and usage recommendations.
11
11
  #
12
12
  # [0]: https://webfunction.org/package#error-definition
13
13
  #
14
14
  class DocumentedError
15
- def initialize(error)
16
- @error = error
15
+ def initialize(code:, docs: nil)
16
+ @code = code
17
+ @docs = docs.to_s
17
18
  end
18
19
 
19
- # ## Code
20
- #
21
- # The code of the error.
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
35
+
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
53
+ end
54
+
55
+ # The machine-readable code of the error.
22
56
  #
23
57
  # @return [String]
24
58
  #
25
- def code
26
- @error["code"]
27
- end
59
+ attr_reader :code
28
60
 
29
- # ## Docs
30
- #
31
61
  # The documentation of the error.
32
62
  #
33
63
  # @return [String]
34
64
  #
35
- def docs
36
- @error["docs"].to_s
37
- end
65
+ attr_reader :docs
38
66
  end
39
67
  end