pax_global_header00006660000000000000000000000064146216105220014511gustar00rootroot0000000000000052 comment=47cf6aa076a1522724e7251740471f0f2542e659 apollo_upload_server-ruby-2.1.6/000077500000000000000000000000001462161052200167165ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/.github/000077500000000000000000000000001462161052200202565ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/.github/workflows/000077500000000000000000000000001462161052200223135ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/.github/workflows/specs.yml000066400000000000000000000005631462161052200241570ustar00rootroot00000000000000name: Rspec test suite on: [push, pull_request] jobs: test: strategy: fail-fast: false matrix: ruby: ['2.7', '3.0', '3.1'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - run: bundle exec rspecapollo_upload_server-ruby-2.1.6/.gitignore000066400000000000000000000001311462161052200207010ustar00rootroot00000000000000/.bundle/ /.yardoc /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ /vendor/bundle/ apollo_upload_server-ruby-2.1.6/.rspec000066400000000000000000000000261462161052200200310ustar00rootroot00000000000000--require spec_helper apollo_upload_server-ruby-2.1.6/CODE_OF_CONDUCT.md000066400000000000000000000062311462161052200215170ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ifuelen@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ apollo_upload_server-ruby-2.1.6/Gemfile000066400000000000000000000002601462161052200202070ustar00rootroot00000000000000source 'https://rubygems.org' git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in apollo_upload_server.gemspec gemspec apollo_upload_server-ruby-2.1.6/Gemfile.lock000066400000000000000000000035001462161052200211360ustar00rootroot00000000000000PATH remote: . specs: apollo_upload_server (2.1.5) actionpack (>= 6.1.6) graphql (>= 1.8) GEM remote: https://rubygems.org/ specs: actionpack (7.0.4) actionview (= 7.0.4) activesupport (= 7.0.4) rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) actionview (7.0.4) activesupport (= 7.0.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) activesupport (7.0.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) builder (3.2.4) concurrent-ruby (1.1.10) crass (1.0.6) diff-lcs (1.5.0) erubi (1.11.0) graphql (2.0.15) i18n (1.12.0) concurrent-ruby (~> 1.0) loofah (2.19.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mini_portile2 (2.8.0) minitest (5.16.3) nokogiri (1.13.9) mini_portile2 (~> 2.8.0) racc (~> 1.4) racc (1.6.0) rack (2.2.4) rack-test (2.0.2) rack (>= 1.3) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.4.3) loofah (~> 2.3) rake (13.0.6) rspec (3.11.0) rspec-core (~> 3.11.0) rspec-expectations (~> 3.11.0) rspec-mocks (~> 3.11.0) rspec-core (3.11.0) rspec-support (~> 3.11.0) rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-mocks (3.11.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-support (3.11.0) tzinfo (2.0.5) concurrent-ruby (~> 1.0) PLATFORMS ruby DEPENDENCIES apollo_upload_server! bundler (~> 2.1) rake (~> 13.0) rspec (~> 3.11) BUNDLED WITH 2.3.17 apollo_upload_server-ruby-2.1.6/LICENSE.txt000066400000000000000000000020701462161052200205400ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Artur Plysyuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. apollo_upload_server-ruby-2.1.6/README.md000066400000000000000000000056151462161052200202040ustar00rootroot00000000000000# ApolloUploadServer Middleware which allows you to upload files using [graphql-ruby](https://github.com/rmosolgo/graphql-ruby), [apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client) and Ruby on Rails. Note: this implementation uses [v2 of the GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec/tree/v2.0.0-alpha.2), so you should use apollo-upload-client library >= v7.0.0-alpha.3. If you need support for [v1 of the GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec/tree/v1.0.0), you must use [version 1.0.0](https://github.com/jetruby/apollo_upload_server-ruby/tree/1.0.0) of this gem. ## Installation Add this line to your application's Gemfile: ```ruby gem 'apollo_upload_server', '2.1' ``` And then execute: $ bundle Or install it yourself as: $ gem install apollo_upload_server Middleware will be used automatically. Gem adds custom `Upload` type to your GraphQL types. Use `ApolloUploadServer::Upload` type for your file as input field: ```ruby input_field :file, ApolloUploadServer::Upload ``` That's all folks! ## Configuration The following configuration options are supported: ### Strict Mode This can be set on `ApolloUploadServer::Middleware`: ```ruby ApolloUploadServer::Middleware.strict_mode = true ``` Doing so ensures that all mapped array values are present in the input. If this is set to `true`, then for following request: ```json { "operations": { "query": "mutation { ... }", "operationName": "SomeOperation", "variables": { "input": { "id": "123", "avatars": [null, null] } } } } ``` A mapping for `variables.input.avatars.0` or `variables.input.avatars.1`, will work, but one for `variables.input.avatars.100` will not, and will raise an error. In strict mode, passing empty destination arrays will always fail. ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/jetruby/apollo_upload_server-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. Tests can be run via `bundle exec rspec`. ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct Everyone interacting in the ApolloUploadServer project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/jetruby/apollo_upload_server-ruby/blob/master/CODE_OF_CONDUCT.md). ## About JetRuby ApolloUploadServer is maintained and founded by JetRuby Agency. We love open source software! See [our projects][portfolio] or [contact us][contact] to design, develop, and grow your product. [portfolio]: http://jetruby.com/portfolio/ [contact]: http://jetruby.com/#contactUs apollo_upload_server-ruby-2.1.6/Rakefile000066400000000000000000000000601462161052200203570ustar00rootroot00000000000000require 'bundler/gem_tasks' task default: :spec apollo_upload_server-ruby-2.1.6/apollo_upload_server.gemspec000066400000000000000000000020511462161052200245010ustar00rootroot00000000000000lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'apollo_upload_server/version' Gem::Specification.new do |spec| spec.name = 'apollo_upload_server' spec.version = ApolloUploadServer::VERSION spec.authors = ['JetRuby'] spec.email = ['engineering@jetruby.com'] spec.summary = 'Middleware which allows you to upload files using graphql and multipart/form-data.' spec.description = 'apollo-upload-server implementation for Ruby on Rails as middleware.' spec.homepage = 'https://github.com/jetruby/apollo_upload_server-ruby' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) end spec.require_paths = ['lib'] spec.add_dependency 'actionpack', '>= 6.1.6' spec.add_dependency 'graphql', '>= 1.8' spec.add_development_dependency 'bundler', '~> 2.1' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rspec', '~> 3.11' end apollo_upload_server-ruby-2.1.6/bin/000077500000000000000000000000001462161052200174665ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/bin/console000077500000000000000000000005431462161052200210600ustar00rootroot00000000000000#!/usr/bin/env ruby require 'bundler/setup' require 'apollo_upload_server' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require 'irb' IRB.start(__FILE__) apollo_upload_server-ruby-2.1.6/bin/setup000077500000000000000000000002031462161052200205470ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here apollo_upload_server-ruby-2.1.6/lib/000077500000000000000000000000001462161052200174645ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server.rb000066400000000000000000000003221462161052200242260ustar00rootroot00000000000000require 'apollo_upload_server/graphql_data_builder' require 'apollo_upload_server/middleware' require 'apollo_upload_server/version' require 'apollo_upload_server/railtie' require 'apollo_upload_server/upload' apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/000077500000000000000000000000001462161052200237045ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/graphql_data_builder.rb000066400000000000000000000055341462161052200303750ustar00rootroot00000000000000# frozen_string_literal: true require 'json' require 'apollo_upload_server/wrappers/uploaded_file' module ApolloUploadServer class GraphQLDataBuilder OutOfBounds = Class.new(ArgumentError) def initialize(strict_mode: false) @strict_mode = strict_mode end def call(params) operations = JSON.parse(params['operations']) file_mapper = JSON.parse(params['map']) return nil if operations.nil? || file_mapper.nil? if operations.is_a?(Hash) single_transformation(file_mapper, operations, params) else { '_json' => multiple_transformation(file_mapper, operations, params) } end end private def single_transformation(file_mapper, operations, params) operations = operations.dup file_mapper.each do |file_index, paths| paths.each do |path| splited_path = path.split('.') # splited_path => 'variables.input.profile_photo'; splited_path[0..-2] => ['variables', 'input'] # dig from first to penultimate key, and merge last key with value as file field = get_parent_field(operations, splited_path) assign_file(field, splited_path, params[file_index]) end end operations end def multiple_transformation(file_mapper, operations, params) operations = operations.dup file_mapper.each do |file_index, paths| paths.each do |path| splited_path = path.split('.') # dig from second to penultimate key, and merge last key with value as file to operation with first key index field = operations[splited_path.first.to_i].dig(*splited_path[1..-2]) assign_file(field, splited_path, params[file_index]) end end operations end def verify_array_index!(path, index, size) return unless @strict_mode return if 0 <= index && index < size raise OutOfBounds, "Path #{path.join('.')} maps to out-of-bounds index: #{index}" end def get_parent_field(operations, splited_path) # returns parent element of file field splited_path[0..-2].inject(operations) do |element, key| case element when Array element[Integer(key)] else element[key] end end end def assign_file(field, splited_path, file) wrapped_file = Wrappers::UploadedFile.new(file) if field.is_a? Hash field.merge!(splited_path.last => wrapped_file) elsif field.is_a? Array index = parse_array_index(splited_path) verify_array_index!(splited_path, index, field.size) field[index] = wrapped_file end end def parse_array_index(path) return path.last.to_i unless @strict_mode Integer(path.last) rescue ArgumentError raise OutOfBounds, "Not a valid path to an array value: #{path.join('.')}" end end end apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/middleware.rb000066400000000000000000000015621462161052200263520ustar00rootroot00000000000000require 'apollo_upload_server/graphql_data_builder' require "active_support/configurable" module ApolloUploadServer class Middleware include ActiveSupport::Configurable # Strict mode requires that all mapped files are present in the mapping arrays. config_accessor :strict_mode do false end def initialize(app) @app = app end def call(env) unless env['CONTENT_TYPE'].to_s.include?('multipart/form-data') return @app.call(env) end request = ActionDispatch::Request.new(env) params = request.params if params['operations'].present? && params['map'].present? result = GraphQLDataBuilder.new(strict_mode: self.class.strict_mode).call(request.params) result&.each do |key, value| request.update_param(key, value) end end @app.call(env) end end end apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/railtie.rb000066400000000000000000000003351462161052200256630ustar00rootroot00000000000000require 'apollo_upload_server/middleware' module ApolloUploadServer class Railtie < Rails::Railtie initializer 'apollo_upload_server.apply_middleware' do |app| app.middleware.use Middleware end end end apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/upload.rb000066400000000000000000000005651462161052200255230ustar00rootroot00000000000000# frozen_string_literal: true require 'graphql' module ApolloUploadServer class Upload < GraphQL::Schema::Scalar graphql_name "Upload" def self.coerce_input(value, _ctx) return super if value.nil? || value.is_a?(::ApolloUploadServer::Wrappers::UploadedFile) raise GraphQL::CoercionError, "#{value.inspect} is not a valid upload" end end end apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/version.rb000066400000000000000000000000711462161052200257140ustar00rootroot00000000000000module ApolloUploadServer VERSION = '2.1.6'.freeze end apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/wrappers/000077500000000000000000000000001462161052200255475ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/lib/apollo_upload_server/wrappers/uploaded_file.rb000066400000000000000000000006061462161052200306720ustar00rootroot00000000000000# frozen_string_literal: true require 'delegate' require 'action_dispatch/http/upload' module ApolloUploadServer module Wrappers class UploadedFile < DelegateClass(::ActionDispatch::Http::UploadedFile) def initialize(wrapped_foo) super end def as_json(options = nil) instance_values.except('tempfile').as_json(options) end end end end apollo_upload_server-ruby-2.1.6/spec/000077500000000000000000000000001462161052200176505ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/spec/apollo_upload_server/000077500000000000000000000000001462161052200240705ustar00rootroot00000000000000apollo_upload_server-ruby-2.1.6/spec/apollo_upload_server/graphql_data_builder_spec.rb000066400000000000000000000206361462161052200315730ustar00rootroot00000000000000require 'spec_helper' require 'apollo_upload_server/graphql_data_builder' describe ApolloUploadServer::GraphQLDataBuilder do describe '#call for single operation' do let(:params) do { 'operations' => { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'model' => {} } } }.to_json, 'map' => { '0' => ['variables.input.avatar', 'variables.input.model.avatar'] }.to_json, '0' => :file0 } end let(:expected_params) do { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'avatar' => :file0, 'model' => { 'avatar' => :file0 } } } } end specify do expect(described_class.new.call(params)).to eq(expected_params) end end describe '#call for single operation with multiple files' do let(:params) do { 'operations' => { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [nil], 'model' => { 'avatars' => [nil] } } } }.to_json, 'map' => { '0' => ['variables.input.avatars.0', 'variables.input.model.avatars.0'] }.to_json, '0' => :file0 } end let(:expected_params) do { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [:file0], 'model' => { 'avatars' => [:file0] } } } } end specify do expect(described_class.new.call(params)).to eq(expected_params) end context 'when the index is not a string' do let(:params) do { 'operations' => { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [nil], 'model' => { 'avatars' => [nil] } } } }.to_json, 'map' => { '0' => ['variables.input.avatars.foo', 'variables.input.model.avatars.0'] }.to_json, '0' => :file0 } end specify do expect(described_class.new.call(params)).to eq(expected_params) end it 'is rejected in strict mode' do expect do described_class.new(strict_mode: true).call(params) end.to raise_error(described_class::OutOfBounds) end end context 'when the array is empty' do let(:params) do { 'operations' => { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [], 'model' => { 'avatars' => [nil] } } } }.to_json, 'map' => { '0' => ['variables.input.avatars.0', 'variables.input.model.avatars.0'] }.to_json, '0' => :file0 } end let(:expected_params) do { 'query' => 'mutation { blah blah }', 'operationName' => 'SomeOperation', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [:file0], 'model' => { 'avatars' => [:file0] } } } } end specify do expect(described_class.new.call(params)).to eq(expected_params) end it 'accepts this input in lax mode' do expect(described_class.new.call(params)).to eq(expected_params) end it 'rejects this input in strict mode' do expect do described_class.new(strict_mode: true).call(params) end.to raise_error(described_class::OutOfBounds) end end end describe '#call for multiple operations' do let(:params) do { 'operations' => [{ 'query' => 'mutation { blah blah1 }', 'operationName' => nil, 'variables' => { 'input' => { 'id' => '123' } } }, { 'query' => 'mutation { blah blah2 }', 'operationName' => 'hashKeyCzaza', 'variables' => { 'input' => { 'id' => '123' } } }, { 'query' => 'mutation { blah blah3 }', 'operationName' => 'Some_Operation', 'hashKeyA' => { 'hashKeyB' => { 'id' => '123' } } }].to_json, 'map' => { '0' => ['0.variables.input.avatar', '2.hashKeyA.hashKeyB.hashKeyC'] }.to_json, '0' => :file0 } end let(:expected_params) do {'_json' => [ { 'query' => 'mutation { blah blah1 }', 'operationName' => nil, 'variables' => { 'input' => { 'id' => '123', 'avatar' => :file0 } } }, { 'query' => 'mutation { blah blah2 }', 'operationName' => 'hashKeyCzaza', 'variables' => { 'input' => { 'id' => '123' } } }, { 'query' => 'mutation { blah blah3 }', 'operationName' => 'Some_Operation', 'hashKeyA' => { 'hashKeyB' => { 'id' => '123', 'hashKeyC' => :file0 } } } ]} end specify do expect(described_class.new.call(params)).to eq(expected_params) end end describe '#call for multiple operations and many files' do let(:params) do { 'operations' => [{ 'query' => 'mutation { blah blah1 }', 'operationName' => nil, 'variables' => { 'input' => { 'id' => '123' } } }, { 'query' => 'mutation { blah blah2 }', 'operationName' => 'hashKeyCzaza', 'variables' => { 'input' => { 'id' => '123' } } }, { 'query' => 'mutation { blah blah3 }', 'operationName' => 'Some_Operation', 'hashKeyA' => { 'hashKeyB' => { 'id' => '123', 'model' => { 'id' => '23' } } } }].to_json, 'map' => { '0' => ['0.variables.input.avatar', '2.hashKeyA.hashKeyB.hashKeyC', '2.hashKeyA.hashKeyB.file0'], '2' => ['0.variables.input.file2', '1.variables.input.profile_photo', '2.hashKeyA.hashKeyB.model.photo'] }.to_json, '0' => :file0, '1' => :file1, '2' => :file2 } end let(:expected_params) do {'_json' => [ { 'query' => 'mutation { blah blah1 }', 'operationName' => nil, 'variables' => { 'input' => { 'id' => '123', 'avatar' => :file0, 'file2' => :file2 } } }, { 'query' => 'mutation { blah blah2 }', 'operationName' => 'hashKeyCzaza', 'variables' => { 'input' => { 'id' => '123', 'profile_photo' => :file2 } } }, { 'query' => 'mutation { blah blah3 }', 'operationName' => 'Some_Operation', 'hashKeyA' => { 'hashKeyB' => { 'id' => '123', 'model' => { 'id' => '23', 'photo' => :file2 }, 'hashKeyC' => :file0, 'file0' => :file0 } } } ] } end specify do expect(described_class.new.call(params)).to eq(expected_params) end end describe '#call for multiple operations with multiple files' do let(:params) do { 'operations' => [{ 'query' => 'mutation { blah blah1 }', 'operationName' => nil, 'variables' => { 'input' => { 'id' => '123', 'avatars' => [nil] } } }, { 'query' => 'mutation { blah blah2 }', 'operationName' => 'hashKeyCzaza', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [nil] } } } ].to_json, 'map' => { '0' => ['0.variables.input.avatars.0', '1.variables.input.avatars.0'] }.to_json, '0' => :file0 } end let(:expected_params) do {'_json' => [ { 'query' => 'mutation { blah blah1 }', 'operationName' => nil, 'variables' => { 'input' => { 'id' => '123', 'avatars' => [:file0] } } }, { 'query' => 'mutation { blah blah2 }', 'operationName' => 'hashKeyCzaza', 'variables' => { 'input' => { 'id' => '123', 'avatars' => [:file0] } } } ]} end specify do expect(described_class.new.call(params)).to eq(expected_params) end end end apollo_upload_server-ruby-2.1.6/spec/apollo_upload_server/middleware_spec.rb000066400000000000000000000026561462161052200275550ustar00rootroot00000000000000require 'spec_helper' require 'action_dispatch' require 'apollo_upload_server/middleware' describe ApolloUploadServer::Middleware do around do |example| mode = described_class.strict_mode example.run described_class.strict_mode = mode end describe '#call' do let(:app) do Rack::Builder.new do use ApolloUploadServer::Middleware run ->(_env) { [200, { 'Content-Type' => 'text/plain' }, 'Hello, World.'] } end end context "when CONTENT_TYPE is 'multipart/form-data'" do subject do Rack::MockRequest.new(app).post('/', { 'CONTENT_TYPE' => 'multipart/form-data', input: 'operations={}&map={}' }) end it { expect(subject.status).to eq(200) } end context "when CONTENT_TYPE is not 'multipart/form-data'" do subject do Rack::MockRequest.new(app).post('/', { 'CONTENT_TYPE' => 'text/plain' }) end it { expect(subject.status).to eq(200) } end context 'when configured to run in strict mode' do before do described_class.strict_mode = true end subject do Rack::MockRequest.new(app).post('/', { 'CONTENT_TYPE' => 'multipart/form-data', input: 'operations={}&map={}' }) end it 'propagates this setting to the data builder' do expect(ApolloUploadServer::GraphQLDataBuilder).to receive(:new).with(strict_mode: true).and_call_original subject end end end end apollo_upload_server-ruby-2.1.6/spec/apollo_upload_server/upload_spec.rb000066400000000000000000000011701462161052200267120ustar00rootroot00000000000000require 'spec_helper' require 'apollo_upload_server/upload' RSpec.describe ApolloUploadServer::Upload do let(:ctx) { {} } describe '#coerce_input' do let(:uploaded_file) { ApolloUploadServer::Wrappers::UploadedFile.new('test') } specify do expect(described_class.coerce_input(uploaded_file, ctx)).to eq(uploaded_file) expect { described_class.coerce_input('test', ctx) }.to raise_error(GraphQL::CoercionError) expect(described_class.coerce_input(nil, ctx)).to eq(nil) end end describe '#coerce_result' do it { expect(described_class.coerce_result('test', ctx)).to eq 'test' } end end apollo_upload_server-ruby-2.1.6/spec/spec_helper.rb000066400000000000000000000004701462161052200224670ustar00rootroot00000000000000RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end config.shared_context_metadata_behavior = :apply_to_host_groups end