├── .gitignore ├── .ruby-version ├── .travis.yml ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── gemfiles ├── .bundle │ └── config ├── .gitignore ├── rails_4.1.gemfile ├── rails_4.2.gemfile ├── rails_5.0.gemfile ├── rails_5.1.gemfile ├── rails_5.2.gemfile ├── rails_5.2.gemfile~Ruby on Rails 5.2 & 6.0 and Ruby 2.6.1 testing ├── rails_6.0.gemfile └── rails_6.1.gemfile ├── lib ├── olive_branch.rb └── olive_branch │ ├── middleware.rb │ └── version.rb ├── olive_branch.gemspec └── spec ├── benchmark_spec.rb ├── integration_spec.rb ├── olive_branch └── middleware_spec.rb ├── rails_helper.rb ├── spec_helper.rb └── test_app ├── .gitignore ├── Rakefile ├── app └── controllers │ ├── application_controller.rb │ └── posts_controller.rb ├── bin ├── bundle ├── rails └── rake ├── config.ru ├── config ├── application.rb ├── boot.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ └── wrap_parameters.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── example_responses └── complex.json ├── log ├── .gitignore └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png └── favicon.ico └── tmp └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | *.gem 3 | .byebug_history 4 | .idea/ -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: "bundle exec rspec" 2 | cache: bundler 3 | rvm: 4 | - 2.2 5 | - 2.3 6 | - 2.4 7 | - 2.5 8 | - 2.6.1 9 | - 2.7.3 10 | gemfile: 11 | - gemfiles/rails_4.2.gemfile 12 | - gemfiles/rails_5.0.gemfile 13 | - gemfiles/rails_5.1.gemfile 14 | - gemfiles/rails_5.2.gemfile 15 | - gemfiles/rails_6.0.gemfile 16 | - gemfiles/rails_6.1.gemfile 17 | 18 | jobs: 19 | # Rails 6.0 requires 2.5 or newer 20 | exclude: 21 | - rvm: 2.2 22 | gemfile: gemfiles/rails_6.0.gemfile 23 | - rvm: 2.2 24 | gemfile: gemfiles/rails_6.1.gemfile 25 | - rvm: 2.3 26 | gemfile: gemfiles/rails_6.0.gemfile 27 | - rvm: 2.3 28 | gemfile: gemfiles/rails_6.1.gemfile 29 | - rvm: 2.4 30 | gemfile: gemfiles/rails_6.0.gemfile 31 | - rvm: 2.4 32 | gemfile: gemfiles/rails_6.1.gemfile 33 | - rvm: 2.7.3 34 | gemfile: gemfiles/rails_4.2.gemfile -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Will not work on Ruby 2.6.1 4 | # appraise 'rails-4.1' do 5 | # gem 'rails', '~> 4.1.16' 6 | # end 7 | 8 | appraise 'rails-4.2' do 9 | gem 'rails', '~> 4.2.11' 10 | end 11 | 12 | appraise 'rails-5.0' do 13 | gem 'rails', '~> 5.0.7.1' 14 | end 15 | 16 | appraise 'rails-5.1' do 17 | gem 'rails', '~> 5.1.6.1' 18 | end 19 | 20 | appraise 'rails-5.2' do 21 | gem 'rails', '~> 5.2.5' 22 | end 23 | 24 | appraise 'rails-6.0' do 25 | gem 'rails', '~> 6.0.3.6' 26 | end 27 | 28 | appraise 'rails-6.1' do 29 | gem 'rails', '~> 6.1.3.1' 30 | end 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 4.0.0 2 | ------ 3 | * [Remove Oj as a required dependency](https://github.com/vigetlabs/olive_branch/pull/44) 4 | 5 | 3.0.0 6 | ------ 7 | * [Change default inflection header to Key-Inflection](https://github.com/vigetlabs/olive_branch/pull/35) -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in olive_branch.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Eli Fatsi (Viget) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OliveBranch 2 | 3 | [![Code Climate](https://codeclimate.com/github/vigetlabs/olive_branch.png)](https://codeclimate.com/github/vigetlabs/olive_branch) 4 | [![Build Status](https://travis-ci.org/vigetlabs/olive_branch.svg?branch=master)](https://travis-ci.org/vigetlabs/olive_branch) 5 | 6 | This gem lets your API users pass in and receive camelCased or dash-cased keys, while your Rails app receives and produces snake_cased ones. 7 | 8 | ## Install 9 | 10 | 1. Add this to your Gemfile and then `bundle install`: 11 | 12 | ```ruby 13 | gem "olive_branch" 14 | ``` 15 | 16 | 2. Add this to `config/applcation.rb` if you want the clients to control the transformation behaviour through the `Key-Inflection` HTTP header sent by the client: 17 | 18 | ```ruby 19 | config.middleware.use OliveBranch::Middleware 20 | ``` 21 | 22 | Alternative, if you want to always convert between snake_case and camelCase for your API and only your API, to keep Rubyist and JavaScript developer's happy, use the following configuration: 23 | 24 | ```ruby 25 | excluded_routes = ->(env) { !env["PATH_INFO"].match(%r{^/api}) } 26 | config.middleware.use OliveBranch::Middleware, 27 | inflection: "camel", 28 | exclude_params: excluded_routes, 29 | exclude_response: excluded_routes 30 | ``` 31 | 32 | in your `config/application.rb`. 33 | 34 | ## Use 35 | 36 | Include a `Key-Inflection` header with values of `camel`, `dash`, `snake` or `pascal` in your JSON API requests. 37 | 38 | For more examples, see [our blog post](https://www.viget.com/articles/introducing-olivebranch). 39 | 40 | ## Optimizations and configuration 41 | 42 | `OliveBranch` uses `multi_json`, which will automatically choose the fastest available JSON parsing library present in your application. 43 | Most Ruby applications default to using the JSON library that ships with Ruby. However, by including a coder that `multi_json` considers faster, like [Oj](https://github.com/ohler55/oj) in your gemfile, you can potentially save up to ~20% response time. 44 | 45 | The middleware can be initialized with custom camelize/dasherize implementations, so if you know you have a fixed size set of keys, you can save a considerable amount of time by providing a custom camelize that caches like so: 46 | 47 | ```ruby 48 | class FastCamel 49 | def self.camel_cache 50 | @camel_cache ||= {} 51 | end 52 | 53 | def self.camelize(string) 54 | camel_cache[string] ||= string.underscore.camelize(:lower) 55 | end 56 | end 57 | 58 | 59 | ... 60 | 61 | config.middleware.use OliveBranch::Middleware, camelize: FastCamel.method(:camelize) 62 | ``` 63 | 64 | Default inflection header key can be changed like 65 | 66 | ```ruby 67 | config.middleware.use OliveBranch::Middleware, inflection_header: 'Inflect-With' 68 | ``` 69 | 70 | A default inflection can be specified so you don't have to include the `Key-Inflection` header on every request. If you opt for default inflection, you may want to exclude the routes that Rails uses (see Filtering). 71 | 72 | ```ruby 73 | config.middleware.use OliveBranch::Middleware, inflection: 'camel' 74 | ``` 75 | 76 | A benchmark of this compared to the standard implementation shows a saving of ~75% rails response times for a complex response payload, or a ~400% improvement, but there is a risk of memory usage ballooning if you have dynamic keys. You can make this method as complex as required, but keep in mind that it will end up being called a _lot_ in a busy app, so it's worth thinking about how to do what you need in the fastest manner possible. 77 | 78 | ### Filtering 79 | 80 | #### Content type 81 | 82 | It is also possible to include a custom content type check in the same manner 83 | 84 | ```ruby 85 | config.middleware.use OliveBranch::Middleware, content_type_check: -> (content_type) { 86 | content_type == "my/content-type" 87 | } 88 | ``` 89 | 90 | #### Excluding URLs 91 | 92 | Additionally you can define a custom check by passing a proc 93 | 94 | For params transforming 95 | 96 | ```ruby 97 | config.middleware.use OliveBranch::Middleware, exclude_params: -> (env) { 98 | env['PATH_INFO'].match(/^\/do_not_transform/) 99 | } 100 | ``` 101 | 102 | Or response transforming 103 | 104 | ```ruby 105 | config.middleware.use OliveBranch::Middleware, exclude_response: -> (env) { 106 | env['PATH_INFO'].match(/^\/do_not_transform/) 107 | } 108 | ``` 109 | 110 | #### Rails routes & Action Text 111 | 112 | If you're using default inflection, exclude the routes that Rails uses 113 | ```ruby 114 | rails_routes = -> (env) { env['PATH_INFO'].match(/^\/rails/) } 115 | config.middleware.use OliveBranch::Middleware, inflection: "camel", exclude_params: rails_routes, exclude_response: rails_routes 116 | ``` 117 | 118 | ## Upgrading to version 3 119 | 120 | Default inflection header changed from `X-Key-Inflection` to `Key-Inflection`. 121 | 122 | ## Troubleshooting 123 | 124 | We've seen folks raise issues that inbound transformations are not taking place. This is often due to the fact that OliveBranch, by default, is only transforming keys when a request's Content-Type is `application/json`. 125 | 126 | Note that your HTTP client library may suppress even a manually specified `Content-Type` header if the request body is empty (e.g. [Axios does this](https://github.com/axios/axios/issues/86)). This is a common gotcha for GET requests, the body of which are [often expected to be empty](https://stackoverflow.com/questions/978061/http-get-with-request-body) for reasons of caching. If you're seeing the middleware perform on POST or PATCH requests, but not GET requests, this may be your issue. 127 | 128 | You may choose to force inbound transformation on every request by overriding the `content_type_check` functionality: 129 | 130 | ```ruby 131 | config.middleware.use OliveBranch::Middleware, content_type_check: -> (content_type) { true } 132 | ``` 133 | 134 | * * * 135 | 136 | OliveBranch is released under the [MIT License](http://www.opensource.org/licenses/MIT). See MIT-LICENSE for further details. 137 | 138 | * * * 139 | 140 | 141 | Code At Viget 142 | 143 | 144 | Visit [code.viget.com](http://code.viget.com) to see more projects from [Viget.](https://viget.com) 145 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | begin 4 | require "rspec/core/rake_task" 5 | RSpec::Core::RakeTask.new(:spec) 6 | task default: :spec 7 | rescue LoadError 8 | end 9 | 10 | task :benchmark do 11 | sh "BENCHMARK_REPETITIONS=1000 rspec spec/benchmark_spec.rb" 12 | end 13 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "1" 3 | -------------------------------------------------------------------------------- /gemfiles/.gitignore: -------------------------------------------------------------------------------- 1 | *.gemfile.lock 2 | -------------------------------------------------------------------------------- /gemfiles/rails_4.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 4.1.16" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_4.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 4.2.11" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_5.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 5.0.7.1" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_5.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 5.1.6.1" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_5.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 5.2.2" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_5.2.gemfile~Ruby on Rails 5.2 & 6.0 and Ruby 2.6.1 testing: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file was generated by Appraisal 4 | 5 | source 'https://rubygems.org' 6 | 7 | gem 'rails', '~> 5.2.2' 8 | 9 | gemspec path: '../' 10 | -------------------------------------------------------------------------------- /gemfiles/rails_6.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 6.0.0.beta1" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_6.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "~> 6.1.3" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /lib/olive_branch.rb: -------------------------------------------------------------------------------- 1 | module OliveBranch 2 | autoload :Middleware, "olive_branch/middleware" 3 | end 4 | -------------------------------------------------------------------------------- /lib/olive_branch/middleware.rb: -------------------------------------------------------------------------------- 1 | require "multi_json" 2 | 3 | module OliveBranch 4 | class Checks 5 | def self.content_type_check(content_type) 6 | content_type =~ /application\/json/ || content_type =~ /application\/vnd\.api\+json/ 7 | end 8 | 9 | def self.default_exclude(env) 10 | false 11 | end 12 | end 13 | 14 | class Transformations 15 | class << self 16 | def transform(value, transform_method) 17 | case value 18 | when Array then value.map { |item| transform(item, transform_method) } 19 | when Hash then value.deep_transform_keys! { |key| transform(key, transform_method) } 20 | when String then transform_method.call(value) 21 | else value 22 | end 23 | end 24 | 25 | def pascalize(string) 26 | string.underscore.camelize(:upper) 27 | end 28 | 29 | def camelize(string) 30 | string.underscore.camelize(:lower) 31 | end 32 | 33 | def dasherize(string) 34 | string.dasherize 35 | end 36 | 37 | def underscore_params(env) 38 | req = ActionDispatch::Request.new(env) 39 | req.request_parameters 40 | req.query_parameters 41 | 42 | env["action_dispatch.request.request_parameters"].deep_transform_keys!(&:underscore) 43 | env["action_dispatch.request.query_parameters"].deep_transform_keys!(&:underscore) 44 | end 45 | end 46 | end 47 | 48 | class Middleware 49 | def initialize(app, args = {}) 50 | @app = app 51 | @camelize = args[:camelize] || Transformations.method(:camelize) 52 | @dasherize = args[:dasherize] || Transformations.method(:dasherize) 53 | @pascalize = args[:pascalize] || Transformations.method(:pascalize) 54 | @content_type_check = args[:content_type_check] || Checks.method(:content_type_check) 55 | @exclude_response = args[:exclude_response] || Checks.method(:default_exclude) 56 | @exclude_params = args[:exclude_params] || Checks.method(:default_exclude) 57 | @default_inflection = args[:inflection] 58 | @inflection_header = args.fetch(:inflection_header, 'Key-Inflection').gsub(/[^a-z0-9]/i, '_').upcase 59 | @inflection_header = "HTTP_#{@inflection_header}" unless @inflection_header.start_with?('HTTP_') 60 | end 61 | 62 | def call(env) 63 | Transformations.underscore_params(env) unless exclude_params?(env) 64 | status, headers, response = @app.call(env) 65 | 66 | return [status, headers, response] if exclude_response?(env, headers) 67 | 68 | new_responses = [] 69 | 70 | response.each do |body| 71 | begin 72 | new_response = MultiJson.load(body) 73 | rescue MultiJson::ParseError 74 | new_responses << body 75 | next 76 | end 77 | 78 | Transformations.transform(new_response, inflection_method(env)) 79 | 80 | new_responses << MultiJson.dump(new_response) 81 | end 82 | 83 | [status, headers, new_responses] 84 | end 85 | 86 | private 87 | 88 | def exclude_params?(env) 89 | exclude?(env, env["CONTENT_TYPE"], @exclude_params) 90 | end 91 | 92 | def exclude_response?(env, headers) 93 | exclude_rails_route?(env) || 94 | exclude?(env, headers['Content-Type'], @exclude_response) 95 | end 96 | 97 | def exclude?(env, content_type, block) 98 | !inflection_type(env) || !valid_content_type?(content_type) || block.call(env) 99 | end 100 | 101 | def exclude_rails_route?(env) 102 | env['PATH_INFO'].to_s.start_with?('/rails') 103 | end 104 | 105 | def valid_content_type?(content_type) 106 | @content_type_check.call(content_type) 107 | end 108 | 109 | def inflection_type(env) 110 | env[@inflection_header] || @default_inflection 111 | end 112 | 113 | def inflection_method(env) 114 | inflection = inflection_type(env) 115 | 116 | if inflection == "camel" 117 | @camelize 118 | elsif inflection == "dash" 119 | @dasherize 120 | elsif inflection == 'pascal' 121 | @pascalize 122 | else 123 | # probably misconfigured, do nothing 124 | -> (string) { string } 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /lib/olive_branch/version.rb: -------------------------------------------------------------------------------- 1 | module OliveBranch 2 | VERSION = '4.0.1' 3 | end 4 | -------------------------------------------------------------------------------- /olive_branch.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | require "olive_branch/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "olive_branch" 7 | s.version = OliveBranch::VERSION 8 | s.summary = "Handle camel/snake/dash case conversion" 9 | s.description = "Handle camel/snake/dash case conversion" 10 | s.authors = ["Eli Fatsi", "David Eisinger"] 11 | s.email = ["eli.fatsi@viget.com", "david.eisinger@viget.com"] 12 | s.files = Dir["lib/**/*"] + ["MIT-LICENSE", "README.md"] 13 | s.homepage = "https://github.com/vigetlabs/olive_branch" 14 | s.license = "MIT" 15 | 16 | s.add_dependency "rails", ">= 4.0" 17 | s.add_dependency "multi_json" 18 | 19 | s.add_development_dependency "rspec", "~> 3.5.0" 20 | s.add_development_dependency "appraisal" 21 | s.add_development_dependency "rspec-rails" 22 | end 23 | -------------------------------------------------------------------------------- /spec/benchmark_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | BENCHMARK_REPETITIONS ||= Integer(ENV["BENCHMARK_REPETITIONS"] || 0) 4 | 5 | if BENCHMARK_REPETITIONS > 0 6 | RSpec.describe 'Benchmark thingerer', :type => :request do 7 | let(:url) { '/posts/complex' } 8 | let(:headers) do 9 | { "CONTENT_TYPE" => "application/json", 'X-Key-Inflection' => 'camel' } 10 | end 11 | 12 | context "when the X-Key-Inflection HTTP header is set to 'camel'" do 13 | it "benchmarks 1000 repetitions" do 14 | Benchmark.bm do |x| 15 | x.report("time taken ") { BENCHMARK_REPETITIONS.times { get_request } } 16 | end 17 | end 18 | end 19 | 20 | def get_request 21 | if Rails::VERSION::MAJOR >= 5 22 | get url, headers: headers 23 | else 24 | get url, headers 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/integration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe 'Integration Test', :type => :request do 4 | let(:url) { '/posts/1234?categoryFilter[categoryName][]=food' } 5 | let(:headers) do 6 | { "CONTENT_TYPE" => "application/json", 'Key-Inflection' => 'camel' } 7 | end 8 | let(:params) do 9 | { 'post' => { 'authorName' => 'John Smith' } } 10 | end 11 | 12 | context "when the Key-Inflection HTTP header is set to 'camel'" do 13 | it "should transform response keys to camel case" do 14 | put_request 15 | 16 | payload = JSON.parse(response.body) 17 | 18 | expect(payload['postAuthorName']).to eq 'John Smith' 19 | expect(payload['categoryFilterName']).to eq ['food'] 20 | end 21 | 22 | it "should set the controller's params to be underscored for the request/query parameters" do 23 | put_request 24 | 25 | req_params = JSON.parse(request.env['params_spy'].to_json).with_indifferent_access 26 | 27 | expect(req_params).to include(post: { author_name: 'John Smith' }, category_filter: { category_name: ['food'] }) 28 | end 29 | 30 | it "should NOT transform the path params in the controller's params" do 31 | put_request 32 | 33 | req_params = JSON.parse(request.env['params_spy'].to_json).with_indifferent_access 34 | 35 | expect(req_params).to include(postId: "1234") 36 | end 37 | end 38 | 39 | context "when the Key-Inflection HTTP header is not set" do 40 | let(:headers) { { "CONTENT_TYPE" => "application/json" } } 41 | 42 | it "should NOT transform response keys" do 43 | put_request 44 | 45 | payload = JSON.parse(response.body) 46 | 47 | expect(payload['postAuthorName']).to be_nil 48 | expect(payload['categoryFilterName']).to be_nil 49 | expect(payload).to include('post_author_name', 'category_filter_name') 50 | end 51 | 52 | it "should NOT transform the controller's params' keys" do 53 | put_request 54 | 55 | req_params = JSON.parse(request.env['params_spy'].to_json).with_indifferent_access 56 | 57 | expect(req_params).to include(params) 58 | end 59 | 60 | it "should NOT transform the path params in the controller's params" do 61 | put_request 62 | 63 | req_params = JSON.parse(request.env['params_spy'].to_json).with_indifferent_access 64 | 65 | expect(req_params).to include(postId: "1234") 66 | end 67 | end 68 | 69 | def put_request 70 | if Rails::VERSION::MAJOR >= 5 71 | put url, params: params.to_json, headers: headers 72 | else 73 | put url, params.to_json, headers 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /spec/olive_branch/middleware_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe OliveBranch::Middleware do 4 | describe "modifying request" do 5 | let(:params) do 6 | { 7 | "action_dispatch.request.request_parameters" => { 8 | "post" => { 9 | "authorName" => "Adam Smith" 10 | } 11 | } 12 | } 13 | end 14 | 15 | it "snake cases incoming params if content-type JSON and inflection header present" do 16 | incoming_params = nil 17 | 18 | app = -> (env) do 19 | incoming_params = env["action_dispatch.request.request_parameters"] 20 | [200, {}, ["{}"]] 21 | end 22 | 23 | env = params.merge( 24 | "CONTENT_TYPE" => "application/json", 25 | "HTTP_KEY_INFLECTION" => "camel" 26 | ) 27 | 28 | described_class.new(app).call(env) 29 | 30 | expect(incoming_params["post"]["author_name"]).not_to be_nil 31 | end 32 | 33 | it "snake cases incoming query params if content-type JSON and inflection header present" do 34 | incoming_params = nil 35 | 36 | app = -> (env) do 37 | incoming_params = env["action_dispatch.request.query_parameters"] 38 | [200, {}, ["{}"]] 39 | end 40 | 41 | env = params.merge( 42 | "CONTENT_TYPE" => "application/json", 43 | "HTTP_KEY_INFLECTION" => "camel", 44 | "QUERY_STRING" => "categoryFilter[categoryName]=economics", 45 | ) 46 | 47 | described_class.new(app).call(env) 48 | 49 | expect(incoming_params["category_filter"]["category_name"]).to eq "economics" 50 | end 51 | 52 | it 'snake cases incoming query with JSON and pascal inflection' do 53 | incoming_params = nil 54 | 55 | app = lambda do |env| 56 | incoming_params = env['action_dispatch.request.query_parameters'] 57 | [200, {}, ['{}']] 58 | end 59 | 60 | env = params.merge( 61 | 'CONTENT_TYPE' => 'application/json', 62 | 'HTTP_KEY_INFLECTION' => 'pascal', 63 | 'QUERY_STRING' => 'CategoryFilter[CategoryName]=economics' 64 | ) 65 | 66 | described_class.new(app).call(env) 67 | 68 | expect( 69 | incoming_params['category_filter']['category_name'] 70 | ).to eq 'economics' 71 | end 72 | 73 | it "does not modify incoming params if content-type not JSON" do 74 | incoming_params = nil 75 | 76 | app = -> (env) do 77 | incoming_params = env["action_dispatch.request.request_parameters"] 78 | [200, {}, ["{}"]] 79 | end 80 | 81 | env = params.merge( 82 | "CONTENT_TYPE" => "text/html", 83 | "HTTP_KEY_INFLECTION" => "camel" 84 | ) 85 | 86 | described_class.new(app).call(env) 87 | 88 | expect(incoming_params["post"]["authorName"]).not_to be_nil 89 | end 90 | 91 | it "does not modify incoming params if exclude returns true" do 92 | incoming_params = nil 93 | 94 | app = -> (env) do 95 | incoming_params = env["action_dispatch.request.request_parameters"] 96 | [200, {}, ["{}"]] 97 | end 98 | 99 | env = params.merge( 100 | "CONTENT_TYPE" => "application/json", 101 | "HTTP_KEY_INFLECTION" => "camel", 102 | "PATH_INFO" => "/do_not_transform" 103 | ) 104 | 105 | exclude_params = proc do |env| 106 | path = env["PATH_INFO"] 107 | !!path.match(/^\/do_not_transform/) 108 | end 109 | 110 | described_class.new(app, exclude_params: exclude_params).call(env) 111 | 112 | expect(incoming_params["post"]["authorName"]).not_to be_nil 113 | end 114 | 115 | it "does not modify incoming params if inflection header missing" do 116 | incoming_params = nil 117 | 118 | app = -> (env) do 119 | incoming_params = env["action_dispatch.request.request_parameters"] 120 | [200, {}, ["{}"]] 121 | end 122 | 123 | env = params.merge("CONTENT_TYPE" => "application/json") 124 | 125 | described_class.new(app).call(env) 126 | 127 | expect(incoming_params["post"]["authorName"]).not_to be_nil 128 | end 129 | 130 | context "with a custom content type check" do 131 | let(:content_type_check) do 132 | ->(content_type) { content_type == "foo/type" } 133 | end 134 | 135 | it "snake cases incoming params if content-type matches the custom check" do 136 | incoming_params = nil 137 | 138 | app = -> (env) do 139 | incoming_params = env["action_dispatch.request.request_parameters"] 140 | [200, {}, ["{}"]] 141 | end 142 | 143 | env = params.merge( 144 | "CONTENT_TYPE" => "foo/type", 145 | "HTTP_KEY_INFLECTION" => "camel" 146 | ) 147 | 148 | described_class.new(app, content_type_check: content_type_check).call(env) 149 | 150 | expect(incoming_params["post"]["author_name"]).not_to be_nil 151 | end 152 | 153 | it "does not modify incoming params if content-type not matching custom check" do 154 | incoming_params = nil 155 | 156 | app = -> (env) do 157 | incoming_params = env["action_dispatch.request.request_parameters"] 158 | [200, {}, ["{}"]] 159 | end 160 | 161 | env = params.merge( 162 | "CONTENT_TYPE" => "application/json", 163 | "HTTP_KEY_INFLECTION" => "camel" 164 | ) 165 | 166 | described_class.new(app, content_type_check: content_type_check).call(env) 167 | 168 | expect(incoming_params["post"]["authorName"]).not_to be_nil 169 | end 170 | end 171 | end 172 | 173 | describe "modifying response" do 174 | it "camel-cases response if JSON and inflection header present" do 175 | app = -> (env) do 176 | [ 177 | 200, 178 | { "Content-Type" => "application/json" }, 179 | ['{"post":{"author_name":"Adam Smith","author-hobby":"Economics"}}'] 180 | ] 181 | end 182 | 183 | request = Rack::MockRequest.new(described_class.new(app)) 184 | 185 | response = request.get("/", "HTTP_KEY_INFLECTION" => "camel") 186 | 187 | expect(JSON.parse(response.body)["post"]["authorName"]).not_to be_nil 188 | expect(JSON.parse(response.body)["post"]["authorHobby"]).not_to be_nil 189 | end 190 | 191 | it 'camel-cases array response if JSON and inflection header present' do 192 | app = lambda do |_env| 193 | [ 194 | 200, 195 | { 'Content-Type' => 'application/json' }, 196 | ['[{"author_name":"Adam Smith","author-hobby":"Economics"}]'] 197 | ] 198 | end 199 | 200 | request = Rack::MockRequest.new(described_class.new(app)) 201 | 202 | response = request.get('/', 'HTTP_KEY_INFLECTION' => 'camel') 203 | 204 | expect(JSON.parse(response.body)[0]['authorName']).not_to be_nil 205 | expect(JSON.parse(response.body)[0]['authorHobby']).not_to be_nil 206 | end 207 | 208 | it "dash-cases response if JSON and inflection header present" do 209 | app = -> (env) do 210 | [ 211 | 200, 212 | { "Content-Type" => "application/json" }, 213 | ['{"post":{"author_name":"Adam Smith"}}'] 214 | ] 215 | end 216 | 217 | request = Rack::MockRequest.new(described_class.new(app)) 218 | 219 | response = request.get("/", "HTTP_KEY_INFLECTION" => "dash") 220 | 221 | expect(JSON.parse(response.body)["post"]["author-name"]).not_to be_nil 222 | end 223 | 224 | it 'dash-cases array response if JSON and inflection header present' do 225 | app = lambda do |_env| 226 | [ 227 | 200, 228 | { 'Content-Type' => 'application/json' }, 229 | ['[{"author_name":"Adam Smith"}]'] 230 | ] 231 | end 232 | 233 | request = Rack::MockRequest.new(described_class.new(app)) 234 | 235 | response = request.get('/', 'HTTP_KEY_INFLECTION' => 'dash') 236 | 237 | expect(JSON.parse(response.body)[0]['author-name']).not_to be_nil 238 | end 239 | 240 | it 'pascal-cases response if JSON and inflection header are present' do 241 | app = lambda do |_env| 242 | [ 243 | 200, 244 | { 'Content-Type' => 'application/json' }, 245 | ['{"post":{"author_name":"Adam Smith"}}'] 246 | ] 247 | end 248 | 249 | request = Rack::MockRequest.new(described_class.new(app)) 250 | 251 | response = request.get('/', 'HTTP_KEY_INFLECTION' => 'pascal') 252 | expect(JSON.parse(response.body)['Post']['AuthorName']).not_to be_nil 253 | end 254 | 255 | it 'pascal-cases array response if JSON and inflection header present' do 256 | app = lambda do |_env| 257 | [ 258 | 200, 259 | { 'Content-Type' => 'application/json' }, 260 | ['[{"author_name":"Adam Smith","author-hobby":"Economics"}]'] 261 | ] 262 | end 263 | 264 | request = Rack::MockRequest.new(described_class.new(app)) 265 | 266 | response = request.get('/', 'HTTP_KEY_INFLECTION' => 'pascal') 267 | 268 | expect(JSON.parse(response.body)[0]['AuthorName']).not_to be_nil 269 | expect(JSON.parse(response.body)[0]['AuthorHobby']).not_to be_nil 270 | end 271 | 272 | it "does not modify response if exclude returns true" do 273 | app = -> (env) do 274 | [ 275 | 200, 276 | { "Content-Type" => "application/json" }, 277 | ['{"post":{"author_name":"Adam Smith"}}'] 278 | ] 279 | end 280 | 281 | exclude_response = proc do |env| 282 | path = env["PATH_INFO"] 283 | !!path.match(/^\/do_not_transform/) 284 | end 285 | 286 | request = Rack::MockRequest.new(described_class.new(app, exclude_response: exclude_response)) 287 | 288 | response = request.get("/do_not_transform", "HTTP_KEY_INFLECTION" => "camel") 289 | 290 | expect(JSON.parse(response.body)["post"]["author_name"]).not_to be_nil 291 | end 292 | 293 | it 'excludes rails routes by default' do 294 | app = lambda do |_env| 295 | [ 296 | 200, 297 | { 'Content-Type' => 'application/json' }, 298 | ['{"direct_upload_token": "value1", "attachment_name": "value2"}'] 299 | ] 300 | end 301 | 302 | request = Rack::MockRequest.new(described_class.new(app)) 303 | 304 | response = request.get('/rails', 'HTTP_KEY_INFLECTION' => 'camel') 305 | 306 | expect(JSON.parse(response.body)['direct_upload_token']).to eq 'value1' 307 | expect(JSON.parse(response.body)['attachment_name']).to eq 'value2' 308 | end 309 | 310 | it "does not modify response if not JSON " do 311 | app = -> (env) do 312 | [ 313 | 200, 314 | { "Content-Type" => "text/html" }, 315 | ['{"post":{"author_name":"Adam Smith"}}'] 316 | ] 317 | end 318 | 319 | request = Rack::MockRequest.new(described_class.new(app)) 320 | 321 | response = request.get("/", "HTTP_KEY_INFLECTION" => "camel") 322 | 323 | expect(JSON.parse(response.body)["post"]["author_name"]).not_to be_nil 324 | end 325 | 326 | it "does not modify response if inflection header missing" do 327 | app = -> (env) do 328 | [ 329 | 200, 330 | { "Content-Type" => "application/json" }, 331 | ['{"post":{"author_name":"Adam Smith"}}'] 332 | ] 333 | end 334 | 335 | request = Rack::MockRequest.new(described_class.new(app)) 336 | 337 | response = request.get("/") 338 | 339 | expect(JSON.parse(response.body)["post"]["author_name"]).not_to be_nil 340 | end 341 | 342 | it "does not modify response if invalid JSON" do 343 | app = -> (env) do 344 | [ 345 | 200, 346 | { "Content-Type" => "application/json" }, 347 | ['{"post":{"author_name":"Adam Smith"}'] 348 | ] 349 | end 350 | 351 | request = Rack::MockRequest.new(described_class.new(app)) 352 | 353 | response = request.get("/", "HTTP_KEY_INFLECTION" => "camel") 354 | 355 | expect(response.body =~ /author_name/).not_to be_nil 356 | end 357 | 358 | context "with custom camelize method" do 359 | let(:camelize) do 360 | ->(string) { "camel#{string}" } 361 | end 362 | 363 | it "uses the custom camelize method" do 364 | app = -> (env) do 365 | [ 366 | 200, 367 | { "Content-Type" => "application/json" }, 368 | ['{"post":{"author_name":"Adam Smith","author-hobby":"Economics"}}'] 369 | ] 370 | end 371 | 372 | request = Rack::MockRequest.new(described_class.new(app, camelize: camelize)) 373 | 374 | response = request.get("/", "HTTP_KEY_INFLECTION" => "camel") 375 | 376 | expect(JSON.parse(response.body)["camelpost"]["camelauthor_name"]).not_to be_nil 377 | expect(JSON.parse(response.body)["camelpost"]["camelauthor-hobby"]).not_to be_nil 378 | end 379 | end 380 | 381 | context "with custom dasherize method" do 382 | let(:dasherize) do 383 | ->(string) { "dash#{string}" } 384 | end 385 | 386 | it "uses the custom dasherize method" do 387 | app = -> (env) do 388 | [ 389 | 200, 390 | { "Content-Type" => "application/json" }, 391 | ['{"post":{"author_name":"Adam Smith","author-hobby":"Economics"}}'] 392 | ] 393 | end 394 | 395 | request = Rack::MockRequest.new(described_class.new(app, dasherize: dasherize)) 396 | 397 | response = request.get("/", "HTTP_KEY_INFLECTION" => "dash") 398 | 399 | expect(JSON.parse(response.body)["dashpost"]["dashauthor_name"]).not_to be_nil 400 | expect(JSON.parse(response.body)["dashpost"]["dashauthor-hobby"]).not_to be_nil 401 | end 402 | end 403 | 404 | context "with a default inflection" do 405 | it "uses the default inflection" do 406 | app = -> (env) do 407 | [ 408 | 200, 409 | { "Content-Type" => "application/json" }, 410 | ['{"post":{"author_name":"Adam Smith"}}'] 411 | ] 412 | end 413 | 414 | request = Rack::MockRequest.new(described_class.new(app, inflection: 'camel')) 415 | 416 | response = request.get("/") 417 | 418 | expect(JSON.parse(response.body)["post"]["authorName"]).not_to be_nil 419 | end 420 | end 421 | 422 | context "with frozen strings" do 423 | it "dups the string before replacing" do 424 | app = -> (env) do 425 | [ 426 | 200, 427 | { "Content-Type" => "application/json" }, 428 | ['{"post":{"author_name":"Adam Smith"}}'.freeze] 429 | ] 430 | end 431 | 432 | request = Rack::MockRequest.new(described_class.new(app, inflection: 'camel')) 433 | 434 | response = request.get("/") 435 | 436 | expect(JSON.parse(response.body)["post"]["authorName"]).not_to be_nil 437 | end 438 | end 439 | end 440 | end 441 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | require 'spec_helper' 3 | ENV['RAILS_ENV'] ||= 'test' 4 | require File.expand_path('../test_app/config/environment', __FILE__) 5 | abort("The Rails environment is running in production mode!") if Rails.env.production? 6 | require 'rspec/rails' 7 | 8 | RSpec.configure do |config| 9 | config.infer_spec_type_from_file_location! 10 | config.filter_rails_from_backtrace! 11 | end 12 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'olive_branch' 2 | 3 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 4 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 5 | # The generated `.rspec` file contains `--require spec_helper` which will cause 6 | # this file to always be loaded, without a need to explicitly require it in any 7 | # files. 8 | # 9 | # Given that it is always loaded, you are encouraged to keep this file as 10 | # light-weight as possible. Requiring heavyweight dependencies from this file 11 | # will add to the boot time of your test suite on EVERY test run, even for an 12 | # individual file that may not need all of that loaded. Instead, consider making 13 | # a separate helper file that requires the additional dependencies and performs 14 | # the additional setup, and require it from the spec files that actually need 15 | # it. 16 | # 17 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 18 | RSpec.configure do |config| 19 | # rspec-expectations config goes here. You can use an alternate 20 | # assertion/expectation library such as wrong or the stdlib/minitest 21 | # assertions if you prefer. 22 | config.expect_with :rspec do |expectations| 23 | # This option will default to `true` in RSpec 4. It makes the `description` 24 | # and `failure_message` of custom matchers include text for helper methods 25 | # defined using `chain`, e.g.: 26 | # be_bigger_than(2).and_smaller_than(4).description 27 | # # => "be bigger than 2 and smaller than 4" 28 | # ...rather than: 29 | # # => "be bigger than 2" 30 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 31 | end 32 | 33 | # rspec-mocks config goes here. You can use an alternate test double 34 | # library (such as bogus or mocha) by changing the `mock_with` option here. 35 | config.mock_with :rspec do |mocks| 36 | # Prevents you from mocking or stubbing a method that does not exist on 37 | # a real object. This is generally recommended, and will default to 38 | # `true` in RSpec 4. 39 | mocks.verify_partial_doubles = true 40 | end 41 | 42 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 43 | # have no way to turn it off -- the option exists only for backwards 44 | # compatibility in RSpec 3). It causes shared context metadata to be 45 | # inherited by the metadata hash of host groups and examples, rather than 46 | # triggering implicit auto-inclusion in groups with matching metadata. 47 | config.shared_context_metadata_behavior = :apply_to_host_groups 48 | 49 | # The settings below are suggested to provide a good initial experience 50 | # with RSpec, but feel free to customize to your heart's content. 51 | =begin 52 | # This allows you to limit a spec run to individual examples or groups 53 | # you care about by tagging them with `:focus` metadata. When nothing 54 | # is tagged with `:focus`, all examples get run. RSpec also provides 55 | # aliases for `it`, `describe`, and `context` that include `:focus` 56 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 57 | config.filter_run_when_matching :focus 58 | 59 | # Allows RSpec to persist some state between runs in order to support 60 | # the `--only-failures` and `--next-failure` CLI options. We recommend 61 | # you configure your source control system to ignore this file. 62 | config.example_status_persistence_file_path = "spec/examples.txt" 63 | 64 | # Limits the available syntax to the non-monkey patched syntax that is 65 | # recommended. For more details, see: 66 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 67 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 68 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 69 | config.disable_monkey_patching! 70 | 71 | # Many RSpec users commonly either run the entire suite or an individual 72 | # file, and it's useful to allow more verbose output when running an 73 | # individual spec file. 74 | if config.files_to_run.one? 75 | # Use the documentation formatter for detailed output, 76 | # unless a formatter has already been configured 77 | # (e.g. via a command-line flag). 78 | config.default_formatter = "doc" 79 | end 80 | 81 | # Print the 10 slowest examples and example groups at the 82 | # end of the spec run, to help surface which specs are running 83 | # particularly slow. 84 | config.profile_examples = 10 85 | 86 | # Run specs in random order to surface order dependencies. If you find an 87 | # order dependency and want to debug it, you can fix the order by providing 88 | # the seed, which is printed after each run. 89 | # --seed 1234 90 | config.order = :random 91 | 92 | # Seed global randomization in this process using the `--seed` CLI option. 93 | # Setting this allows you to use `--seed` to deterministically reproduce 94 | # test failures related to randomization by passing the same `--seed` value 95 | # as the one that triggered the failure. 96 | Kernel.srand config.seed 97 | =end 98 | end 99 | -------------------------------------------------------------------------------- /spec/test_app/.gitignore: -------------------------------------------------------------------------------- 1 | tmp/pids/* 2 | -------------------------------------------------------------------------------- /spec/test_app/Rakefile: -------------------------------------------------------------------------------- 1 | require_relative 'config/application' 2 | 3 | Rails.application.load_tasks 4 | -------------------------------------------------------------------------------- /spec/test_app/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # ApplicationController 2 | class ApplicationController < ActionController::Base; end 3 | -------------------------------------------------------------------------------- /spec/test_app/app/controllers/posts_controller.rb: -------------------------------------------------------------------------------- 1 | # PostsController 2 | class PostsController < ApplicationController 3 | COMPLEX_RESPONSE ||= JSON.parse( 4 | File.read(Rails.root.join('example_responses/complex.json')) 5 | ) 6 | 7 | def update 8 | request.env['params_spy'] = params 9 | 10 | payload = { 11 | post_author_name: params[:post][:author_name], 12 | category_filter_name: (params[:category_filter] || {})[:category_name] 13 | } 14 | 15 | render json: payload 16 | end 17 | 18 | def complex 19 | render json: COMPLEX_RESPONSE 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/test_app/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /spec/test_app/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /spec/test_app/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /spec/test_app/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /spec/test_app/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'action_controller/railtie' 4 | require 'olive_branch' 5 | 6 | Bundler.require(*Rails.groups) 7 | 8 | module TestApp 9 | # Application 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 if config.respond_to?(:load_defaults) 13 | 14 | config.middleware.use OliveBranch::Middleware 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/test_app/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) 6 | -------------------------------------------------------------------------------- /spec/test_app/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /spec/test_app/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | config.cache_classes = false 3 | config.eager_load = false 4 | config.consider_all_requests_local = true 5 | config.active_support.deprecation = :log 6 | end 7 | -------------------------------------------------------------------------------- /spec/test_app/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | config.cache_classes = true 3 | config.eager_load = true 4 | config.consider_all_requests_local = false 5 | config.action_controller.perform_caching = true 6 | config.read_encrypted_secrets = true 7 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 8 | config.log_level = :debug 9 | config.log_tags = [:request_id] 10 | config.i18n.fallbacks = true 11 | config.active_support.deprecation = :notify 12 | config.log_formatter = ::Logger::Formatter.new 13 | end 14 | -------------------------------------------------------------------------------- /spec/test_app/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | config.cache_classes = true 3 | config.eager_load = false 4 | config.consider_all_requests_local = true 5 | config.action_controller.perform_caching = false 6 | config.action_dispatch.show_exceptions = false 7 | config.action_controller.allow_forgery_protection = false 8 | config.active_support.deprecation = :stderr 9 | end 10 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | ActiveSupport.on_load(:action_controller) do 2 | wrap_parameters format: [:json] 3 | end 4 | -------------------------------------------------------------------------------- /spec/test_app/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # NOTE: The camelized path parameter is used to test that we do *not* apply 3 | # any transformations on path parameters. 4 | put 'posts/:postId', format: :json, to: 'posts#update' 5 | get 'posts/complex', format: :json, to: 'posts#complex' 6 | end 7 | -------------------------------------------------------------------------------- /spec/test_app/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: df191712ea642f3dde713d27fb8e7936557b490ed82cc8da8904623290594a5376a50ac46a33a7c6804fc16f59f11a87efbef31805c4305b43b1bc2175c217b8 22 | 23 | test: 24 | secret_key_base: 7911e48bffad7dddbaa6faf1fa71af8190046bf0847632ce70555faf7dc42079148f822ed4d081442d0a406771d0befdc6662d642ceaeb5e632323ec3191b21f 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /spec/test_app/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /spec/test_app/example_responses/complex.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"","type":"","attributes":{"max_score":null,"current_page":1,"total_pages":2,"total_items":38,"start_index":1,"end_index":24,"aggregations":{"categories":[{"key":"","doc_count":6719},{"key":"","doc_count":4478},{"key":"","doc_count":3001},{"key":"","doc_count":2397},{"key":"","doc_count":2188},{"key":"","doc_count":2179},{"key":"","doc_count":1822},{"key":"","doc_count":1288},{"key":"","doc_count":1104},{"key":"","doc_count":1024},{"key":"","doc_count":817},{"key":"","doc_count":780},{"key":"","doc_count":751},{"key":"","doc_count":731},{"key":"","doc_count":686},{"key":"","doc_count":489},{"key":"","doc_count":466},{"key":"","doc_count":464},{"key":"","doc_count":364},{"key":"","doc_count":233},{"key":"","doc_count":206},{"key":"","doc_count":190},{"key":"","doc_count":187},{"key":"","doc_count":155},{"key":"","doc_count":143},{"key":"","doc_count":121},{"key":"","doc_count":98},{"key":"","doc_count":83},{"key":"","doc_count":74},{"key":"","doc_count":73},{"key":"","doc_count":72},{"key":"","doc_count":63},{"key":"","doc_count":59},{"key":"","doc_count":50},{"key":"","doc_count":48},{"key":"","doc_count":46},{"key":"","doc_count":32},{"key":"","doc_count":29},{"key":"","doc_count":25},{"key":"","doc_count":14},{"key":"","doc_count":8},{"key":"","doc_count":6},{"key":"","doc_count":4}],"item_attributes":{"retina_ready":[],"add_ons_applications_supported":[],"columns":[],"graphics_applications_supported":[],"min_browser_version":[],"graphic_templates_applications_supported":[{"key":"","doc_count":36}],"colors":[],"is_tileable":[],"email_platforms":[],"spacing":[],"sizes":[],"html_templates_file_types":[],"has_documentation":[{"key":1,"key_as_string":"","doc_count":34},{"key":0,"key_as_string":"","doc_count":1}],"psd_files_included":[],"presentation_templates_file_types":[],"dpi":[{"key":300,"doc_count":20},{"key":72,"doc_count":6},{"key":150,"doc_count":2},{"key":350,"doc_count":2}],"color_space":[{"key":"","doc_count":38}],"web_templates_file_types":[],"orientation":[{"key":"","doc_count":34},{"key":"","doc_count":1},{"key":"","doc_count":1}],"presentation_templates_applications_supported":[],"optimum_size":[],"fonts_file_types":[],"serif_sans_serif":[],"sketch_files_included":[],"graphics_file_types":[],"is_webfont":[],"layout":[],"add_ons_file_types":[],"graphic_templates_file_types":[{"key":"","doc_count":37},{"key":"","doc_count":2},{"key":"","doc_count":2},{"key":"","doc_count":2}],"background":[],"preview_url":[],"responsive":[],"is_layered":[{"key":1,"key_as_string":"","doc_count":37}],"is_vector":[{"key":1,"key_as_string":"","doc_count":1}],"ecommerce_ready":[],"dimensions":[]},"tags":[],"item_type":{"graphic-templates":10123,"3d":6719,"graphics":5952,"fonts":1301,"web-templates":1014,"presentation-templates":731,"add-ons":653,"cms-templates":604,"photos":0},"total_library_size":27097},"items":[{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":220712478,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":155630782,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_vector":"","is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":56637739,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":56274348,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":97720223,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":685945639,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":65654090,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":71500197,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":999403649,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":803774132,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":377501157,"market_item":{"id":"","demo_url":null,"legacy_id":19872404},"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":["","",""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":438813458,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":378215054,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":["",""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":2950706606,"market_item":{"id":"","demo_url":null,"legacy_id":14771025},"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":324307632,"market_item":{"id":"","demo_url":null,"legacy_id":19757621},"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":null,"description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":177486091,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":580924352,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":785042196,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":103014265,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":690117106,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","dimensions":{"width":"","height":"","unit":""},"graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":922304110,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":26296645,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":["",""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":560458794,"market_item":null,"pixelsquid_product":null},{"id":"","item_uuid":"","slug":"","title":"","sub_title":"","description":"","item_type":"","item_type_name":"","saleable":true,"published_at":"","updated_at":"","tags":["","","","","","","","","","","","","","",""],"categories":[""],"item_attributes":{"dpi":"","orientation":"","dimensions":{"width":"","height":"","unit":""},"is_layered":"","graphic_templates_applications_supported":[""],"has_documentation":"","graphic_templates_file_types":[""],"color_space":""},"cover_image":{"id":"","signed_image_set_sizes":{"w140":"","w174":"","w316":"","w355":"","w433":"","w632":"","w710":"","w866":"","w900":"","w1019":"","w1170":"","w1370":"","w2038":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":"","og1200x800":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},"preview_images":[{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""},{"id":"","signed_image_set_sizes":{"w316":"","w632":"","w900":"","w1170":"","w1370":"","w2740":"","tn60x40":"","tn120x80":"","tn316x211":"","tn632x421":""},"file_extension":"","s3_bucket_name":"","cdn_subdomain":"","provider_cloud_name":""}],"contributor_username":"","file_name":"","file_size_bytes":141776759,"market_item":{"id":"","demo_url":null,"legacy_id":13563329},"pixelsquid_product":null}]}}} 2 | -------------------------------------------------------------------------------- /spec/test_app/log/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | -------------------------------------------------------------------------------- /spec/test_app/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigetlabs/olive_branch/8bd792610945ff3dc7b379a0aa7e039aa79a4e94/spec/test_app/log/.keep -------------------------------------------------------------------------------- /spec/test_app/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/test_app/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/test_app/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /spec/test_app/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigetlabs/olive_branch/8bd792610945ff3dc7b379a0aa7e039aa79a4e94/spec/test_app/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /spec/test_app/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigetlabs/olive_branch/8bd792610945ff3dc7b379a0aa7e039aa79a4e94/spec/test_app/public/apple-touch-icon.png -------------------------------------------------------------------------------- /spec/test_app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigetlabs/olive_branch/8bd792610945ff3dc7b379a0aa7e039aa79a4e94/spec/test_app/public/favicon.ico -------------------------------------------------------------------------------- /spec/test_app/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigetlabs/olive_branch/8bd792610945ff3dc7b379a0aa7e039aa79a4e94/spec/test_app/tmp/.keep --------------------------------------------------------------------------------