Class: WSDL::Operation

Inherits:
Object
  • Object
show all
Defined in:
lib/wsdl/operation.rb

Overview

Represents a callable SOAP operation.

Operation instances carry mutable per-request state (#prepare, #reset!, #invoke) and are therefore not thread-safe. Create a separate Operation per thread or per request. The underlying Definition is frozen and safe to share.

Constant Summary collapse

ENCODING =

Default XML encoding used in SOAP request headers.

Returns:

  • (String)
'UTF-8'
CONTENT_TYPE =

HTTP Content-Type base values keyed by SOAP version.

Returns:

  • (Hash{String => String})
{
  '1.1' => 'text/xml',
  '1.2' => 'application/soap+xml'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(op_data, endpoint, http, config: Config.new) ⇒ Operation

Creates a new Operation from Definition operation data.

All read-only derived fields (contract, element parts, RPC wrapper) are eagerly computed from the frozen op_data during construction so they are safe for concurrent reads without synchronization.

Parameters:

  • op_data (Hash{Symbol => Object})

    operation hash from Definition#operation_data

  • endpoint (String)

    the SOAP endpoint URL

  • http (Object)

    an HTTP client instance

  • config (Config) (defaults to: Config.new)

    behavioral configuration



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/wsdl/operation.rb', line 34

def initialize(op_data, endpoint, http, config: Config.new)
  @op_data = op_data
  @http = http
  @config = config

  @endpoint = endpoint
  @soap_version = op_data[:soap_version]
  @soap_action = op_data[:soap_action]
  @encoding = ENCODING

  @request_document = nil
  @security = Security::Config.new
  @http_header_overrides = {}

  build_derived_fields(op_data)
end

Instance Attribute Details

#contractWSDL::Contract::OperationContract (readonly)

Returns canonical operation contract metadata.



140
141
142
# File 'lib/wsdl/operation.rb', line 140

def contract
  @contract
end

#encodingObject

Returns the value of attribute encoding.



62
63
64
# File 'lib/wsdl/operation.rb', line 62

def encoding
  @encoding
end

#endpointObject

Returns the value of attribute endpoint.



62
63
64
# File 'lib/wsdl/operation.rb', line 62

def endpoint
  @endpoint
end

#soap_actionObject

Returns the value of attribute soap_action.



62
63
64
# File 'lib/wsdl/operation.rb', line 62

def soap_action
  @soap_action
end

#soap_versionObject

Returns the value of attribute soap_version.



62
63
64
# File 'lib/wsdl/operation.rb', line 62

def soap_version
  @soap_version
end

Instance Method Details

#http_headersHash{String => String}

Returns the merged HTTP headers for the SOAP request.

Auto-generated headers (Content-Type, SOAPAction) are computed from the current SOAP version, action, and encoding. Any headers set via #http_headers= are merged on top, so user-provided values win on conflict while auto-generated defaults are preserved.

Returns:

  • (Hash{String => String})


199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/wsdl/operation.rb', line 199

def http_headers
  headers = {}
  content_type = [CONTENT_TYPE[soap_version], "charset=#{encoding}"]

  case soap_version
  when '1.1'
    headers['SOAPAction'] = soap_action.nil? ? '' : %("#{soap_action}")
  when '1.2'
    content_type << %(action="#{soap_action}") if soap_action && !soap_action.empty?
  end

  headers['Content-Type'] = content_type.join(';')
  headers.merge(@http_header_overrides)
end

#http_headers=(headers) ⇒ void

This method returns an undefined value.

Merges custom headers on top of auto-generated HTTP headers.

The provided headers are stored and merged over the auto-generated defaults each time #http_headers is called. User-provided values win on conflict. Call #reset! to clear overrides.

Parameters:

  • headers (Hash{String => String})


222
223
224
# File 'lib/wsdl/operation.rb', line 222

def http_headers=(headers)
  @http_header_overrides = headers
end

#input_element_nameString?

Returns the name of the first input body element.

For document/literal wrapped operations, this is the element name that appears in the SOAP body (which may differ from the operation name).

Examples:

operation.input_element_name  # => "InitialRequest"

Returns:

  • (String, nil)


74
75
76
# File 'lib/wsdl/operation.rb', line 74

def input_element_name
  input_body_parts.first&.name
end

#input_styleString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Low-level input binding style from the WSDL.

Prefer #contract .style for public introspection.

Returns:

  • (String)

    e.g. document/literal



289
290
291
# File 'lib/wsdl/operation.rb', line 289

def input_style
  @op_data[:input_style]
end

#invoke { ... } ⇒ Response

Invokes this SOAP operation.

When a block is given, calls #prepare first — combining request building and invocation into a single step.

Examples:

One-step invoke

response = operation.invoke do
  tag('GetUser') { tag('id', 123) }
end

Yields:

  • optional request DSL block (forwarded to #prepare)

Returns:

Raises:



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/wsdl/operation.rb', line 264

def invoke(&block)
  prepare(&block) if block
  ensure_request_definition!

  http_response = @http.post(endpoint, http_headers, to_xml)
  enforce_response_size_limit!(http_response)

  response = Response.new(
    http_response:,
    output_body_parts:,
    output_header_parts:,
    output_style:,
    verification: @security.response_verification_options
  )

  @security.response_policy.enforce!(response)
  response
end

#nameString

Returns the operation name from the WSDL definition.

Examples:

operation.name  # => "getBank"

Returns:

  • (String)


58
59
60
# File 'lib/wsdl/operation.rb', line 58

def name
  @op_data[:name]
end

#output_namespaceString?

Returns the output body namespace from the WSDL binding.

For RPC/literal operations, this is the namespace used on the response wrapper element. For document/literal operations this may be nil.

Examples:

operation.output_namespace  # => "http://apiNamespace.com"

Returns:

  • (String, nil)


89
90
91
# File 'lib/wsdl/operation.rb', line 89

def output_namespace
  @op_data[:rpc_output_namespace]
end

#output_styleString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Low-level output binding style from the WSDL.

Prefer #contract .style for public introspection.

Returns:

  • (String)

    e.g. document/literal



299
300
301
# File 'lib/wsdl/operation.rb', line 299

def output_style
  @op_data[:output_style]
end

#prepare { ... } ⇒ self

Prepares request envelope from DSL and validates it immediately.

Yields:

  • DSL prepare block

Returns:

  • (self)

Raises:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/wsdl/operation.rb', line 146

def prepare(&block)
  raise RequestDslError, 'operation.prepare requires a block' unless block

  if @request_document
    raise RequestDslError,
      'operation.prepare was already called. ' \
      'Use operation.reset! to clear the previous request before preparing a new one'
  end

  document = Request::Envelope.new
  security = Security::Config.new
  context = Request::DSLContext.new(document:, security:, limits: @config.limits)
  context.instance_exec(&block)

  Request::Validator.new(
    contract:,
    strictness: @config.strictness,
    schema_complete: @op_data[:schema_complete]
  ).validate!(document)

  Request::SecurityConflictDetector.new(document:, security:).validate!

  @request_document = document
  @security = security
  self
end

#prepared?Boolean

Returns whether a request has been prepared via #prepare.

Returns:

  • (Boolean)


176
177
178
# File 'lib/wsdl/operation.rb', line 176

def prepared?
  !@request_document.nil?
end

#reset!self

Clears the prepared request, allowing #prepare to be called again. Also clears any custom HTTP header overrides set via #http_headers=.

Returns:

  • (self)


184
185
186
187
188
189
# File 'lib/wsdl/operation.rb', line 184

def reset!
  @request_document = nil
  @security = Security::Config.new
  @http_header_overrides = {}
  self
end

#to_xml(pretty: false) ⇒ String

Serializes the prepared request envelope to SOAP envelope XML.

By default, returns compact XML (no extra whitespace). Pass pretty: true to get indented output for debugging or logging.

Examples:

Compact XML (default)

operation.to_xml

Pretty-printed for inspection

puts operation.to_xml(pretty: true)

Parameters:

  • pretty (Boolean) (defaults to: false)

    format XML with indentation (default: false)

Returns:

  • (String)


240
241
242
243
244
245
246
247
248
# File 'lib/wsdl/operation.rb', line 240

def to_xml(pretty: false)
  ensure_request_definition!

  document = prepare_serializable_document(@request_document || Request::Envelope.new)
  serializer = Request::Serializer.new(document:, soap_version:, pretty:)
  return serializer.serialize unless @security.configured?

  Security::SecurityHeader.new(@security).apply(serializer.to_document)
end