savon-multipart 2.1.1 → 2.1.3

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
- SHA1:
3
- metadata.gz: 1453752c3356664f6f81bceaf030316157f4d7d4
4
- data.tar.gz: 9511e7c2d636c54af0eb11dcf1e6c6264422d2ed
2
+ SHA256:
3
+ metadata.gz: 4473b669ad70af6dfa4371b373da602fe0a9e982b70e2813473506bdf5d28146
4
+ data.tar.gz: 3270f043522e840c94fc3b0ac7b419ccc35e98aa9071c4f63b04844425724fa6
5
5
  SHA512:
6
- metadata.gz: f3b2cbf00edc90989ced02fa321cb5e370e7f3938ea092a91036d4fdddadf6acad9cbc7905445c12dd43a108f0cfe09913d0ac8909c71a2c3adf7f2b9a15b6bc
7
- data.tar.gz: 43975f967153b5107f1fa1050fa6c131991148c5ab81acb3434abfa98a12a2599b4cbbe231df02de9235af538ce3a921f814193fea3da3a797ae3849bce45924
6
+ metadata.gz: df2da88ea80eb1d39cbb091d2139f47552e7353938daf76a33534553676140608cf84b5e65fb41b6870c6dc4192e5104875f8c8c0a58947d0963f48979d01169
7
+ data.tar.gz: 336c526a3f42e0d03560aa29d4046ea62b0efa6eba3a0058abf74e90ae80a8ec6165411926ab68595615fd9348acb928ba0e30c02805c5db6a663e41f4123fbb
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a bug report
4
+
5
+ ---
6
+
7
+ <!-- Please don't delete this template or we'll close your issue -->
8
+ <!-- Before creating an issue, please ensure you are using the latest version of savon-multipart. -->
9
+
10
+ # Bug report
11
+
12
+
13
+ **Current behavior:**
14
+
15
+
16
+ **Expected behavior:**
17
+
18
+
19
+ **Steps to reproduce current behavior:**
20
+
21
+ <!-- The more of the following you can give us, the faster we can reproduce and fix it. -->
22
+
23
+ * Client setup and the operation call (the Ruby code you ran):
24
+
25
+ * The request XML built by the client (or just the part that's wrong):
26
+
27
+ * The response you got back (body, and for a transport issue the status and headers):
28
+
29
+ * The WSDL of the service, or a snippet of the operation involved:
30
+
31
+
32
+ **System information:**
33
+
34
+ * ruby version:
35
+ * savon-multipart version:
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a new feature for savon-multipart
4
+
5
+ ---
6
+
7
+ <!-- Please don't delete this template or we'll close your issue -->
8
+
9
+ ## Feature request
10
+
11
+ <!-- Issues which contain questions or support requests will be closed. -->
12
+
13
+ **Motivation for this feature:**
14
+
15
+ <!-- Please describe the use case or problem this would solve. -->
16
+
17
+ **Proposed implementation:**
18
+
19
+ <!-- Please describe at least one proposed implementation design. -->
20
+
21
+ **Would this introduce a breaking change?**
22
+
23
+ <!-- If yes, please describe the impact and a migration path for existing applications. -->
24
+
25
+ **Are you willing to work on this yourself?**
26
+ yes
27
+
@@ -0,0 +1,16 @@
1
+ **What kind of change is this?**
2
+
3
+ <!-- E.g. a bugfix, feature, refactoring, build change, etc… -->
4
+
5
+ **Did you add tests for your changes?**
6
+
7
+ <!-- Note that we won't merge your changes if you don't add tests -->
8
+
9
+ **Summary of changes**
10
+
11
+ <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->
12
+ <!-- Try to link to an open issue for more information. -->
13
+
14
+ **Other information**
15
+
16
+
@@ -0,0 +1,8 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ cooldown:
8
+ default-days: 7
@@ -0,0 +1,81 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read # checkout only, nothing else
10
+
11
+ jobs:
12
+ audit:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16
+ with:
17
+ persist-credentials: false
18
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
19
+ with:
20
+ ruby-version: "3.4"
21
+ bundler-cache: true
22
+ - name: Audit dependencies
23
+ run: bundle exec bundle-audit check --update
24
+ - name: Audit Ruby runtime
25
+ # CVE-2026-41316 is an ERB flaw fixed in Ruby 4.0.3 and not yet backported.
26
+ # Ignored on affected rubies only. See:
27
+ # https://www.ruby-lang.org/en/news/2026/04/21/erb-cve-2026-41316/
28
+ # https://github.com/advisories/GHSA-q339-8rmv-2mhv
29
+ run: |
30
+ ignore=""
31
+ if ruby -e 'exit 1 unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new("4.0.3")'; then
32
+ ignore="--ignore CVE-2026-41316"
33
+ fi
34
+ bundle exec ruby-audit check $ignore
35
+
36
+ zizmor:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
40
+ with:
41
+ persist-credentials: false
42
+ - name: Audit workflows
43
+ uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
44
+ with:
45
+ advanced-security: false # fail the build on findings instead of uploading SARIF
46
+
47
+ lint:
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
51
+ with:
52
+ persist-credentials: false
53
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
54
+ with:
55
+ ruby-version: "3.4"
56
+ bundler-cache: true
57
+ - name: RuboCop
58
+ run: bundle exec rubocop --format github
59
+
60
+ test:
61
+ runs-on: ubuntu-latest
62
+ strategy:
63
+ fail-fast: false
64
+ matrix:
65
+ ruby: ['3.0', 3.1, 3.2, 3.3, 3.4, '4.0', truffleruby, head, truffleruby-head]
66
+ # Head builds exist to surface upstream regressions early.
67
+ # They should not block the build.
68
+ continue-on-error: ${{ matrix.ruby == 'head' || matrix.ruby == 'truffleruby-head' }}
69
+ env:
70
+ RAILS_ENV: test
71
+ steps:
72
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
73
+ with:
74
+ persist-credentials: false
75
+ - name: Set up Ruby
76
+ uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
77
+ with:
78
+ ruby-version: ${{ matrix.ruby }}
79
+ bundler-cache: true # bundle installs and caches dependencies
80
+ - name: Run tests
81
+ run: bundle exec rake --trace
@@ -0,0 +1,22 @@
1
+ name: Push gem
2
+ on:
3
+ release:
4
+ types: [published]
5
+ jobs:
6
+ push:
7
+ runs-on: ubuntu-latest
8
+ permissions:
9
+ id-token: write
10
+ contents: write
11
+ environment: release
12
+ steps:
13
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
14
+ with:
15
+ persist-credentials: false
16
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
17
+ with:
18
+ ruby-version: "3"
19
+ bundler-cache: false
20
+ - name: Install dependencies
21
+ run: bundle install
22
+ - uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
data/.rubocop.yml ADDED
@@ -0,0 +1,37 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ plugins:
4
+ - rubocop-rake
5
+ - rubocop-rspec
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 3.0
9
+ SuggestExtensions: true
10
+
11
+ Gemspec/RequireMFA:
12
+ Enabled: true
13
+
14
+ Gemspec/RequiredRubyVersion:
15
+ Enabled: false
16
+ # Gemspec floors are intentionally low; raising them is a separate breaking-change decision.
17
+
18
+ Naming/FileName:
19
+ Exclude:
20
+ - "lib/savon-multipart.rb"
21
+ # Entry-point file matches the gem name; renaming it would break `require "savon-multipart"`.
22
+
23
+ Layout/FirstHashElementIndentation:
24
+ EnforcedStyle: consistent
25
+
26
+ Layout/HashAlignment:
27
+ EnforcedHashRocketStyle: table
28
+
29
+ RSpec/ContextWording:
30
+ Enabled: false
31
+
32
+ Style/BlockDelimiters:
33
+ EnforcedStyle: semantic
34
+
35
+ Style/HashSyntax:
36
+ EnforcedStyle: ruby19
37
+ EnforcedShorthandSyntax: never
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,7 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # using RuboCop version 1.86.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
- ## 2.1.1 (current)
1
+ ## 2.1.3 (2026-09-16)
2
2
 
3
- * Improve detection of multipart content-type header.s
3
+ * Fix `xop:Include` parts with percent-encoded Content-IDs not being replaced in the response. ([#21](https://github.com/savonrb/savon-multipart/pull/21))
4
+
5
+ ## 2.1.2 (2022-01-20)
6
+
7
+ * Loosen dependency on `mail` gem. ([#23](https://github.com/savonrb/savon-multipart/pull/23))
8
+ * Add ability to flatten MTOM XOP parts back into the main XML document ([#17](https://github.com/savonrb/savon-multipart/pull/17))
9
+
10
+ ## 2.1.1
11
+
12
+ * Improve detection of multipart content-type header.
4
13
 
5
14
  ## 2.1.0
6
15
 
data/Gemfile CHANGED
@@ -1,9 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
  gemspec
3
5
 
4
- platform :rbx do
5
- gem 'json'
6
- gem 'racc'
7
- gem 'rubysl'
8
- gem 'rubinius-coverage'
6
+ if RUBY_VERSION > '3.1'
7
+ # net-smtp, net-pop and net-imap were removed from ruby 3.1 standard gems. See: https://github.com/rails/rails/pull/42366
8
+ # Can drop when https://github.com/mikel/mail/pull/1439 is resolved
9
+ gem 'net-imap', require: false
10
+ gem 'net-pop', require: false
11
+ gem 'net-smtp', require: false
9
12
  end
13
+
14
+ gem 'rubocop', '~> 1.86', '>= 1.86.2'
15
+ gem 'rubocop-rake', '~> 0.7.1'
16
+ gem 'rubocop-rspec', '~> 3.9'
17
+
18
+ gem 'bundler-audit', '~> 0.9.3', require: false
19
+ gem 'ruby_audit', '~> 3.1', require: false if RUBY_VERSION >= '3.1.0'
data/README.md CHANGED
@@ -1,23 +1,34 @@
1
- Savon Multipart [![Build Status](https://secure.travis-ci.org/savonrb/savon-multipart.png)](http://travis-ci.org/savonrb/savon-multipart)
2
- ===============
1
+ # Savon Multipart
3
2
 
4
- Adds multipart support (SOAP with Attachments) to [Savon](https://github.com/savonrb/savon).
5
- Please test and provide feedback so we can support as many multipart-soap messages as possible.
3
+ [![CI](https://github.com/savonrb/savon-multipart/actions/workflows/ci.yml/badge.svg)](https://github.com/savonrb/savon-multipart/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/savon-multipart.svg)](https://rubygems.org/gems/savon-multipart)
5
+
6
+ Adds multipart support (SOAP with Attachments) to [Savon](https://github.com/savonrb/savon).
6
7
 
7
8
  Installation
8
9
  ------------
9
10
 
10
- Savon Multipart is available through [Rubygems](http://rubygems.org/gems/savon-multipart) and can be installed via:
11
+ Savon Multipart is available through [Rubygems](https://rubygems.org/gems/savon-multipart) and can be installed via:
11
12
 
12
13
  ```
13
14
  $ gem install savon-multipart
14
15
  ```
15
16
 
17
+ Or in your `Gemfile`:
18
+
19
+ ```ruby
20
+ gem 'savon-multipart', '~> 2.1'
21
+ ```
22
+
16
23
  Usage
17
24
  ------------
18
25
 
19
- Just require the gem 'savon-multipart', which automatically requires 'savon' as well:
26
+ Just require the gem 'savon-multipart', '~> 2.1', which automatically requires 'savon' as well:
20
27
 
21
28
  ```
22
29
  require 'savon-multipart'
23
30
  ```
31
+
32
+ ## Ruby support
33
+
34
+ Savon Multipart requires Ruby >= 1.9.2.
data/RELEASING.md ADDED
@@ -0,0 +1,19 @@
1
+ _You'll need write access to the repository to create a release. Publishing is handled automatically via [trusted publishing](https://guides.rubygems.org/trusted-publishing/)._
2
+
3
+ ## Cutting a release
4
+
5
+ 1. On your release branch (e.g. `main`, `v2.x`), edit [CHANGELOG.md](https://github.com/savonrb/savon-multipart/blob/main/CHANGELOG.md) to finalize the new version number and list of all changes.
6
+ 2. Bump [version.rb](https://github.com/savonrb/savon-multipart/blob/main/lib/savon/multipart/version.rb) to the version you picked in previous step.
7
+ 3. **Final check**: make sure all tests are green, and that `rake build` succeeds. If not, merge any fixes back to the release branch and go to step 1.
8
+ 4. [Draft a new release](https://github.com/savonrb/savon-multipart/releases/new) on Github.
9
+ - In the "Choose a tag" field, type the version tag - e.g. `v2.12.1` - and select "Create new tag on publish".
10
+ - Use `v[version]` for the release title, and copy the changelog into the release notes.
11
+ - Click "Publish release". Github creates and pushes the tag, which triggers the release workflow.
12
+ 5. Publishing to RubyGems.org is fully automated. The [Push gem workflow](https://github.com/savonrb/savon-multipart/actions/workflows/gem_push.yml) triggers on the published release and requires approval from a release manager via the `release` environment before credentials are issued. **Before approving**: confirm that CI is green on the release commit. Approve the deployment in the Actions UI and the gem will be built and pushed automatically.
13
+
14
+ ## Updating minimum ruby version
15
+
16
+ - Update `required_ruby_version` in [savon-multipart.gemspec](https://github.com/savonrb/savon-multipart/blob/main/savon-multipart.gemspec)
17
+ - Update the test matrix in [ci.yml](https://github.com/savonrb/savon-multipart/blob/main/.github/workflows/ci.yml)
18
+ - Update [README](https://github.com/savonrb/savon-multipart/blob/main/README.md) with the correct support matrix
19
+ - Note the updated requirement in [CHANGELOG.md](https://github.com/savonrb/savon-multipart/blob/main/CHANGELOG.md)
data/Rakefile CHANGED
@@ -1,11 +1,16 @@
1
- require "bundler"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
2
4
  Bundler::GemHelper.install_tasks
3
5
 
4
- require "rspec/core/rake_task"
6
+ require 'rspec/core/rake_task'
5
7
 
6
8
  RSpec::Core::RakeTask.new do |t|
7
- t.rspec_opts = %w(-c)
9
+ t.rspec_opts = %w[-c]
8
10
  end
9
11
 
10
- task :default => :spec
11
- task :test => :spec
12
+ desc 'Run the spec suite'
13
+ task default: :spec
14
+
15
+ desc 'Run the spec suite'
16
+ task test: :spec
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mail'
4
+ require 'cgi'
2
5
 
3
6
  module Savon
4
7
  module Multipart
8
+ # Savon::Response subclass that understands multipart/related SOAP
9
+ # responses, exposing MIME parts, attachments and XOP content.
5
10
  class Response < Savon::Response
6
11
  attr_reader :parts
7
12
 
@@ -20,31 +25,75 @@ module Savon
20
25
  end
21
26
 
22
27
  def xml
23
- if multipart?
24
- parse_body unless @has_parsed_body
25
- @parts.first.body.to_s
28
+ return super unless multipart?
29
+
30
+ parse_body unless @has_parsed_body
31
+ multipart_xml
32
+ end
33
+
34
+ private
35
+
36
+ def multipart_xml
37
+ if xop?
38
+ parse_xop unless @has_parsed_xop
39
+ @xop_body
26
40
  else
27
- super
41
+ @parts.first.body.to_s
28
42
  end
29
43
  end
30
44
 
31
- private
32
45
  def multipart?
33
46
  !(http.headers['content-type'] =~ /^multipart/im).nil?
34
47
  end
35
48
 
49
+ def xop?
50
+ if multipart?
51
+ parse_body unless @has_parsed_body
52
+ !(@parts.first.header['content-type'].to_s =~ %r{^application/xop\+xml}i).nil?
53
+ else
54
+ false
55
+ end
56
+ end
57
+
36
58
  def boundary
37
59
  return unless multipart?
60
+
38
61
  @boundary ||= Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
39
62
  end
40
63
 
41
64
  def parse_body
42
65
  @parts = Mail::Part.new(
43
- :headers => http.headers,
44
- :body => http.body
66
+ headers: http.headers,
67
+ body: http.body
45
68
  ).body.split!(boundary).parts
46
69
  @has_parsed_body = true
47
70
  end
71
+
72
+ def parse_xop
73
+ parsed = Nokogiri.XML(@parts.first.body.to_s)
74
+ xop_elements = parsed.xpath('//xop:Include', xop: 'http://www.w3.org/2004/08/xop/include')
75
+
76
+ if xop_elements.empty?
77
+ @xop_body = @parts.first.body.to_s
78
+ else
79
+ replace_xop_includes(xop_elements)
80
+ @xop_body = parsed.to_s
81
+ end
82
+ @has_parsed_xop = true
83
+ end
84
+
85
+ def replace_xop_includes(xop_elements)
86
+ xop_elements.each do |xop_element|
87
+ href = xop_element.attributes['href'].to_s
88
+ cid = CGI.unescape(href[4..])
89
+ data = find_part_by_cid(cid).body.to_s
90
+ xop_element.parent.content = Base64.encode64(data).chomp
91
+ end
92
+ end
93
+
94
+ def find_part_by_cid(cid)
95
+ @parts.find { |part| part.header['content-id'].to_s == "<#{cid}>" }
96
+ end
48
97
  end
49
98
  end
50
99
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Savon
2
4
  module Multipart
3
- VERSION = "2.1.1"
5
+ VERSION = '2.1.3'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'savon'
2
4
  require 'savon/multipart/version'
3
5
  require 'savon/multipart/response'
@@ -1,29 +1,31 @@
1
- lib = File.expand_path("../lib", __FILE__)
2
- $:.unshift lib unless $:.include? lib
1
+ # frozen_string_literal: true
3
2
 
4
- require "savon/multipart/version"
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
5
+
6
+ require 'savon/multipart/version'
5
7
 
6
8
  Gem::Specification.new do |s|
7
- s.name = "savon-multipart"
9
+ s.name = 'savon-multipart'
8
10
  s.version = Savon::Multipart::VERSION
9
- s.authors = ["Martin Westin", "Daniel Harrington"]
10
- s.email = ["martin@eimermusic.com", "me@rubiii.com"]
11
- s.homepage = "http://savonrb.com"
12
- s.summary = "Heavy metal Ruby SOAP client with multipart support"
13
- s.description = "Adds multipart support (SOAP with Attachments) to Savon"
11
+ s.authors = ['Martin Westin', 'Daniel Harrington']
12
+ s.email = ['martin@eimermusic.com', 'me@rubiii.com']
13
+ s.homepage = 'http://savonrb.com'
14
+ s.summary = 'Heavy metal Ruby SOAP client with multipart support'
15
+ s.description = 'Adds multipart support (SOAP with Attachments) to Savon'
16
+ # NOTE: this floor is intentionally conservative — CI starts at Ruby 3.0, but the gem likely
17
+ # still works on older Rubies. Don't raise it just to satisfy lint or match CI without evidence of breakage.
14
18
  s.required_ruby_version = '>= 1.9.2'
15
19
 
16
- s.rubyforge_project = s.name
17
-
18
- s.add_dependency "savon", "~> 2"
19
- s.add_dependency "mail", "~> 2.5.4"
20
+ s.add_dependency 'mail', '~> 2'
21
+ s.add_dependency 'savon', '~> 2'
20
22
 
21
- s.add_development_dependency "rake"
22
- s.add_development_dependency "pry"
23
- s.add_development_dependency "rspec"
24
- s.add_development_dependency "autotest"
25
- s.add_development_dependency "transpec"
23
+ s.add_development_dependency 'autotest'
24
+ s.add_development_dependency 'pry'
25
+ s.add_development_dependency 'rake'
26
+ s.add_development_dependency 'rspec'
26
27
 
27
28
  s.files = `git ls-files`.split("\n")
28
- s.require_path = "lib"
29
+ s.require_path = 'lib'
30
+ s.metadata['rubygems_mfa_required'] = 'true'
29
31
  end
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:wsdl="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><ns1:TransactionID soapenv:actor="" soapenv:mustUnderstand="1" xsi:type="xsd:string" xmlns:ns1="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2">2011012713535811111111111</ns1:TransactionID></soapenv:Header><soapenv:Body><SubmitReq xmlns="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2"><MM7Version>5.3.0</MM7Version><SenderIdentification><VASPID>messaging</VASPID><VASID>ADM</VASID><SenderAddress><ShortCode>1111</ShortCode></SenderAddress></SenderIdentification><Recipients><To><Number>11111111111</Number></To></Recipients><ServiceCode>1</ServiceCode><MessageClass>Personal</MessageClass><ExpiryDate>2011-01-28T13:53:58Z</ExpiryDate><DeliveryReport>false</DeliveryReport><ReadReply>false</ReadReply><Priority>Normal</Priority><Subject>Test MMS via Savon</Subject><ChargedParty>Sender</ChargedParty><Content href="cid:attachment_1" allowAdaptations="true"/></SubmitReq></soapenv:Body></soapenv:Envelope>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:wsdl="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><ns1:TransactionID soapenv:actor="" soapenv:mustUnderstand="1" xsi:type="xsd:string" xmlns:ns1="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2">2011012713535811111111111</ns1:TransactionID></soapenv:Header><soapenv:Body><SubmitReq xmlns="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2"><MM7Version>5.3.0</MM7Version><SenderIdentification><VASPID>messaging</VASPID><VASID>ADM</VASID><SenderAddress><ShortCode>1111</ShortCode></SenderAddress></SenderIdentification><Recipients><To><Number>11111111111</Number></To></Recipients><ServiceCode>1</ServiceCode><MessageClass>Personal</MessageClass><ExpiryDate>2011-01-28T13:53:58Z</ExpiryDate><DeliveryReport>false</DeliveryReport><ReadReply>false</ReadReply><Priority>Normal</Priority><Subject>Test MMS via Savon</Subject><ChargedParty>Sender</ChargedParty><Content href="cid:attachment_1" allowAdaptations="true"/></SubmitReq></soapenv:Body></soapenv:Envelope>
@@ -0,0 +1,13 @@
1
+ ----==_mimepart_4d416ae62fd32_201a8043814c4724
2
+ Content-ID: <http://tempuri.org/0>
3
+ Content-Transfer-Encoding: 8bit
4
+ Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
5
+
6
+ <?xml version="1.0" encoding="UTF-8"?><envelope ><header></header><body><BinaryData><xop:Include href="cid:http://tempuri.org/1/123456789" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></BinaryData></body></envelope>
7
+ ----==_mimepart_4d416ae62fd32_201a8043814c4724
8
+ Content-ID: <http://tempuri.org/1/123456789>
9
+ Content-Transfer-Encoding: binary
10
+ Content-Type: application/octet-stream
11
+
12
+ BinaryDataGoesHere
13
+ ----==_mimepart_4d416ae62fd32_201a8043814c4724--
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'savon-multipart'
4
+
5
+ RSpec.describe Savon::Multipart::Response do
6
+ let(:multipart_content_type) do
7
+ 'multipart/related; boundary="--==_mimepart_4d416ae62fd32_201a8043814c4724"; ' \
8
+ 'charset=UTF-8; type="text/xml"'
9
+ end
10
+ let(:header) { { 'Content-Type' => multipart_content_type } }
11
+ let(:body) { File.read(path) }
12
+ let(:response) { soap_response(headers: header, body: body) }
13
+
14
+ def soap_response(options = {})
15
+ defaults = { code: 200, headers: {}, body: '' }
16
+ response = defaults.merge options
17
+ globals = {
18
+ multipart: true,
19
+ raise_errors: true,
20
+ convert_response_tags_to: ->(tag) { Savon::StringUtils.snakecase(tag).to_sym }
21
+ }
22
+ http = HTTPI::Response.new(response[:code], response[:headers], response[:body])
23
+
24
+ Savon::Multipart::Response.new(http, globals, {})
25
+ end
26
+
27
+ context 'multipart' do
28
+ let(:path) { File.expand_path('../../fixtures/response/multipart.txt', __dir__) }
29
+
30
+ it 'parses the SOAP envelope from the first MIME part' do
31
+ expected = File.expand_path('../../fixtures/response/multipart_expected.xml', __dir__)
32
+ expect(response.xml.strip).to eq(File.read(expected))
33
+ end
34
+
35
+ it 'exposes the top-level MIME parts' do
36
+ expect(response.parts.length).to eq(2)
37
+ end
38
+
39
+ it 'exposes nested MIME parts' do
40
+ expect(response.parts[1].parts.length).to eq(3)
41
+ end
42
+
43
+ it 'decodes attachment bodies' do
44
+ expect(response.parts[1].parts[2].body.decoded.strip).to eq('This is a test message from Github')
45
+ end
46
+
47
+ it 'returns the attachments' do
48
+ expect(response.attachments.size).to eq(1)
49
+ end
50
+
51
+ it 'only parses the SOAP body once' do
52
+ # Referencing `response` here builds it (parsing the body a first time)
53
+ # before the spy is installed, so no further parses may happen below.
54
+ allow(response).to receive(:parse_body).and_call_original
55
+
56
+ 5.times do
57
+ response.attachments
58
+ end
59
+
60
+ expect(response).not_to have_received(:parse_body)
61
+ end
62
+ end
63
+
64
+ context 'simple multipart' do
65
+ let(:path) { File.expand_path('../../fixtures/response/simple_multipart.txt', __dir__) }
66
+
67
+ it 'returns a String from the #xml method' do
68
+ expect(response.xml.class).to eq(String)
69
+ end
70
+
71
+ it 'returns a Hash from the #body method' do
72
+ expect(response.body).to eq({ submit_req: true })
73
+ end
74
+ end
75
+
76
+ context 'simple xop' do
77
+ let(:path) { File.expand_path('../../fixtures/response/simple_xop.txt', __dir__) }
78
+
79
+ it 'returns a String from the #xml method' do
80
+ expect(response.xml.class).to eq(String)
81
+ end
82
+
83
+ it 'returns a Hash from the #body method, include xop data' do
84
+ expect(response.body).to eq({ binary_data: Base64.encode64('BinaryDataGoesHere').chomp })
85
+ end
86
+ end
87
+
88
+ describe 'a multipart response with case sensitive headers' do
89
+ let(:multipart_content_type) do
90
+ 'MuLtIpArT/rElAtEd; boundary="--==_mimepart_4d416ae62fd32_201a8043814c4724"; ' \
91
+ 'charset=UTF-8; type="text/xml"'
92
+ end
93
+ let(:path) { File.expand_path('../../fixtures/response/simple_multipart.txt', __dir__) }
94
+
95
+ it 'does not care about upper or lowercase values for ContentType' do
96
+ expect(response.body).to eq({ submit_req: true })
97
+ end
98
+ end
99
+
100
+ context 'not multipart' do
101
+ let(:path) { File.expand_path('../../fixtures/response/not_multipart.txt', __dir__) }
102
+ let(:header) { { 'Content-Type' => 'text/html; charset=utf-8' } }
103
+
104
+ it 'parses soap messages without attachments too' do
105
+ expected = File.expand_path('../../fixtures/response/not_multipart_expected.xml', __dir__)
106
+ expect(response.xml.chomp).to eq(File.read(expected))
107
+ end
108
+
109
+ it 'has no parts' do
110
+ expect(response.parts.size).to eq(0)
111
+ end
112
+
113
+ it 'has no attachments' do
114
+ expect(response.attachments.size).to eq(0)
115
+ end
116
+ end
117
+
118
+ context 'soap errors' do
119
+ let(:path) { File.expand_path('../../fixtures/response/soap_fault.txt', __dir__) }
120
+
121
+ it 'handles them correctly' do
122
+ expect { response }.to raise_error(Savon::SOAPFault, /The service cannot be found/)
123
+ end
124
+ end
125
+ end
metadata CHANGED
@@ -1,18 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon-multipart
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Westin
8
8
  - Daniel Harrington
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-05-20 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: savon
14
+ name: mail
16
15
  requirement: !ruby/object:Gem::Requirement
17
16
  requirements:
18
17
  - - "~>"
@@ -26,21 +25,21 @@ dependencies:
26
25
  - !ruby/object:Gem::Version
27
26
  version: '2'
28
27
  - !ruby/object:Gem::Dependency
29
- name: mail
28
+ name: savon
30
29
  requirement: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - "~>"
33
32
  - !ruby/object:Gem::Version
34
- version: 2.5.4
33
+ version: '2'
35
34
  type: :runtime
36
35
  prerelease: false
37
36
  version_requirements: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - "~>"
40
39
  - !ruby/object:Gem::Version
41
- version: 2.5.4
40
+ version: '2'
42
41
  - !ruby/object:Gem::Dependency
43
- name: rake
42
+ name: autotest
44
43
  requirement: !ruby/object:Gem::Requirement
45
44
  requirements:
46
45
  - - ">="
@@ -68,21 +67,7 @@ dependencies:
68
67
  - !ruby/object:Gem::Version
69
68
  version: '0'
70
69
  - !ruby/object:Gem::Dependency
71
- name: rspec
72
- requirement: !ruby/object:Gem::Requirement
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: '0'
77
- type: :development
78
- prerelease: false
79
- version_requirements: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- version: '0'
84
- - !ruby/object:Gem::Dependency
85
- name: autotest
70
+ name: rake
86
71
  requirement: !ruby/object:Gem::Requirement
87
72
  requirements:
88
73
  - - ">="
@@ -96,7 +81,7 @@ dependencies:
96
81
  - !ruby/object:Gem::Version
97
82
  version: '0'
98
83
  - !ruby/object:Gem::Dependency
99
- name: transpec
84
+ name: rspec
100
85
  requirement: !ruby/object:Gem::Requirement
101
86
  requirements:
102
87
  - - ">="
@@ -117,27 +102,38 @@ executables: []
117
102
  extensions: []
118
103
  extra_rdoc_files: []
119
104
  files:
105
+ - ".github/ISSUE_TEMPLATE/Bug_report.md"
106
+ - ".github/ISSUE_TEMPLATE/Feature_request.md"
107
+ - ".github/PULL_REQUEST_TEMPLATE.md"
108
+ - ".github/dependabot.yml"
109
+ - ".github/workflows/ci.yml"
110
+ - ".github/workflows/gem_push.yml"
120
111
  - ".gitignore"
121
112
  - ".rspec"
122
- - ".travis.yml"
113
+ - ".rubocop.yml"
114
+ - ".rubocop_todo.yml"
123
115
  - CHANGELOG.md
124
116
  - Gemfile
125
117
  - LICENSE
126
118
  - README.md
119
+ - RELEASING.md
127
120
  - Rakefile
128
121
  - lib/savon-multipart.rb
129
122
  - lib/savon/multipart/response.rb
130
123
  - lib/savon/multipart/version.rb
131
124
  - savon-multipart.gemspec
132
125
  - spec/fixtures/response/multipart.txt
126
+ - spec/fixtures/response/multipart_expected.xml
133
127
  - spec/fixtures/response/not_multipart.txt
128
+ - spec/fixtures/response/not_multipart_expected.xml
134
129
  - spec/fixtures/response/simple_multipart.txt
130
+ - spec/fixtures/response/simple_xop.txt
135
131
  - spec/fixtures/response/soap_fault.txt
136
- - spec/savon/soap/response_spec.rb
132
+ - spec/savon/multipart/response_spec.rb
137
133
  homepage: http://savonrb.com
138
134
  licenses: []
139
- metadata: {}
140
- post_install_message:
135
+ metadata:
136
+ rubygems_mfa_required: 'true'
141
137
  rdoc_options: []
142
138
  require_paths:
143
139
  - lib
@@ -152,9 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
148
  - !ruby/object:Gem::Version
153
149
  version: '0'
154
150
  requirements: []
155
- rubyforge_project: savon-multipart
156
- rubygems_version: 2.4.6
157
- signing_key:
151
+ rubygems_version: 3.6.9
158
152
  specification_version: 4
159
153
  summary: Heavy metal Ruby SOAP client with multipart support
160
154
  test_files: []
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- # https://github.com/travis-ci/travis-ci/wiki/.travis.yml-options
2
- language: "ruby"
3
- script: "bundle exec rake"
4
- rvm:
5
- - 1.9.3
6
- - 2.0
7
- - 2.1
8
- - 2.2
9
- - jruby-19mode
10
- - rbx-2
@@ -1,83 +0,0 @@
1
- require 'savon-multipart'
2
-
3
- describe Savon::Multipart::Response do
4
-
5
- let(:header) {{ "Content-Type" => 'multipart/related; boundary="--==_mimepart_4d416ae62fd32_201a8043814c4724"; charset=UTF-8; type="text/xml"' }}
6
- let(:body) { File.read(path) }
7
- let(:response) { soap_response(:headers => header, :body => body) }
8
-
9
- def soap_response(options = {})
10
- defaults = { :code => 200, :headers => {}, :body => "" }
11
- response = defaults.merge options
12
- globals = {
13
- :multipart => true,
14
- :raise_errors => true,
15
- :convert_response_tags_to => lambda { |tag| tag.snakecase.to_sym}
16
- }
17
- http = HTTPI::Response.new(response[:code], response[:headers], response[:body])
18
-
19
- Savon::Multipart::Response.new(http, globals, {})
20
- end
21
-
22
- context "multipart" do
23
- let(:path) { File.expand_path("../../../fixtures/response/multipart.txt", __FILE__) }
24
-
25
- it "parses without Exception" do
26
- expect(response.xml.strip).to eq('<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:wsdl="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><ns1:TransactionID soapenv:actor="" soapenv:mustUnderstand="1" xsi:type="xsd:string" xmlns:ns1="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2">2011012713535811111111111</ns1:TransactionID></soapenv:Header><soapenv:Body><SubmitReq xmlns="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2"><MM7Version>5.3.0</MM7Version><SenderIdentification><VASPID>messaging</VASPID><VASID>ADM</VASID><SenderAddress><ShortCode>1111</ShortCode></SenderAddress></SenderIdentification><Recipients><To><Number>11111111111</Number></To></Recipients><ServiceCode>1</ServiceCode><MessageClass>Personal</MessageClass><ExpiryDate>2011-01-28T13:53:58Z</ExpiryDate><DeliveryReport>false</DeliveryReport><ReadReply>false</ReadReply><Priority>Normal</Priority><Subject>Test MMS via Savon</Subject><ChargedParty>Sender</ChargedParty><Content href="cid:attachment_1" allowAdaptations="true"/></SubmitReq></soapenv:Body></soapenv:Envelope>')
27
- expect(response.parts.length).to eq(2)
28
- expect(response.parts[1].parts.length).to eq(3)
29
- expect(response.parts[1].parts[2].body.decoded.strip).to eq("This is a test message from Github")
30
- end
31
-
32
- it "returns the attachments" do
33
- expect(response.attachments.size).to eq(1)
34
- end
35
-
36
- it "only parses the SOAP body once" do
37
- Mail::Part.stub(:new).and_return(double(Mail::Part).as_null_object)
38
- expect(Mail::Part).to receive(:new).exactly(1).times
39
- 5.times { response.attachments }
40
- end
41
- end
42
-
43
- context "simple multipart" do
44
- let(:path) { File.expand_path('../../../fixtures/response/simple_multipart.txt', __FILE__) }
45
-
46
- it "returns a String from the #xml method" do
47
- expect(response.xml.class).to eq(String)
48
- end
49
-
50
- it "returns a Hash from the #body method" do
51
- expect(response.body.class).to eq(Hash)
52
- expect(response.body).to eq({:submit_req => true})
53
- end
54
- end
55
-
56
- describe "a multipart response with case sensitive headers" do
57
- let(:header) {{ "Content-Type" => 'MuLtIpArT/rElAtEd; boundary="--==_mimepart_4d416ae62fd32_201a8043814c4724"; charset=UTF-8; type="text/xml"' }}
58
- let(:path) { File.expand_path('../../../fixtures/response/simple_multipart.txt', __FILE__) }
59
-
60
- it "does not care about upper or lowercase values for ContentType" do
61
- expect(response.body).to eq({:submit_req => true})
62
- end
63
- end
64
-
65
- context "not multipart" do
66
- let(:path) { File.expand_path('../../../fixtures/response/not_multipart.txt', __FILE__) }
67
- let(:header) { { 'Content-Type' => 'text/html; charset=utf-8'} }
68
-
69
- it "parses soap messages without attachments too" do
70
- expect(response.xml.chomp).to eq('<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:wsdl="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><ns1:TransactionID soapenv:actor="" soapenv:mustUnderstand="1" xsi:type="xsd:string" xmlns:ns1="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2">2011012713535811111111111</ns1:TransactionID></soapenv:Header><soapenv:Body><SubmitReq xmlns="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2"><MM7Version>5.3.0</MM7Version><SenderIdentification><VASPID>messaging</VASPID><VASID>ADM</VASID><SenderAddress><ShortCode>1111</ShortCode></SenderAddress></SenderIdentification><Recipients><To><Number>11111111111</Number></To></Recipients><ServiceCode>1</ServiceCode><MessageClass>Personal</MessageClass><ExpiryDate>2011-01-28T13:53:58Z</ExpiryDate><DeliveryReport>false</DeliveryReport><ReadReply>false</ReadReply><Priority>Normal</Priority><Subject>Test MMS via Savon</Subject><ChargedParty>Sender</ChargedParty><Content href="cid:attachment_1" allowAdaptations="true"/></SubmitReq></soapenv:Body></soapenv:Envelope>')
71
- expect(response.parts.size).to eq(0)
72
- expect(response.attachments.size).to eq(0)
73
- end
74
- end
75
-
76
- context "soap errors" do
77
- let(:path) { File.expand_path('../../../fixtures/response/soap_fault.txt', __FILE__) }
78
-
79
- it "handles them correctly" do
80
- expect { response }.to raise_error(Savon::SOAPFault, /The service cannot be found/)
81
- end
82
- end
83
- end