diff --git a/.gitignore b/.gitignore index 9fcfc5cca..ac2cc5770 100644 --- a/.gitignore +++ b/.gitignore @@ -35,8 +35,8 @@ capybara-*.html /tmp/* /data/* /db/*.sqlite3 -/public/system/* -public/assets +/public/* +!/public/.keep /coverage/ /spec/tmp/ /config/data_bags/* diff --git a/.travis.yml b/.travis.yml index dbe99ec55..826f7394a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,11 +52,6 @@ after_success: docker push $REPO:$TRAVIS_TAG; echo "Pushed to" $REPO:$TRAVIS_TAG; AUTO_DEPLOY=true; - elif [ "$TRAVIS_PULL_REQUEST" == "true" ]; then - docker build -f Dockerfile -t $REPO:$TRAVIS_PULL_REQUEST_BRANCH .; - docker push $REPO:$TRAVIS_PULL_REQUEST_BRANCH; - echo "Pushed to" $REPO:$TRAVIS_PULL_REQUEST_BRANCH; - AUTO_DEPLOY=true; elif [ "$TRAVIS_BRANCH" == "master" ]; then docker build -f Dockerfile -t $REPO .; docker push $REPO; @@ -66,6 +61,7 @@ after_success: docker build -f Dockerfile -t $REPO:$TRAVIS_BRANCH .; docker push $REPO:$TRAVIS_BRANCH; echo "Pushed to" $REPO:$TRAVIS_BRANCH; + AUTO_DEPLOY=true; fi - if [ "$AUTO_DEPLOY" == "true" ]; then @@ -90,10 +86,6 @@ after_success: git add prod-eu-west/services/client-api/_lupo.auto.tfvars; git commit -m "Adding lupo git variables for commit tagged ${TRAVIS_TAG?}"; git push "https://${TRAVIS_SECURE_TOKEN}@github.com/datacite/mastino.git" master; - elif [ "$TRAVIS_PULL_REQUEST" == "true" ]; then - git add stage/services/client-api/_lupo.auto.tfvars; - git commit -m "Adding lupo git variables for pull request ${TRAVIS_PULL_REQUEST}"; - git push "https://${TRAVIS_SECURE_TOKEN}@github.com/datacite/mastino.git" ${TRAVIS_PULL_REQUEST_BRANCH}; else git add stage/services/client-api/_lupo.auto.tfvars; git commit -m "Adding lupo git variables for latest commit"; diff --git a/Dockerfile b/Dockerfile index 698ce7ac8..dc48bb109 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN bash -lc 'rvm --default use ruby-2.4.4' # Update installed APT packages RUN apt-get update && apt-get upgrade -y -o Dpkg::Options::="--force-confold" && \ - apt-get install ntp wget tzdata -y && \ + apt-get install ntp wget tzdata pandoc -y && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # install dockerize @@ -53,12 +53,18 @@ RUN mkdir -p tmp/pids && \ chown -R app:app /home/app/webapp && \ chmod -R 755 /home/app/webapp +# Install Ruby gems for middleman +WORKDIR /home/app/webapp/vendor/middleman +RUN /sbin/setuser app bundle install + # Add Runit script for shoryuken workers +WORKDIR /home/app/webapp RUN mkdir /etc/service/shoryuken ADD vendor/docker/shoryuken.sh /etc/service/shoryuken/run # Run additional scripts during container startup (i.e. not at build time) RUN mkdir -p /etc/my_init.d +COPY vendor/docker/70_index_page.sh /etc/my_init.d/70_index_page.sh COPY vendor/docker/80_flush_cache.sh /etc/my_init.d/80_flush_cache.sh COPY vendor/docker/90_migrate.sh /etc/my_init.d/90_migrate.sh diff --git a/Gemfile b/Gemfile index c2444041e..43b13cef7 100644 --- a/Gemfile +++ b/Gemfile @@ -4,17 +4,19 @@ gem 'rails', '~> 5.2.0' gem 'bootsnap', '~> 1.2', '>= 1.2.1' gem 'mysql2', '~> 0.4.4' gem 'dotenv' +gem 'rake', '~> 12.0' gem 'multi_json' gem 'json', '~> 1.8', '>= 1.8.5' gem 'oj', '>= 2.8.3' gem 'jsonlint', '~> 0.2.0' gem 'equivalent-xml', '~> 0.6.0' gem 'nokogiri', '~> 1.8.1' +gem 'diffy', '~> 3.2', '>= 3.2.1' gem 'commonmarker', '~> 0.17.9' gem 'iso8601', '~> 0.9.0' gem 'patron', '~> 0.13.1', require: false gem 'maremma', '>= 4.1' -gem 'bolognese', '~> 0.9', '>= 0.10' +gem 'bolognese', '~> 1.0' gem 'dalli', '~> 2.7', '>= 2.7.6' gem 'lograge', '~> 0.10.0' gem 'logstash-event', '~> 1.2', '>= 1.2.02' @@ -32,9 +34,9 @@ gem 'api-pagination' gem 'cancancan', '~> 2.0' gem 'country_select', '~> 3.1' gem 'countries', '~> 2.1', '>= 2.1.2' -gem 'aasm', '~> 4.12', '>= 4.12.3' +gem 'aasm', '~> 5.0', '>= 5.0.1' gem "facets", require: false -gem 'shoryuken', '~> 3.2', '>= 3.2.2' +gem 'shoryuken', '~> 4.0' gem "aws-sdk-s3", require: false gem 'aws-sdk-sqs', '~> 1.3' gem 'bergamasco', '~> 0.3.10' @@ -52,6 +54,7 @@ gem 'elasticsearch-rails', '~> 5.0', '>= 5.0.2' gem 'faraday_middleware-aws-sigv4', '~> 0.2.4' gem 'rack-utf8_sanitizer', '~> 1.6' gem 'oj_mimic_json', '~> 1.0', '>= 1.0.1' +gem 'turnout', '~> 2.5' group :development, :test do gem 'rspec-rails', '~> 3.5', '>= 3.5.2' @@ -64,6 +67,7 @@ group :development do gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' + gem 'spring-commands-rspec' # gem 'httplog', '~> 1.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 4be65ca64..994781cf0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,50 +1,50 @@ GEM remote: https://rubygems.org/ specs: - aasm (4.12.3) + aasm (5.0.1) concurrent-ruby (~> 1.0) - actioncable (5.2.1) - actionpack (= 5.2.1) + actioncable (5.2.2) + actionpack (= 5.2.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.1) - actionpack (= 5.2.1) - actionview (= 5.2.1) - activejob (= 5.2.1) + actionmailer (5.2.2) + actionpack (= 5.2.2) + actionview (= 5.2.2) + activejob (= 5.2.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.1) - actionview (= 5.2.1) - activesupport (= 5.2.1) + actionpack (5.2.2) + actionview (= 5.2.2) + activesupport (= 5.2.2) rack (~> 2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.1) - activesupport (= 5.2.1) + actionview (5.2.2) + activesupport (= 5.2.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - active_model_serializers (0.10.7) + active_model_serializers (0.10.8) actionpack (>= 4.1, < 6) activemodel (>= 4.1, < 6) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (5.2.1) - activesupport (= 5.2.1) + activejob (5.2.2) + activesupport (= 5.2.2) globalid (>= 0.3.6) - activemodel (5.2.1) - activesupport (= 5.2.1) - activerecord (5.2.1) - activemodel (= 5.2.1) - activesupport (= 5.2.1) + activemodel (5.2.2) + activesupport (= 5.2.2) + activerecord (5.2.2) + activemodel (= 5.2.2) + activesupport (= 5.2.2) arel (>= 9.0) - activestorage (5.2.1) - actionpack (= 5.2.1) - activerecord (= 5.2.1) + activestorage (5.2.2) + actionpack (= 5.2.2) + activerecord (= 5.2.2) marcel (~> 0.3.1) - activesupport (5.2.1) + activesupport (5.2.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -52,24 +52,24 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) ansi (1.5.0) - api-pagination (4.8.1) + api-pagination (4.8.2) arel (9.0.0) aws-eventstream (1.0.1) - aws-partitions (1.105.0) - aws-sdk-core (3.30.0) + aws-partitions (1.125.0) + aws-sdk-core (3.44.0) aws-eventstream (~> 1.0) aws-partitions (~> 1.0) aws-sigv4 (~> 1.0) jmespath (~> 1.0) - aws-sdk-kms (1.9.0) - aws-sdk-core (~> 3, >= 3.26.0) + aws-sdk-kms (1.13.0) + aws-sdk-core (~> 3, >= 3.39.0) aws-sigv4 (~> 1.0) - aws-sdk-s3 (1.21.0) - aws-sdk-core (~> 3, >= 3.26.0) + aws-sdk-s3 (1.30.0) + aws-sdk-core (~> 3, >= 3.39.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.0) - aws-sdk-sqs (1.7.0) - aws-sdk-core (~> 3, >= 3.26.0) + aws-sdk-sqs (1.10.0) + aws-sdk-core (~> 3, >= 3.39.0) aws-sigv4 (~> 1.0) aws-sigv4 (1.0.3) base32-url (0.5) @@ -93,13 +93,14 @@ GEM latex-decode (~> 0.0) binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) - bolognese (0.15.6) + bolognese (1.0.32) activesupport (>= 4.2.5, < 6) benchmark_methods (~> 0.7) bibtex-ruby (~> 4.1) builder (~> 3.2, >= 3.2.2) citeproc-ruby (~> 1.1, >= 1.1.10) colorize (~> 0.8.1) + concurrent-ruby (~> 1.0.5) csl-styles (~> 1.0, >= 1.0.1.8) edtf (~> 3.0, >= 3.0.4) gender_detector (~> 0.1.2) @@ -117,18 +118,19 @@ GEM thor (~> 0.19) bootsnap (1.3.2) msgpack (~> 1.0) - bugsnag (6.8.0) + bugsnag (6.10.0) concurrent-ruby (~> 1.0) builder (3.2.3) byebug (10.0.2) cancancan (2.3.0) - capybara (3.9.0) + capybara (3.12.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - xpath (~> 3.1) + regexp_parser (~> 1.2) + xpath (~> 3.2) case_transform (0.2) activesupport citeproc (1.0.9) @@ -160,10 +162,11 @@ GEM csl (~> 1.0) css_parser (1.6.0) addressable - dalli (2.7.8) + dalli (2.7.9) database_cleaner (1.7.0) debug_inspector (0.0.3) diff-lcs (1.3) + diffy (3.2.1) docile (1.1.5) docopt (0.6.1) domain_name (0.5.20180417) @@ -202,7 +205,7 @@ GEM railties (>= 3.0.0) faker (1.9.1) i18n (>= 0.7) - faraday (0.15.3) + faraday (0.15.4) multipart-post (>= 1.2, < 3) faraday-encoding (0.0.5) faraday @@ -211,7 +214,7 @@ GEM faraday_middleware-aws-sigv4 (0.2.4) aws-sigv4 (~> 1.0) faraday (>= 0.9) - fast_jsonapi (1.4) + fast_jsonapi (1.5) activesupport (>= 4.2) ffi (1.9.25) flipper (0.16.0) @@ -232,7 +235,7 @@ GEM htmlentities (4.3.4) http-cookie (1.0.3) domain_name (~> 0.5) - i18n (1.1.0) + i18n (1.2.0) concurrent-ruby (~> 1.0) i18n_data (0.8.0) iso8601 (0.9.1) @@ -272,10 +275,10 @@ GEM logstash-event (1.2.02) logstash-logger (0.26.1) logstash-event (~> 1.2) - loofah (2.2.2) + loofah (2.2.3) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.0) + mail (2.7.1) mini_mime (>= 0.1.1) mailgun-ruby (1.1.11) rest-client (~> 2.0.2) @@ -292,7 +295,7 @@ GEM multi_json (~> 1.12) nokogiri (~> 1.8.1) oj (>= 2.8.3) - method_source (0.9.0) + method_source (0.9.2) mime-types (3.2.2) mime-types-data (~> 3.2015) mime-types-data (3.2018.0812) @@ -301,7 +304,7 @@ GEM mini_mime (1.0.1) mini_portile2 (2.3.0) minitest (5.11.3) - money (6.13.0) + money (6.13.1) i18n (>= 0.6.4, <= 2) msgpack (1.2.4) multi_json (1.13.1) @@ -328,41 +331,43 @@ GEM pwqgen.rb (0.1.0) docopt (~> 0.5) sysrandom - rack (2.0.5) + rack (2.0.6) + rack-accept (0.4.5) + rack (>= 0.4) rack-cors (1.0.2) rack-test (1.1.0) rack (>= 1.0, < 3) rack-utf8_sanitizer (1.6.0) rack (>= 1.0, < 3.0) - rails (5.2.1) - actioncable (= 5.2.1) - actionmailer (= 5.2.1) - actionpack (= 5.2.1) - actionview (= 5.2.1) - activejob (= 5.2.1) - activemodel (= 5.2.1) - activerecord (= 5.2.1) - activestorage (= 5.2.1) - activesupport (= 5.2.1) + rails (5.2.2) + actioncable (= 5.2.2) + actionmailer (= 5.2.2) + actionpack (= 5.2.2) + actionview (= 5.2.2) + activejob (= 5.2.2) + activemodel (= 5.2.2) + activerecord (= 5.2.2) + activestorage (= 5.2.2) + activesupport (= 5.2.2) bundler (>= 1.3.0) - railties (= 5.2.1) + railties (= 5.2.2) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.0.4) loofah (~> 2.2, >= 2.2.2) - railties (5.2.1) - actionpack (= 5.2.1) - activesupport (= 5.2.1) + railties (5.2.2) + actionpack (= 5.2.2) + activesupport (= 5.2.2) method_source rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) - rake (12.3.1) + rake (12.3.2) rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - rdf (3.0.4) + rdf (3.0.7) hamster (~> 3.0) link_header (~> 0.0, >= 0.0.8) rdf-aggregate-repo (2.2.1) @@ -383,6 +388,7 @@ GEM rdf (>= 2.2, < 4.0) rdf-xsd (3.0.1) rdf (~> 3.0) + regexp_parser (1.3.0) request_store (1.4.1) rack (>= 1.4) rest-client (2.0.2) @@ -397,7 +403,7 @@ GEM rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) - rspec-rails (3.8.0) + rspec-rails (3.8.1) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -410,7 +416,7 @@ GEM i18n ruby_dep (1.5.0) safe_yaml (1.0.4) - shoryuken (3.3.0) + shoryuken (4.0.2) aws-sdk-core (>= 2) concurrent-ruby thor @@ -428,6 +434,8 @@ GEM unicode_utils (>= 1.2.2) spring (2.0.2) activesupport (>= 4.2) + spring-commands-rspec (1.0.4) + spring (>= 0.9.1) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) @@ -444,10 +452,15 @@ GEM rdf (>= 2.2, < 4.0) sysrandom (1.0.5) temple (0.8.0) - thor (0.20.0) + thor (0.20.3) thread_safe (0.3.6) - tilt (2.0.8) + tilt (2.0.9) trollop (2.9.9) + turnout (2.5.0) + i18n (>= 0.7, < 2) + rack (>= 1.3, < 3) + rack-accept (~> 0.4) + tilt (>= 1.4, < 3) tzinfo (1.2.5) thread_safe (~> 0.1) unf (0.1.4) @@ -462,14 +475,14 @@ GEM websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) - xpath (3.1.0) + xpath (3.2.0) nokogiri (~> 1.8) PLATFORMS ruby DEPENDENCIES - aasm (~> 4.12, >= 4.12.3) + aasm (~> 5.0, >= 5.0.1) active_model_serializers (~> 0.10.0) api-pagination aws-sdk-s3 @@ -479,7 +492,7 @@ DEPENDENCIES bergamasco (~> 0.3.10) better_errors binding_of_caller - bolognese (~> 0.9, >= 0.10) + bolognese (~> 1.0) bootsnap (~> 1.2, >= 1.2.1) bugsnag (~> 6.1, >= 6.1.1) byebug @@ -491,6 +504,7 @@ DEPENDENCIES country_select (~> 3.1) dalli (~> 2.7, >= 2.7.6) database_cleaner + diffy (~> 3.2, >= 3.2.1) dotenv elasticsearch-extensions (~> 0.0.29) elasticsearch-model (~> 5.0, >= 5.0.2) @@ -527,16 +541,19 @@ DEPENDENCIES rack-cors (~> 1.0, >= 1.0.2) rack-utf8_sanitizer (~> 1.6) rails (~> 5.2.0) + rake (~> 12.0) rspec-rails (~> 3.5, >= 3.5.2) - shoryuken (~> 3.2, >= 3.2.2) + shoryuken (~> 4.0) shoulda-matchers (~> 3.1) simple_command slack-notifier (~> 2.1) spring + spring-commands-rspec spring-watcher-listen (~> 2.0.0) strip_attributes (~> 1.8) + turnout (~> 2.5) vcr (~> 3.0.3) webmock (~> 3.1) BUNDLED WITH - 1.16.1 + 1.17.1 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 24e5349a9..5a5dc755a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -73,7 +73,9 @@ def type_and_credentials_from_request_headers when "CanCan::AccessDenied" then 403 when "ActiveRecord::RecordNotFound", "AbstractController::ActionNotFound", "ActionController::RoutingError" then 404 when "ActionController::UnknownFormat" then 406 + when "ActiveRecord::RecordNotUnique" then 409 when "ActiveModel::ForbiddenAttributesError", "ActionController::ParameterMissing", "ActionController::UnpermittedParameters", "ActiveModelSerializers::Adapter::JsonApi::Deserialization::InvalidDocument" then 422 + when "SocketError" then 500 else 400 end @@ -88,6 +90,10 @@ def type_and_credentials_from_request_headers message = "The resource you are looking for doesn't exist." elsif status == 406 message = "The content type is not recognized." + elsif status == 409 + message = "The resource already exists." + elsif ["JSON::ParserError", "Nokogiri::XML::SyntaxError", "ActionDispatch::Http::Parameters::ParseError"].include?(exception.class.to_s) + message = exception.message else Bugsnag.notify(exception) diff --git a/app/controllers/client_prefixes_controller.rb b/app/controllers/client_prefixes_controller.rb index 1c50221c9..54d3175da 100644 --- a/app/controllers/client_prefixes_controller.rb +++ b/app/controllers/client_prefixes_controller.rb @@ -150,9 +150,9 @@ def set_client_prefix end def safe_params - puts params ActiveModelSerializers::Deserialization.jsonapi_parse!( - params, only: [:id, :client, :prefix, :provider_prefix] + params, only: [:id, :client, :prefix, :providerPrefix], + keys: { "providerPrefix" => :provider_prefix } ) end end diff --git a/app/controllers/clients_controller.rb b/app/controllers/clients_controller.rb index ee8645887..85bf650ff 100644 --- a/app/controllers/clients_controller.rb +++ b/app/controllers/clients_controller.rb @@ -31,13 +31,14 @@ def index elsif params[:ids].present? response = Client.find_by_ids(params[:ids], page: page, sort: sort) else - response = Client.query(params[:query], year: params[:year], provider_id: params[:provider_id], fields: params[:fields], page: page, sort: sort) + response = Client.query(params[:query], year: params[:year], provider_id: params[:provider_id], software: params[:software], query_fields: params[:query_fields], page: page, sort: sort) end total = response.results.total total_pages = page[:size] > 0 ? (total.to_f / page[:size]).ceil : 0 years = total > 0 ? facet_by_year(response.response.aggregations.years.buckets) : nil providers = total > 0 ? facet_by_provider(response.response.aggregations.providers.buckets) : nil + software = total > 0 ? facet_by_software(response.response.aggregations.software.buckets) : nil @clients = response.results.results @@ -47,7 +48,8 @@ def index "total-pages" => total_pages, page: page[:number], years: years, - providers: providers + providers: providers, + software: software }.compact options[:links] = { @@ -55,6 +57,7 @@ def index next: @clients.blank? ? nil : request.base_url + "/clients?" + { query: params[:query], "provider-id" => params[:provider_id], + software: params[:software], year: params[:year], fields: params[:fields], "page[number]" => page[:number] + 1, @@ -146,8 +149,8 @@ def set_client def safe_params fail JSON::ParserError, "You need to provide a payload following the JSONAPI spec" unless params[:data].present? ActiveModelSerializers::Deserialization.jsonapi_parse!( - params, only: [:symbol, :name, "contact-name", "contact-email", :domains, :provider, :url, :repository, "target-id", "is-active", "password-input"], - keys: { "contact-name" => :contact_name, "contact-email" => :contact_email, "target-id" => :target_id, "is-active" => :is_active, "password-input" => :password_input } + params, only: [:symbol, :name, "contactName", "contactEmail", :domains, :provider, :url, :repository, :description, :software, "targetId", "isActive", "passwordInput"], + keys: { "contactName" => :contact_name, "contactEmail" => :contact_email, "targetId" => :target_id, "isActive" => :is_active, "passwordInput" => :password_input } ) end end diff --git a/app/controllers/concerns/countable.rb b/app/controllers/concerns/countable.rb index 4e5ae65a3..52c9570cd 100644 --- a/app/controllers/concerns/countable.rb +++ b/app/controllers/concerns/countable.rb @@ -24,7 +24,7 @@ def client_count(provider_id: nil) response = Client.query(nil, include_deleted: true, page: { number: 1, size: 0 }) end - response.results.total > 0 ? facet_by_cumuative_year(response.response.aggregations.cumulative_years.buckets) : [] + response.results.total > 0 ? facet_by_cumulative_year(response.response.aggregations.cumulative_years.buckets) : [] end # show provider count for admin @@ -33,7 +33,7 @@ def provider_count(provider_id: nil) return nil if provider_id response = Provider.query(nil, include_deleted: true, page: { number: 1, size: 0 }) - response.results.total > 0 ? facet_by_cumuative_year(response.response.aggregations.cumulative_years.buckets) : [] + response.results.total > 0 ? facet_by_cumulative_year(response.response.aggregations.cumulative_years.buckets) : [] end end end diff --git a/app/controllers/concerns/facetable.rb b/app/controllers/concerns/facetable.rb index 0c661ebcb..623febd63 100644 --- a/app/controllers/concerns/facetable.rb +++ b/app/controllers/concerns/facetable.rb @@ -22,7 +22,7 @@ def facet_by_year(arr) end end - def facet_by_cumuative_year(arr) + def facet_by_cumulative_year(arr) arr.map do |hsh| { "id" => hsh["key"].to_s, "title" => hsh["key"].to_s, @@ -38,6 +38,14 @@ def facet_by_key(arr) end end + def facet_by_software(arr) + arr.map do |hsh| + { "id" => hsh["key"].downcase, + "title" => hsh["key"], + "count" => hsh["doc_count"] } + end + end + def facet_by_schema(arr) arr.map do |hsh| id = hsh["key"].split("-").last @@ -60,8 +68,8 @@ def facet_by_region(arr) def facet_by_resource_type(arr) arr.map do |hsh| - { "id" => hsh["key"], - "title" => hsh["key"].underscore.humanize, + { "id" => hsh["key"].underscore.dasherize, + "title" => hsh["key"], "count" => hsh["doc_count"] } end end diff --git a/app/controllers/data_centers_controller.rb b/app/controllers/data_centers_controller.rb index 684c6d846..c30780b02 100644 --- a/app/controllers/data_centers_controller.rb +++ b/app/controllers/data_centers_controller.rb @@ -59,6 +59,7 @@ def index }.compact options[:include] = @include options[:is_collection] = true + options[:links] = nil render json: DataCenterSerializer.new(@clients, options).serialized_json, status: :ok end @@ -75,8 +76,14 @@ def show def set_include if params[:include].present? - @include = params[:include].split(",").map { |i| i.downcase.underscore.to_sym } - @include = @include & [:provider, :repository] + include_keys = { + "member" => :provider + } + @include = params[:include].split(",").reduce([]) do |sum, i| + k = include_keys[i.downcase.underscore] + sum << k if k.present? + sum + end else @include = [] end diff --git a/app/controllers/dois_controller.rb b/app/controllers/dois_controller.rb index a739f90dd..3d4a916fd 100644 --- a/app/controllers/dois_controller.rb +++ b/app/controllers/dois_controller.rb @@ -1,6 +1,10 @@ require 'uri' +require 'base64' class DoisController < ApplicationController + include ActionController::MimeResponds + include Crosscitable + prepend_before_action :authenticate_user! before_action :set_doi, only: [:show, :destroy, :get_url] before_action :set_include, only: [:index, :show, :create, :update] @@ -80,17 +84,19 @@ def index render json: DoiSerializer.new(@dois, options).serialized_json, status: :ok else - sort = case params[:sort] when "name" then { "doi" => { order: 'asc' }} when "-name" then { "doi" => { order: 'desc' }} when "created" then { created: { order: 'asc' }} when "-created" then { created: { order: 'desc' }} + when "updated" then { updated: { order: 'asc' }} + when "-updated" then { updated: { order: 'desc' }} when "relevance" then { "_score": { "order": "desc" }} else { updated: { order: 'desc' }} end page = params[:page] || {} + if page[:size].present? page[:size] = [page[:size].to_i, 1000].min max_number = page[:size] > 0 ? 10000/page[:size] : 1 @@ -107,7 +113,6 @@ def index else response = Doi.query(params[:query], state: params[:state], - year: params[:year], created: params[:created], registered: params[:registered], provider_id: params[:provider_id], @@ -115,9 +120,15 @@ def index prefix: params[:prefix], person_id: params[:person_id], resource_type_id: params[:resource_type_id], - fields: params[:fields], + query_fields: params[:query_fields], schema_version: params[:schema_version], link_check_status: params[:link_check_status], + link_check_has_schema_org: params[:link_check_has_schema_org], + link_check_body_has_pid: params[:link_check_body_has_pid], + link_check_found_schema_org_id: params[:link_check_found_schema_org_id], + link_check_found_dc_identifier: params[:link_check_found_dc_identifier], + link_check_found_citation_doi: params[:link_check_found_citation_doi], + link_check_redirect_count_gte: params[:link_check_redirect_count_gte], source: params[:source], page: page, sort: sort) @@ -128,7 +139,6 @@ def index states = total > 0 ? facet_by_key(response.response.aggregations.states.buckets) : nil resource_types = total > 0 ? facet_by_resource_type(response.response.aggregations.resource_types.buckets) : nil - years = total > 0 ? facet_by_year(response.response.aggregations.years.buckets) : nil created = total > 0 ? facet_by_year(response.response.aggregations.created.buckets) : nil registered = total > 0 ? facet_by_year(response.response.aggregations.registered.buckets) : nil providers = total > 0 ? facet_by_provider(response.response.aggregations.providers.buckets) : nil @@ -136,75 +146,96 @@ def index prefixes = total > 0 ? facet_by_key(response.response.aggregations.prefixes.buckets) : nil schema_versions = total > 0 ? facet_by_schema(response.response.aggregations.schema_versions.buckets) : nil sources = total > 0 ? facet_by_key(response.response.aggregations.sources.buckets) : nil - link_checks = total > 0 ? facet_by_cumuative_year(response.response.aggregations.link_checks.buckets) : nil - - @dois = response.results.results - - options = {} - options[:meta] = { - total: total, - "total-pages" => total_pages, - page: page[:number], - states: states, - "resource-types" => resource_types, - years: years, - created: created, - registered: registered, - providers: providers, - clients: clients, - prefixes: prefixes, - "schema-versions" => schema_versions, - sources: sources, - "link-checks" => link_checks - }.compact - - options[:links] = { - self: request.original_url, - next: @dois.blank? ? nil : request.base_url + "/dois?" + { - query: params[:query], - "provider-id" => params[:provider_id], - "client-id" => params[:client_id], - year: params[:year], - fields: params[:fields], - "page[cursor]" => Array.wrap(@dois.last[:sort]).first, - "page[size]" => params.dig(:page, :size) }.compact.to_query - }.compact - options[:include] = @include - options[:is_collection] = true - options[:params] = { - :current_ability => current_ability, - } - - render json: DoiSerializer.new(@dois, options).serialized_json, status: :ok + link_checks_status = total > 0 ? facet_by_cumulative_year(response.response.aggregations.link_checks_status.buckets) : nil + links_with_schema_org = total > 0 ? facet_by_cumulative_year(response.response.aggregations.link_checks_has_schema_org.buckets) : nil + link_checks_schema_org_id = total > 0 ? response.response.aggregations.link_checks_schema_org_id.value : nil + link_checks_dc_identifier = total > 0 ? response.response.aggregations.link_checks_dc_identifier.value : nil + link_checks_citation_doi = total > 0 ? response.response.aggregations.link_checks_citation_doi.value : nil + links_checked = total > 0 ? response.response.aggregations.links_checked.value : nil + + respond_to do |format| + format.json do + @dois = response.results.results + options = {} + options[:meta] = { + total: total, + "total-pages" => total_pages, + page: page[:number], + states: states, + "resource-types" => resource_types, + created: created, + registered: registered, + providers: providers, + clients: clients, + prefixes: prefixes, + "schema-versions" => schema_versions, + sources: sources, + "link-checks-status" => link_checks_status, + "links-checked" => links_checked, + "links-with-schema-org" => links_with_schema_org, + "link-checks-schema-org-id" => link_checks_schema_org_id, + "link-checks-dc-identifier" => link_checks_dc_identifier, + "link-checks-citation-doi" => link_checks_citation_doi + }.compact + + options[:links] = { + self: request.original_url, + next: @dois.blank? ? nil : request.base_url + "/dois?" + { + query: params[:query], + "provider-id" => params[:provider_id], + "client-id" => params[:client_id], + fields: params[:fields], + "page[cursor]" => Array.wrap(@dois.last[:sort]).first, + "page[size]" => params.dig(:page, :size) }.compact.to_query + }.compact + options[:include] = @include + options[:is_collection] = true + options[:params] = { + :current_ability => current_ability, + } + + render json: DoiSerializer.new(@dois, options).serialized_json, status: :ok + end + format.citation do + # fetch formatted citations + render citation: response.records.to_a, style: params[:style] || "apa", locale: params[:locale] || "en-US" + end + format.any(:bibtex, :citeproc, :codemeta, :crosscite, :datacite, :datacite_json, :jats, :ris, :schema_org) { render request.format.to_sym => response.records.to_a } + end end end def show authorize! :read, @doi - options = {} - options[:include] = @include - options[:is_collection] = false - options[:params] = { - :current_ability => current_ability, - } - - render json: DoiSerializer.new(@doi, options).serialized_json, status: :ok + respond_to do |format| + format.json do + options = {} + options[:include] = @include + options[:is_collection] = false + options[:params] = { + current_ability: current_ability, + detail: true + } + + render json: DoiSerializer.new(@doi, options).serialized_json, status: :ok + end + format.citation do + # fetch formatted citation + render citation: @doi, style: params[:style] || "apa", locale: params[:locale] || "en-US" + end + format.any(:bibtex, :citeproc, :codemeta, :crosscite, :datacite, :datacite_json, :jats, :ris, :schema_org) { render request.format.to_sym => @doi } + end end def validate logger = Logger.new(STDOUT) # logger.info safe_params.inspect - @doi = Doi.new(safe_params) - authorize! :create, @doi - - if @doi.errors.present? - logger.info @doi.errors.inspect - render json: serialize(@doi.errors), status: :ok - elsif @doi.validation_errors? - logger.info @doi.validation_errors.inspect - render json: serialize(@doi.validation_errors), status: :ok - else + @doi = Doi.new(safe_params.merge(only_validate: true)) + + authorize! :validate, @doi + + if @doi.valid? options = {} options[:include] = @include options[:is_collection] = false @@ -213,27 +244,30 @@ def validate } render json: DoiSerializer.new(@doi, options).serialized_json, status: :ok + else + logger.info @doi.errors.messages + render json: serialize(@doi.errors.messages), status: :ok end end def create logger = Logger.new(STDOUT) # logger.info safe_params.inspect + @doi = Doi.new(safe_params) - authorize! :create, @doi # capture username and password for reuse in the handle system @doi.current_user = current_user - if safe_params[:xml] && safe_params[:event] && @doi.validation_errors? - logger.error @doi.validation_errors.inspect - render json: serialize(@doi.validation_errors), status: :unprocessable_entity - elsif @doi.save + authorize! :new, @doi + + if @doi.save options = {} options[:include] = @include options[:is_collection] = false options[:params] = { - :current_ability => current_ability, + current_ability: current_ability, + detail: true } render json: DoiSerializer.new(@doi, options).serialized_json, status: :created, location: @doi @@ -246,44 +280,47 @@ def create def update logger = Logger.new(STDOUT) # logger.info safe_params.inspect + @doi = Doi.where(doi: params[:id]).first exists = @doi.present? if exists - if params[:data][:attributes][:mode] == "transfer" + # capture username and password for reuse in the handle system + @doi.current_user = current_user + + if params.dig(:data, :attributes, :mode) == "transfer" + # only update client_id + authorize! :transfer, @doi + @doi.assign_attributes(safe_params.slice(:client_id)) else authorize! :update, @doi + @doi.assign_attributes(safe_params.except(:doi, :client_id)) end - - @doi.assign_attributes(safe_params.except(:doi)) else doi_id = validate_doi(params[:id]) fail ActiveRecord::RecordNotFound unless doi_id.present? @doi = Doi.new(safe_params.merge(doi: doi_id)) + # capture username and password for reuse in the handle system + @doi.current_user = current_user - authorize! :create, @doi + authorize! :new, @doi end - # capture username and password for reuse in the handle system - @doi.current_user = current_user - - if safe_params[:xml] && (safe_params[:event] || safe_params[:validate]) && @doi.validation_errors? - logger.error @doi.validation_errors.inspect - render json: serialize(@doi.validation_errors), status: :unprocessable_entity - elsif @doi.save + if @doi.save options = {} options[:include] = @include options[:is_collection] = false options[:params] = { - :current_ability => current_ability, + current_ability: current_ability, + detail: true } render json: DoiSerializer.new(@doi, options).serialized_json, status: exists ? :ok : :created else - logger.warn @doi.errors.inspect - render json: serialize(@doi.errors), include: @include, status: :unprocessable_entity + logger.warn @doi.errors.messages + render json: serialize(@doi.errors.messages), include: @include, status: :unprocessable_entity end end @@ -409,94 +446,139 @@ def set_include private def safe_params + logger = Logger.new(STDOUT) + fail JSON::ParserError, "You need to provide a payload following the JSONAPI spec" unless params[:data].present? + # default values for attributes stored as JSON + defaults = { data: { titles: [], descriptions: [], types: {}, dates: [], rightsList: [], creators: [], contributors: [] }} + attributes = [ :doi, - "confirm-doi", - :identifier, - :url, :title, + :confirmDoi, + :url, + :titles, + { titles: [:title, :titleType, :lang] }, :publisher, - :published, + :publicationYear, :created, :prefix, :suffix, - "resource-type-subtype", - "last-landing-page", - "last-landing-page-status", - "last-landing-page-status-check", + :types, + { types: [:resourceTypeGeneral, :resourceType, :schemaOrg, :bibtex, :citeproc, :ris] }, + :dates, + { dates: [:date, :dateType, :dateInformation] }, + :landingPage, { - "last-landing-page-status-result" => [ - "error", - "redirect-count", - { "redirect-urls" => [] }, - "download-latency", - "has-schema-org", - "schema-org-id", - { "schema-org-id" => ["@type", "value", "propertyID"] }, - "dc-identifier", - "citation-doi", - "body-has-pid" + landingPage: [ + :checked, + :url, + :status, + :contentType, + :error, + :redirectCount, + { redirectUrls: [] }, + :downloadLatency, + :hasSchemaOrg, + :schemaOrgId, + { schemaOrgId: ["@type", :value, :propertyID] }, + :dcIdentifier, + :citationDoi, + :bodyHasPid ] }, - "last-landing-page-content-type", - "content-url", - "content-size", - "content-format", - :description, - :license, + :contentUrl, + :size, + :format, + :descriptions, + { descriptions: [:description, :descriptionType, :lang] }, + :rightsList, + { rightsList: [:rights, :rightsUri] }, :xml, - :validate, + :regenerate, :source, :version, - "metadata-version", - "schema-version", - :state, "is-active", + :metadataVersion, + :schemaVersion, + :state, + :isActive, :reason, :registered, :updated, :mode, :event, :regenerate, + :should_validate, :client, - "resource_type", - author: [:type, :id, :name, "given-name", "family-name", "givenName", "familyName"] + :creators, + { creators: [:type, :id, :name, :givenName, :familyName, :affiliation] }, + :contributors, + { contributors: [:type, :id, :name, :givenName, :familyName, :affiliation, :contributorType] }, + :identifiers, + { identifiers: [:identifier, :identifierType] }, + :relatedIdentifiers, + { relatedIdentifiers: [:relatedIdentifier, :relatedIdentifierType, :relationType, :resourceTypeGeneral, :relatedMetadataScheme, :schemeUri, :schemeType] }, + :fundingReferences, + { fundingReferences: [:funderName, :funderIdentifier, :funderIdentifierType, :awardNumber, :awardUri, :awardTitle] }, + :geoLocations, + { geoLocations: [{ geolocationPoint: [:pointLongitude, :pointLatitude] }, { geolocationBox: [:westBoundLongitude, :eastBoundLongitude, :southBoundLatitude, :northBoundLatitude] }, :geoLocationPlace] } ] + relationships = [{ client: [data: [:type, :id]] }] + + p = params.require(:data).permit(:type, :id, attributes: attributes, relationships: relationships).reverse_merge(defaults) + client_id = p.dig("relationships", "client", "data", "id") || current_user.try(:client_id) + p = p.fetch("attributes").merge(client_id: client_id) + + # extract attributes from xml field and merge with attributes provided directly + xml = p[:xml].present? ? Base64.decode64(p[:xml]).force_encoding("UTF-8") : nil + + meta = xml.present? ? parse_xml(xml, doi: p[:doi]) : {} + xml = meta["string"] + + read_attrs = [p[:creators], p[:contributors], p[:titles], p[:publisher], + p[:publicationYear], p[:types], p[:descriptions], p[:container], p[:sizes], + p[:formats], p[:version], p[:language], p[:dates], p[:identifiers], + p[:relatedIdentifiers], p[:fundingReferences], p[:geoLocations], p[:rightsList], + p[:subjects], p[:contentUrl], p[:schemaVersion]].compact + + # replace DOI, but otherwise don't touch the XML + # use Array.wrap(read_attrs.first) as read_attrs may also be [[]] + if meta["from"] == "datacite" && Array.wrap(read_attrs.first).blank? + xml = replace_doi(xml, doi: p[:doi] || meta["doi"]) + elsif xml.present? || Array.wrap(read_attrs.first).present? + regenerate = true + end - relationships = [ - { client: [data: [:type, :id]] }, - { provider: [data: [:type, :id]] }, - { "resource-type" => [:data, data: [:type, :id]] } - ] - - p = params.require(:data).permit(:type, :id, attributes: attributes, relationships: relationships) - p = p.fetch("attributes").merge(client_id: p.dig("relationships", "client", "data", "id"), resource_type_general: camelize_str(p.dig("relationships", "resource-type", "data", "id"))) - p.merge( - additional_type: p["resource-type-subtype"], - schema_version: p["schema-version"], - last_landing_page: p["last-landing-page"], - last_landing_page_status: p["last-landing-page-status"], - last_landing_page_status_check: p["last-landing-page-status-check"], - last_landing_page_status_result: p["last-landing-page-status-result"], - last_landing_page_content_type: p["last-landing-page-content-type"] - ).except( - "confirm-doi", :identifier, :prefix, :suffix, "resource-type-subtype", - "metadata-version", "schema-version", :state, :mode, "is-active", - :created, :registered, :updated, "last-landing-page", - "last-landing-page-status", "last-landing-page-status-check", - "last-landing-page-status-result", "last-landing-page-content-type") - end - - def underscore_str(str) - return str unless str.present? + p.merge!(xml: xml) if xml.present? - str.underscore - end + read_attrs_keys = [:creators, :contributors, :titles, :publisher, + :publicationYear, :types, :descriptions, :container, :sizes, + :formats, :language, :dates, :identifiers, + :relatedIdentifiers, :fundingReferences, :geoLocations, :rightsList, + :subjects, :contentUrl, :schemaVersion] - def camelize_str(str) - return str unless str.present? + # merge attributes from xml into regular attributes + # make sure we don't accidentally set any attributes to nil + read_attrs_keys.each do |attr| + p.merge!(attr.to_s.underscore => p[attr].presence || meta[attr.to_s.underscore]) if p.has_key?(attr) || meta[attr.to_s.underscore].present? + end + p.merge!(version_info: p[:version] || meta["version_info"]) if p.has_key?(:version_info) || meta["version_info"].present? - str.underscore.camelize + p.merge( + regenerate: p[:regenerate] || regenerate, + landing_page: p[:landingPage], + last_landing_page: p[:lastLandingPage], + last_landing_page_status: p[:lastLandingPageStatus], + last_landing_page_status_check: p[:lastLandingPageStatusCheck], + last_landing_page_status_result: p[:lastLandingPageStatusResult], + last_landing_page_content_type: p[:lastLandingPageContentType] + ).except( + :confirmDoi, :prefix, :suffix, :publicationYear, + :rightsList, :identifiers, :relatedIdentifiers, :fundingReferences, :geoLocations, + :metadataVersion, :schemaVersion, :state, :mode, :isActive, :landingPage, + :created, :registered, :updated, :lastLandingPage, :version, + :lastLandingPageStatus, :lastLandingPageStatusCheck, + :lastLandingPageStatusResult, :lastLandingPageContentType) end def add_metadata_to_bugsnag(report) diff --git a/app/controllers/index_controller.rb b/app/controllers/index_controller.rb index 57175884a..1417139d6 100644 --- a/app/controllers/index_controller.rb +++ b/app/controllers/index_controller.rb @@ -5,34 +5,10 @@ class IndexController < ApplicationController before_action :set_doi, only: [:show] def index - authorize! :index, :Index render plain: ENV['SITE_TITLE'] end - # we support content negotiation in show action - def show - authorize! :show, @doi - - respond_to do |format| - format.citation do - # fetch formatted citation - @doi.style = params[:style] || "apa" - @doi.locale = params[:locale] || "en-US" - render citation: @doi - end - format.any(:bibtex, :citeproc, :codemeta, :crosscite, :datacite, :datacite_json, :jats, :ris, :schema_org) { render request.format.to_sym => @doi } - format.any { fail ActionController::UnknownFormat } - end - end - # def routing_error # fail ActionController::RoutingError # end - - protected - - def set_doi - @doi = Doi.where(doi: params[:id]).first - fail ActiveRecord::RecordNotFound unless @doi.present? - end end diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index b7fa3464c..957393af6 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -125,8 +125,8 @@ def set_include def safe_params fail JSON::ParserError, "You need to provide a payload following the JSONAPI spec" unless params[:data].present? ActiveModelSerializers::Deserialization.jsonapi_parse!( - params, only: ["media-type", :url], - keys: { "media-type" => :media_type } + params, only: ["mediaType", :url], + keys: { "mediaType" => :media_type } ) end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 1457fb376..6b26f4abe 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -64,6 +64,7 @@ def index }.compact options[:include] = @include options[:is_collection] = true + options[:links] = nil render json: MemberSerializer.new(@members, options).serialized_json, status: :ok end diff --git a/app/controllers/providers_controller.rb b/app/controllers/providers_controller.rb index 7040beeab..3f60e76ed 100644 --- a/app/controllers/providers_controller.rb +++ b/app/controllers/providers_controller.rb @@ -30,7 +30,7 @@ def index elsif params[:ids].present? response = Provider.find_by_ids(params[:ids], page: page, sort: sort) else - response = Provider.query(params[:query], year: params[:year], region: params[:region], organization_type: params[:organization_type], focus_area: params[:focus_area], fields: params[:fields], page: page, sort: sort) + response = Provider.query(params[:query], year: params[:year], region: params[:region], organization_type: params[:organization_type], focus_area: params[:focus_area], query_fields: params[:query_fields], page: page, sort: sort) end total = response.results.total @@ -151,8 +151,8 @@ def set_provider def safe_params fail JSON::ParserError, "You need to provide a payload following the JSONAPI spec" unless params[:data].present? ActiveModelSerializers::Deserialization.jsonapi_parse!( - params, only: [:name, :symbol, :description, :website, :joined, "organization-type", "focus-area", :phone, "contact-name", "contact-email", "is_active", "password-input", :country], - keys: { "organization-type" => :organization_type, "focus-area" => :focus_area, "contact-name" => :contact_name, "contact-email" => :contact_email, :country => :country_code, "is-active" => :is_active, "password-input" => :password_input } + params, only: [:name, :symbol, :description, :website, :joined, "organizationType", "focusArea", :phone, "contactName", "contactEmail", "isActive", "passwordInput", :country], + keys: { "organizationType" => :organization_type, "focusArea" => :focus_area, "contactName" => :contact_name, "contactEmail" => :contact_email, :country => :country_code, "isActive" => :is_active, "passwordInput" => :password_input } ) end end diff --git a/app/controllers/works_controller.rb b/app/controllers/works_controller.rb new file mode 100644 index 000000000..568894575 --- /dev/null +++ b/app/controllers/works_controller.rb @@ -0,0 +1,136 @@ +class WorksController < ApplicationController + prepend_before_action :authenticate_user! + before_action :set_doi, only: [:show] + before_action :set_include, only: [:index, :show] + before_bugsnag_notify :add_metadata_to_bugsnag + + def index + authorize! :read, Doi + + sort = case params[:sort] + when "name" then { "doi" => { order: 'asc' }} + when "-name" then { "doi" => { order: 'desc' }} + when "created" then { created: { order: 'asc' }} + when "-created" then { created: { order: 'desc' }} + when "updated" then { updated: { order: 'asc' }} + when "-updated" then { updated: { order: 'desc' }} + when "relevance" then { "_score": { "order": "desc" }} + else { updated: { order: 'desc' }} + end + + page = params[:page] || {} + if page[:size].present? + page[:size] = [page[:size].to_i, 1000].min + max_number = page[:size] > 0 ? 10000/page[:size] : 1 + else + page[:size] = 25 + max_number = 10000/page[:size] + end + page[:number] = page[:number].to_i > 0 ? [page[:number].to_i, max_number].min : 1 + + if params[:id].present? + response = Doi.find_by_id(params[:id]) + elsif params[:ids].present? + response = Doi.find_by_ids(params[:ids], page: page, sort: sort) + else + response = Doi.query(params[:query], + state: "findable", + created: params[:created], + registered: params[:registered], + provider_id: params[:member_id], + client_id: params[:data_center_id], + prefix: params[:prefix], + person_id: params[:person_id], + resource_type_id: params[:resource_type_id], + schema_version: params[:schema_version], + page: page, + sort: sort) + end + + total = response.results.total + total_pages = page[:size] > 0 ? ([total.to_f, 10000].min / page[:size]).ceil : 0 + + resource_types = total > 0 ? facet_by_resource_type(response.response.aggregations.resource_types.buckets) : nil + registered = total > 0 ? facet_by_year(response.response.aggregations.registered.buckets) : nil + providers = total > 0 ? facet_by_provider(response.response.aggregations.providers.buckets) : nil + clients = total > 0 ? facet_by_client(response.response.aggregations.clients.buckets) : nil + + @dois = response.results.results + + options = {} + options[:meta] = { + "resource-types" => resource_types, + registered: registered, + "data-centers" => clients, + total: total, + "total-pages" => total_pages, + page: page[:number] + }.compact + + options[:links] = { + self: request.original_url, + next: @dois.blank? ? nil : request.base_url + "/dois?" + { + query: params[:query], + "member-id" => params[:provider_id], + "data-center-id" => params[:client_id], + "page[size]" => params.dig(:page, :size) }.compact.to_query + }.compact + options[:include] = @include + options[:is_collection] = true + options[:links] = nil + options[:params] = { + :current_ability => current_ability, + } + + render json: WorkSerializer.new(@dois, options).serialized_json, status: :ok + end + + def show + authorize! :read, @doi + + options = {} + options[:include] = @include + options[:is_collection] = false + options[:params] = { + current_ability: current_ability, + detail: true + } + + render json: WorkSerializer.new(@doi, options).serialized_json, status: :ok + end + + protected + + def set_doi + @doi = Doi.where(doi: params[:id]).first + fail ActiveRecord::RecordNotFound unless @doi.present? + + # capture username and password for reuse in the handle system + @doi.current_user = current_user + end + + def set_include + if params[:include].present? + include_keys = { + "data_center" => :client, + "member" => :provider, + "resource_type" => :resource_type + } + @include = params[:include].split(",").reduce([]) do |sum, i| + k = include_keys[i.downcase.underscore] + sum << k if k.present? + sum + end + else + @include = nil + end + end + + def add_metadata_to_bugsnag(report) + return nil unless params.dig(:data, :attributes, :xml).present? + + report.add_tab(:metadata, { + metadata: Base64.decode64(params.dig(:data, :attributes, :xml)) + }) + end +end diff --git a/app/jobs/doi_import_by_day_job.rb b/app/jobs/doi_import_by_day_job.rb new file mode 100644 index 000000000..43c938d2d --- /dev/null +++ b/app/jobs/doi_import_by_day_job.rb @@ -0,0 +1,7 @@ +class DoiImportByDayJob < ActiveJob::Base + queue_as :lupo_background + + def perform(options={}) + Doi.import_by_day(options) + end +end \ No newline at end of file diff --git a/app/jobs/index_job.rb b/app/jobs/index_job.rb index 8d24b7f7c..5687eba52 100644 --- a/app/jobs/index_job.rb +++ b/app/jobs/index_job.rb @@ -1,6 +1,11 @@ class IndexJob < ActiveJob::Base queue_as :lupo + rescue_from ActiveJob::DeserializationError do |error| + logger = Logger.new(STDOUT) + logger.error error.message + end + def perform(obj) obj.__elasticsearch__.index_document end diff --git a/app/models/ability.rb b/app/models/ability.rb index fbbccc6a8..012f1ac0f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -7,49 +7,44 @@ def initialize(user) alias_action :create, :read, :update, :destroy, to: :crud user ||= User.new(nil) # Guest user - @user = user + # @user = user if user.role_id == "staff_admin" can :manage, :all - # can :manage, [Provider, ProviderPrefix, Client, ClientPrefix, Prefix, Phrase, User] - # can [:read, :transfer, :set_state, :set_minted, :set_url, :delete_test_dois], Doi cannot [:new, :create], Doi do |doi| doi.client.blank? || !doi.client.prefixes.where(prefix: doi.prefix).first end - can [:read_landing_page_results], Doi elsif user.role_id == "staff_user" can :read, :all - can [:read_landing_page_results], Doi elsif user.role_id == "provider_admin" && user.provider_id.present? can [:update, :read], Provider, :symbol => user.provider_id.upcase can [:manage], ProviderPrefix, :provider_id => user.provider_id can [:manage], Client,:provider_id => user.provider_id - can [:manage], ClientPrefix#, :client_id => user.provider_id + can [:manage], ClientPrefix #, :client_id => user.provider_id # if Flipper[:delete_doi].enabled?(user) # can [:manage], Doi, :provider_id => user.provider_id # else # can [:read, :update], Doi, :provider_id => user.provider_id # end - can [:read, :transfer], Doi, :provider_id => user.provider_id + + can [:read, :transfer, :read_landing_page_results], Doi, :provider_id => user.provider_id can [:read], Doi do |doi| doi.findable? end can [:read], User can [:read], Phrase - can [:read_landing_page_results], Doi, :provider_id => user.provider_id elsif user.role_id == "provider_user" && user.provider_id.present? can [:read], Provider, :symbol => user.provider_id.upcase can [:read], ProviderPrefix, :provider_id => user.provider_id can [:read], Client, :provider_id => user.provider_id can [:read], ClientPrefix#, :client_id => user.client_id - can [:read], Doi, :provider_id => user.provider_id + can [:read, :read_landing_page_results], Doi, :provider_id => user.provider_id can [:read], Doi do |doi| doi.findable? end can [:read], User can [:read], Phrase - can [:read_landing_page_results], Doi, :provider_id => user.provider_id elsif user.role_id == "client_admin" && user.client_id.present? can [:read, :update], Client, :symbol => user.client_id.upcase can [:read], ClientPrefix, :client_id => user.client_id @@ -59,26 +54,25 @@ def initialize(user) # else # can [:read, :update], Doi, :client_id => user.client_id # end - can [:read, :update, :destroy, :register_url, :get_url, :get_urls], Doi, :client_id => user.client_id - can [:new, :create], Doi do |doi| - doi.client_id == user.client_id && doi.client.prefixes.where(prefix: doi.prefix).first + + can [:read, :destroy, :update, :register_url, :validate, :get_url, :get_urls, :read_landing_page_results], Doi, :client_id => user.client_id + can [:new, :create], Doi do |doi| + doi.client.prefixes.where(prefix: doi.prefix).present? end can [:read], Doi do |doi| doi.findable? end can [:read], User can [:read], Phrase - can [:read_landing_page_results], Doi, :client_id => user.client_id elsif user.role_id == "client_user" && user.client_id.present? can [:read], Client, :symbol => user.client_id.upcase can [:read], ClientPrefix, :client_id => user.client_id - can [:read], Doi, :client_id => user.client_id + can [:read, :read_landing_page_results], Doi, :client_id => user.client_id can [:read], Doi do |doi| doi.findable? end can [:read], User can [:read], Phrase - can [:read_landing_page_results], Doi, :client_id => user.client_id elsif user.role_id == "user" can [:read, :update], Provider, :symbol => user.provider_id.upcase if user.provider_id.present? can [:read, :update], Client, :symbol => user.client_id.upcase if user.client_id.present? diff --git a/app/models/client.rb b/app/models/client.rb index 774d533e3..8334462af 100644 --- a/app/models/client.rb +++ b/app/models/client.rb @@ -62,7 +62,12 @@ class Client < ActiveRecord::Base analyzer: { string_lowercase: { tokenizer: 'keyword', filter: %w(lowercase ascii_folding) } }, - filter: { ascii_folding: { type: 'asciifolding', preserve_original: true } } + normalizer: { + keyword_lowercase: { type: "custom", filter: %w(lowercase) } + }, + filter: { + ascii_folding: { type: 'asciifolding', preserve_original: true } + } } } do mapping dynamic: 'false' do @@ -71,7 +76,8 @@ class Client < ActiveRecord::Base indexes :provider_id, type: :keyword indexes :repository_id, type: :keyword indexes :prefix_ids, type: :keyword - indexes :name, type: :text, fields: { keyword: { type: "keyword" }, raw: { type: "text", "analyzer": "string_lowercase", "fielddata": true }} + indexes :name, type: :text, fields: { keyword: { type: "keyword" }, raw: { type: "text", analyzer: "string_lowercase", "fielddata": true }} + indexes :description, type: :text indexes :contact_name, type: :text indexes :contact_email, type: :text, fields: { keyword: { type: "keyword" }} indexes :re3data, type: :keyword @@ -80,6 +86,7 @@ class Client < ActiveRecord::Base indexes :domains, type: :text indexes :year, type: :integer indexes :url, type: :text, fields: { keyword: { type: "keyword" }} + indexes :software, type: :text, fields: { keyword: { type: "keyword" }, raw: { type: "text", analyzer: "string_lowercase", "fielddata": true }} indexes :cache_key, type: :keyword indexes :created, type: :date indexes :updated, type: :date @@ -100,12 +107,14 @@ def as_indexed_json(options={}) "repository_id" => repository_id, "prefix_ids" => prefix_ids, "name" => name, + "description" => description, "symbol" => symbol, "year" => year, "contact_name" => contact_name, "contact_email" => contact_email, "domains" => domains, "url" => url, + "software" => software, "is_active" => is_active, "password" => password, "cache_key" => cache_key, @@ -119,14 +128,15 @@ def as_indexed_json(options={}) end def self.query_fields - ['symbol^10', 'name^10', 'contact_name^10', 'contact_email^10', 'domains', 'url', 'repository.software.name^3', 'repository.subjects.text^3', 'repository.certificates.text^3', '_all'] + ['symbol^10', 'name^10', 'description^10', 'contact_name^10', 'contact_email^10', 'domains', 'url', 'software^3', 'repository.subjects.text^3', 'repository.certificates.text^3', '_all'] end def self.query_aggregations { years: { date_histogram: { field: 'created', interval: 'year', min_doc_count: 1 } }, cumulative_years: { terms: { field: 'cumulative_years', min_doc_count: 1, order: { _count: "asc" } } }, - providers: { terms: { field: 'provider_id', size: 15, min_doc_count: 1 } } + providers: { terms: { field: 'provider_id', size: 15, min_doc_count: 1 } }, + software: { terms: { field: 'software.keyword', size: 15, min_doc_count: 1 } } } end @@ -247,7 +257,7 @@ def to_jsonapi "domains" => domains, "provider-id" => provider_id, "prefixes" => prefixes.map { |p| p.prefix }, - "is-active" => is_active == "\x01", + "is-active" => is_active.getbyte(0) == 1, "version" => version, "created" => created.iso8601, "updated" => updated.iso8601, diff --git a/app/models/concerns/cacheable.rb b/app/models/concerns/cacheable.rb index e55ed0a28..52e3dd80a 100644 --- a/app/models/concerns/cacheable.rb +++ b/app/models/concerns/cacheable.rb @@ -50,42 +50,8 @@ def cached_media_count(options={}) end end - def fetch_cached_meta - if updated.present? - Rails.cache.fetch("cached_meta/#{doi}-#{updated.iso8601}") do - if from.present? && string.present? - send("read_" + from, string: string, sandbox: sandbox) - else - read_datacite(string: fetch_cached_xml, sandbox: sandbox) - end - end - else - if from.present? && string.present? - send("read_" + from, string: string, sandbox: sandbox) - else - read_datacite(string: xml, sandbox: sandbox) - end - end - rescue ArgumentError, NoMethodError => e - logger = Logger.new(STDOUT) - logger.error "Error for " + doi + ": " + e.message - return {} - end - - def fetch_cached_xml - if updated.present? - Rails.cache.fetch("cached_xml/#{doi}-#{updated.iso8601}", raw: true) do - m = metadata.first - m.present? ? m.xml : nil - end - else - m = metadata.first - m.present? ? m.xml : nil - end - end - def fetch_cached_metadata_version - if updated.present? + if updated.present? && Rails.application.config.action_controller.perform_caching Rails.cache.fetch("cached_metadata_version/#{doi}-#{updated.iso8601}") do current_metadata ? current_metadata.metadata_version : 0 end @@ -95,19 +61,31 @@ def fetch_cached_metadata_version end def cached_client_response(id, options={}) - Rails.cache.fetch("client_response/#{id}", expires_in: 1.day) do + if Rails.application.config.action_controller.perform_caching + Rails.cache.fetch("client_response/#{id}", expires_in: 1.day) do + Client.where(symbol: id).first + end + else Client.where(symbol: id).first end end def cached_prefix_response(prefix, options={}) - Rails.cache.fetch("prefix_response/#{prefix}", expires_in: 7.days) do + if Rails.application.config.action_controller.perform_caching + Rails.cache.fetch("prefix_response/#{prefix}", expires_in: 7.days) do + Prefix.where(prefix: prefix).first + end + else Prefix.where(prefix: prefix).first end end def cached_provider_response(id, options={}) - Rails.cache.fetch("provider_response/#{id}", expires_in: 1.day) do + if Rails.application.config.action_controller.perform_caching + Rails.cache.fetch("provider_response/#{id}", expires_in: 1.day) do + Provider.where(symbol: id).first + end + else Provider.where(symbol: id).first end end diff --git a/app/models/concerns/checkable.rb b/app/models/concerns/checkable.rb index 74c5c9dff..35e3627ac 100644 --- a/app/models/concerns/checkable.rb +++ b/app/models/concerns/checkable.rb @@ -7,8 +7,8 @@ def get_landing_page_info(doi: nil, url: nil, keep: true) return { "status" => 404, "content-type" => nil, "checked" => Time.zone.now.utc.iso8601 } unless uri.present? - return { "status" => doi.last_landing_page_status, "content-type" => doi.last_landing_page_content_type, "checked" => doi.last_landing_page_status_check } if - doi.present? && keep && doi.last_landing_page_status_check.present? && doi.last_landing_page_status_check > (Time.zone.now - 7.days) + return { "status" => doi.landing_page['status'], "content-type" => doi.landing_page['content_type'], "checked" => doi.landing_page['checked'] } if + doi.present? && keep && doi.landing_page.present? && doi.landing_page['checked'].present? && doi.landing_page['checked'] > (Time.zone.now - 7.days) response = Maremma.head(uri, timeout: 5) if response.headers && response.headers["Content-Type"].present? diff --git a/app/models/concerns/crosscitable.rb b/app/models/concerns/crosscitable.rb index bdca5d828..aefc708b5 100644 --- a/app/models/concerns/crosscitable.rb +++ b/app/models/concerns/crosscitable.rb @@ -7,129 +7,84 @@ module Crosscitable included do include Bolognese::MetadataUtils - # track changes of virtual attributes + attr_accessor :issue, :volume, :style, :locale - def author=(value) - return @author if value.blank? || value == author - - attribute_will_change!(:author) - @author = value - end - - def title=(value) - return @title if value.nil? || value == title - - attribute_will_change!(:title) - @title = value - end - - def publisher=(value) - return @publisher if value.nil? || value == publisher - - attribute_will_change!(:publisher) - @publisher = value - end - - def date_published=(value) - return @date_published if value.nil? || value == date_published - - attribute_will_change!(:date_published) - @date_published = value - end - - def additional_type=(value) - return @additional_type if value.nil? || value == additional_type - - attribute_will_change!(:additional_type) - @additional_type = value - end - - def resource_type_general=(value) - return @resource_type_general if value.nil? || value == resource_type_general - - attribute_will_change!(:resource_type_general) - @resource_type_general = value + def sandbox + !Rails.env.production? end - def description=(value) - return @description if value.nil? || value == description - - attribute_will_change!(:description) - @description = value + def exists? + aasm_state != "not_found" end - def content_size=(value) - return @content_size if value.nil? || value == content_size - - attribute_will_change!(:content_size) - @content_size = value + def meta + @meta || {} end - def content_format=(value) - return @content_format if value.nil? || value == content_format - - attribute_will_change!(:content_format) - @content_format = value - end + def parse_xml(input, options={}) + return {} unless input.present? - # modified bolognese attributes + # check whether input is id and we need to fetch the content + id = normalize_id(input, sandbox: sandbox) - def sandbox - !Rails.env.production? - end + if id.present? + from = find_from_format(id: id) - # cache doi metadata - def meta - @meta ||= fetch_cached_meta - end + # generate name for method to call dynamically + hsh = from.present? ? send("get_" + from, id: id, sandbox: sandbox) : {} + input = hsh.fetch("string", nil) + else + from = find_from_format(string: input) + end - def exists? - meta.fetch("state", "not_found") != "not_found" - end + meta = from.present? ? send("read_" + from, { string: input, doi: options[:doi], sandbox: sandbox }).compact : {} + meta.merge("string" => input, "from" => from) + rescue NoMethodError, ArgumentError => exception + Bugsnag.notify(exception) + logger = Logger.new(STDOUT) + logger.error "Error " + exception.message + " for doi " + @doi + "." + logger.error exception - # default to DataCite schema 4 - def schema_version - @schema_version ||= meta.fetch("schema_version", nil) || "http://datacite.org/schema/kernel-4" + {} end - # cache xml - def xml - @xml || fetch_cached_xml + def replace_doi(input, options={}) + return input unless options[:doi].present? + + doc = Nokogiri::XML(input, nil, 'UTF-8', &:noblanks) + node = doc.at_css("identifier") + node.content = options[:doi].to_s.upcase if node.present? && options[:doi].present? + doc.to_xml.strip end - def xml=(value) - # check that input is well-formed if xml or json - input = well_formed_xml(value) - + def update_xml # check whether input is id and we need to fetch the content - id = normalize_id(input, sandbox: sandbox) + id = normalize_id(xml, sandbox: sandbox) if id.present? - @from = find_from_format(id: id) + from = find_from_format(id: id) # generate name for method to call dynamically - hsh = @from.present? ? send("get_" + @from, id: id, sandbox: sandbox) : {} - @string = hsh.fetch("string", nil) + hsh = from.present? ? send("get_" + from, id: id, sandbox: sandbox) : {} + xml = hsh.fetch("string", nil) else - @from = find_from_format(string: input) - @string = input + from = find_from_format(string: xml) end - attribute_will_change!(:xml) + # generate new xml if attributes have been set directly and/or from metadata that are not DataCite XML + read_attrs = %w(creators contributors titles publisher publication_year types descriptions container sizes formats version_info language dates identifiers related_identifiers funding_references geo_locations rights_list subjects content_url schema_version).map do |a| + [a.to_sym, send(a.to_s)] + end.to_h.compact - @meta = @from.present? ? send("read_" + @from, string: raw, sandbox: sandbox) : {} - @xml = (from == "datacite") ? raw : datacite_xml - rescue NoMethodError, ArgumentError => exception - Bugsnag.notify(exception) - logger = Logger.new(STDOUT) - logger.error "Error " + exception.message + " for doi " + doi + "." - @xml = nil + meta = from.present? ? send("read_" + from, { string: xml, doi: doi, sandbox: sandbox }.merge(read_attrs)) : {} + + xml = datacite_xml + + write_attribute(:xml, xml) end def well_formed_xml(string) - return '' unless string.present? - - string = Base64.decode64(string).force_encoding("UTF-8") + return nil unless string.present? from_xml(string) || from_json(string) @@ -139,20 +94,11 @@ def well_formed_xml(string) def from_xml(string) return nil unless string.start_with?(' e - line, column, level, text = e.message.split(":", 4) - message = text.strip + " at line #{line}, column #{column}" - errors.add(:xml, message) - - string + doc = Nokogiri::XML(string) { |config| config.strict.noblanks } + doc.to_xml.strip end def from_json(string) - return nil unless string.start_with?('[', '{') - linter = JsonLint::Linter.new errors_array = [] @@ -160,38 +106,20 @@ def from_json(string) valid &&= linter.send(:check_syntax_valid?, string, errors_array) valid &&= linter.send(:check_overlapping_keys?, string, errors_array) - errors_array.each { |e| errors.add(:xml, e.capitalize) } - errors_array.empty? ? nil : string - end + raise JSON::ParserError, errors_array.join("\n") if errors_array.present? - # validate against DataCite schema - def validation_errors - kernel = schema_version.split("/").last - filepath = Bundler.rubygems.find_name('bolognese').first.full_gem_path + "/resources/#{kernel}/metadata.xsd" - schema = Nokogiri::XML::Schema(open(filepath)) - - schema.validate(Nokogiri::XML(xml, nil, 'UTF-8')).reduce({}) do |sum, error| - location, level, source, text = error.message.split(": ", 4) - line, column = location.split(":", 2) - title = text.to_s.strip + " at line #{line}, column #{column}" if line.present? - source = source.split("}").last[0..-2] if line.present? - - errors.add(source.to_sym, title) + string + end - sum[source.to_sym] = Array(sum[source.to_sym]) + [title] + def get_content_type(string) + return "xml" if Nokogiri::XML(string).errors.empty? - sum + begin + JSON.parse(string) + return "json" + rescue + "string" end - rescue Nokogiri::XML::SyntaxError => e - line, column, level, text = e.message.split(":", 4) - message = text.strip + " at line #{line}, column #{column}" - errors.add(:xml, message) - - errors - end - - def validation_errors? - validation_errors.present? end end end diff --git a/app/models/concerns/dateable.rb b/app/models/concerns/dateable.rb index ce367ee0b..8a89b2f62 100644 --- a/app/models/concerns/dateable.rb +++ b/app/models/concerns/dateable.rb @@ -1,6 +1,26 @@ module Dateable extend ActiveSupport::Concern + included do + def get_date(dates, date_type) + dd = Array.wrap(dates).find { |d| d["dateType"] == date_type } || {} + dd.fetch("date", nil) + end + + def set_date(dates, date, date_type) + dd = Array.wrap(dates).find { |d| d["dateType"] == date_type } || { "dateType" => date_type } + dd["date"] = date + end + + def get_resource_type(types, type) + types[type] + end + + def set_resource_type(types, text, type) + types[type] = text + end + end + module ClassMethods def get_solr_date_range(from_date, until_date) from_date_string = get_datetime_from_input(from_date) || "*" diff --git a/app/models/concerns/indexable.rb b/app/models/concerns/indexable.rb index 495464e91..1ab3b95a3 100644 --- a/app/models/concerns/indexable.rb +++ b/app/models/concerns/indexable.rb @@ -8,11 +8,11 @@ module Indexable # use index_document instead of update_document to also update virtual attributes IndexJob.perform_later(self) if self.class.name == "Doi" - update_column(:indexed, Time.zone.now) + update_column(:indexed, Time.zone.now) send_import_message(self.to_jsonapi) if aasm_state == "findable" unless Rails.env.test? end end - + before_destroy do begin __elasticsearch__.delete_document @@ -29,7 +29,7 @@ def send_delete_message(data) def send_import_message(data) send_message(data, shoryuken_class: "DoiImportWorker") end - + # shoryuken_class is needed for the consumer to process the message # we use the AWS SQS client directly as there is no consumer in this app def send_message(body, options={}) @@ -85,7 +85,7 @@ def find_by_id_list(ids, options={}) def find_by_ids(ids, options={}) options[:sort] ||= { "_doc" => { order: 'asc' }} - + __elasticsearch__.search({ from: options[:page].present? ? (options.dig(:page, :number) - 1) * options.dig(:page, :size) : 0, size: options[:size] || 25, @@ -111,21 +111,27 @@ def query(query, options={}) sort = options[:sort] end - fields = options[:fields].presence || query_fields + fields = options[:query_fields].presence || query_fields must = [] must << { multi_match: { query: query, fields: fields, type: "phrase_prefix", slop: 3, max_expansions: 10 }} if query.present? must << { term: { aasm_state: options[:state] }} if options[:state].present? - must << { term: { resource_type_id: options[:resource_type_id] }} if options[:resource_type_id].present? + must << { term: { "types.resourceTypeGeneral": options[:resource_type_id].underscore.camelize }} if options[:resource_type_id].present? must << { terms: { provider_id: options[:provider_id].split(",") }} if options[:provider_id].present? must << { terms: { client_id: options[:client_id].split(",") }} if options[:client_id].present? must << { term: { prefix: options[:prefix] }} if options[:prefix].present? must << { term: { "author.id" => "https://orcid.org/#{options[:person_id]}" }} if options[:person_id].present? must << { range: { created: { gte: "#{options[:created].split(",").min}||/y", lte: "#{options[:created].split(",").max}||/y", format: "yyyy" }}} if options[:created].present? - must << { range: { registered: { gte: "#{options[:registered].split(",").min}||/y", lte: "#{options[:registered].split(",").max}||/y", format: "yyyy" }}} if options[:registered].present? must << { term: { schema_version: "http://datacite.org/schema/kernel-#{options[:schema_version]}" }} if options[:schema_version].present? must << { term: { source: options[:source] }} if options[:source].present? - must << { term: { last_landing_page_status: options[:link_check_status] }} if options[:link_check_status].present? + must << { term: { "landing_page.status": options[:link_check_status] }} if options[:link_check_status].present? + must << { exists: { field: "landing_page.checked" }} if options[:link_checked].present? + must << { term: { "landing_page.hasSchemaOrg": options[:link_check_has_schema_org] }} if options[:link_check_has_schema_org].present? + must << { term: { "landing_page.bodyHasPid": options[:link_check_body_has_pid] }} if options[:link_check_body_has_pid].present? + must << { exists: { field: "landing_page.schemaOrgId" }} if options[:link_check_found_schema_org_id].present? + must << { exists: { field: "landing_page.dcIdentifier" }} if options[:link_check_found_dc_identifier].present? + must << { exists: { field: "landing_page.citationDoi" }} if options[:link_check_found_citation_doi].present? + must << { range: { "landing_page.redirectCount": { "gte": options[:link_check_redirect_count_gte] } } } if options[:link_check_redirect_count_gte].present? must_not = [] @@ -139,9 +145,10 @@ def query(query, options={}) must_not << { exists: { field: "deleted_at" }} unless options[:include_deleted] elsif self.name == "Client" must << { range: { created: { gte: "#{options[:year].split(",").min}||/y", lte: "#{options[:year].split(",").max}||/y", format: "yyyy" }}} if options[:year].present? + must << { terms: { "software.raw" => options[:software].split(",") }} if options[:software].present? must_not << { exists: { field: "deleted_at" }} unless options[:include_deleted] elsif self.name == "Doi" - must << { range: { published: { gte: "#{options[:year].split(",").min}||/y", lte: "#{options[:year].split(",").max}||/y", format: "yyyy" }}} if options[:year].present? + must << { range: { registered: { gte: "#{options[:registered].split(",").min}||/y", lte: "#{options[:registered].split(",").max}||/y", format: "yyyy" }}} if options[:registered].present? end __elasticsearch__.search({ diff --git a/app/models/doi.rb b/app/models/doi.rb index f884a1935..2d13fc58a 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -4,6 +4,7 @@ class Doi < ActiveRecord::Base include Metadatable include Cacheable include Licensable + include Dateable # include helper module for generating random DOI suffixes include Helpable @@ -32,12 +33,12 @@ class Doi < ActiveRecord::Base event :register do # can't register test prefix - transitions :from => [:draft], :to => :registered, :unless => :is_test_prefix? + transitions :from => [:draft], :to => :registered, :if => [:registerable?] end event :publish do # can't index test prefix - transitions :from => [:draft], :to => :findable, :unless => :is_test_prefix? + transitions :from => [:draft], :to => :findable, :if => [:registerable?] transitions :from => :registered, :to => :findable end @@ -57,12 +58,15 @@ class Doi < ActiveRecord::Base self.table_name = "dataset" alias_attribute :created_at, :created alias_attribute :updated_at, :updated - alias_attribute :resource_type_subtype, :additional_type - alias_attribute :published, :date_published alias_attribute :registered, :minted + alias_attribute :state, :aasm_state attr_accessor :current_user - attr_accessor :validate + + attribute :regenerate, :boolean, default: false + attribute :only_validate, :boolean, default: false + attribute :should_validate, :boolean, default: false + attribute :agency, :string, default: "DataCite" belongs_to :client, foreign_key: :datacentre has_many :media, -> { order "created DESC" }, foreign_key: :dataset, dependent: :destroy @@ -76,19 +80,18 @@ class Doi < ActiveRecord::Base # from https://www.crossref.org/blog/dois-and-matching-regular-expressions/ but using uppercase validates_format_of :doi, :with => /\A10\.\d{4,5}\/[-\._;()\/:a-zA-Z0-9\*~\$\=]+\z/, :on => :create validates_format_of :url, :with => /\A(ftp|http|https):\/\/[\S]+/ , if: :url?, message: "URL is not valid" - validates_uniqueness_of :doi, message: "This DOI has already been taken" + validates_uniqueness_of :doi, message: "This DOI has already been taken", unless: :only_validate validates :last_landing_page_status, numericality: { only_integer: true }, if: :last_landing_page_status? - - # validate :validation_errors + validates :xml, presence: true, xml_schema: true, :if => Proc.new { |doi| doi.validatable? } after_commit :update_url, on: [:create, :update] after_commit :update_media, on: [:create, :update] - before_save :set_defaults, :update_metadata + before_validation :update_xml, if: :regenerate + before_save :set_defaults, :save_metadata before_create { self.created = Time.zone.now.utc.iso8601 } scope :q, ->(query) { where("dataset.doi = ?", query) } - scope :not_indexed, -> { where("updated > indexed") } # use different index for testing index_name Rails.env.test? ? "dois-test" : "dois" @@ -99,17 +102,42 @@ class Doi < ActiveRecord::Base indexes :doi, type: :keyword indexes :identifier, type: :keyword indexes :url, type: :text, fields: { keyword: { type: "keyword" }} - indexes :author_normalized, type: :object, properties: { - type: { type: :keyword }, - id: { type: :keyword }, + indexes :creators, type: :object, properties: { + nameType: { type: :keyword }, + nameIdentifiers: { type: :object, properties: { + nameIdentifier: { type: :keyword }, + nameIdentifierScheme: { type: :keyword } + }}, name: { type: :text }, - "given-name" => { type: :text }, - "family-name" => { type: :text } + givenName: { type: :text }, + familyName: { type: :text }, + affiliation: { type: :text } + } + indexes :contributors, type: :object, properties: { + nameType: { type: :keyword }, + nameIdentifiers: { type: :object, properties: { + nameIdentifier: { type: :keyword }, + nameIdentifierScheme: { type: :keyword } + }}, + name: { type: :text }, + givenName: { type: :text }, + familyName: { type: :text }, + affiliation: { type: :text }, + contributorType: { type: :keyword } + } + indexes :creator_names, type: :text + indexes :titles, type: :object, properties: { + title: { type: :keyword }, + titleType: { type: :keyword }, + lang: { type: :keyword } + } + indexes :descriptions, type: :object, properties: { + description: { type: :keyword }, + descriptionType: { type: :keyword }, + lang: { type: :keyword } } - indexes :author_names, type: :text - indexes :title_normalized, type: :text - indexes :description_normalized, type: :text indexes :publisher, type: :text, fields: { keyword: { type: "keyword" }} + indexes :publication_year, type: :date, format: "yyyy", ignore_malformed: true indexes :client_id, type: :keyword indexes :provider_id, type: :keyword indexes :resource_type_id, type: :keyword @@ -121,15 +149,71 @@ class Doi < ActiveRecord::Base url: { type: :text }, media_type: { type: :keyword }, version: { type: :keyword }, - created: { type: :date }, - updated: { type: :date } + created: { type: :date, ignore_malformed: true }, + updated: { type: :date, ignore_malformed: true } + } + indexes :identifiers, type: :object, properties: { + identifierType: { type: :keyword }, + identifier: { type: :keyword } } - indexes :alternate_identifier, type: :object, properties: { + indexes :related_identifiers, type: :object, properties: { + relatedIdentifierType: { type: :keyword }, + relatedIdentifier: { type: :keyword }, + relationType: { type: :keyword }, + resourceTypeGeneral: { type: :keyword } + } + indexes :types, type: :object, properties: { + resourceTypeGeneral: { type: :keyword }, + resourceType: { type: :keyword }, + schemaOrg: { type: :keyword }, + bibtex: { type: :keyword }, + citeproc: { type: :keyword }, + ris: { type: :keyword } + } + indexes :funding_references, type: :object, properties: { + funderName: { type: :keyword }, + funderIdentifier: { type: :keyword }, + funderIdentifierType: { type: :keyword }, + awardNumber: { type: :keyword }, + awardUri: { type: :keyword }, + awardTitle: { type: :keyword } + } + indexes :dates, type: :object, properties: { + date: { type: :date, format: "yyyy-MM-dd||yyyy-MM||yyyy", ignore_malformed: true }, + dateType: { type: :keyword } + } + indexes :geo_locations, type: :object, properties: { + geoLocationPoint: { type: :object }, + geoLocationBox: { type: :object }, + geoLocationPlace: { type: :keyword } + } + indexes :rights_list, type: :object, properties: { + rights: { type: :keyword }, + rightsUri: { type: :keyword } + } + indexes :subjects, type: :object, properties: { + subject: { type: :keyword }, + subjectScheme: { type: :keyword }, + schemeUri: { type: :keyword }, + valueUri: { type: :keyword } + } + indexes :container, type: :object, properties: { type: { type: :keyword }, - name: { type: :keyword } + identifier: { type: :keyword }, + identifierType: { type: :keyword }, + title: { type: :keyword }, + volume: { type: :keyword }, + issue: { type: :keyword }, + firstPage: { type: :keyword }, + lastPage: { type: :keyword } } - indexes :resource_type_subtype, type: :keyword - indexes :version, type: :integer + + indexes :xml, type: :text, index: "not_analyzed" + indexes :content_url, type: :keyword + indexes :version_info, type: :keyword + indexes :formats, type: :keyword + indexes :sizes, type: :keyword + indexes :language, type: :keyword indexes :is_active, type: :keyword indexes :aasm_state, type: :keyword indexes :schema_version, type: :keyword @@ -138,19 +222,30 @@ class Doi < ActiveRecord::Base indexes :prefix, type: :keyword indexes :suffix, type: :keyword indexes :reason, type: :text - indexes :xml, type: :text, index: "no" - indexes :last_landing_page_status, type: :integer - indexes :last_landing_page_status_check, type: :date - indexes :last_landing_page_content_type, type: :keyword + indexes :landing_page, type: :object, properties: { + checked: { type: :date, ignore_malformed: true }, + url: { type: :string }, + status: { type: :integer }, + contentType: { type: :string }, + error: { type: :keyword }, + redirectCount: { type: :integer }, + redirectUrls: { type: :keyword }, + downloadLatency: { type: :scaled_float, scaling_factor: 100 }, + hasSchemaOrg: { type: :boolean }, + schemaOrgId: { type: :keyword }, + dcIdentifier: { type: :keyword }, + citationDoi: { type: :keyword }, + bodyHasPid: { type: :boolean } + } indexes :cache_key, type: :keyword - indexes :published, type: :date, format: "yyyy-MM-dd||yyyy-MM||yyyy", ignore_malformed: true - indexes :registered, type: :date - indexes :created, type: :date - indexes :updated, type: :date + indexes :registered, type: :date, ignore_malformed: true + indexes :created, type: :date, ignore_malformed: true + indexes :updated, type: :date, ignore_malformed: true # include parent objects - indexes :client, type: :object - indexes :resource_type, type: :object + indexes :client, type: :object + indexes :provider, type: :object + indexes :resource_type, type: :object end def as_indexed_json(options={}) @@ -160,36 +255,47 @@ def as_indexed_json(options={}) "doi" => doi, "identifier" => identifier, "url" => url, - "author_normalized" => author_normalized, - "author_names" => author_names, - "title_normalized" => title_normalized, - "description_normalized" => description_normalized, + "creators" => creators, + "contributors" => contributors, + "creator_names" => creator_names, + "titles" => titles, + "descriptions" => descriptions, "publisher" => publisher, "client_id" => client_id, "provider_id" => provider_id, + "resource_type_id" => resource_type_id, "media_ids" => media_ids, "prefix" => prefix, "suffix" => suffix, - "resource_type_id" => resource_type_id, - "resource_type_subtype" => resource_type_subtype, - "alternate_identifier" => alternate_identifier, - "b_version" => b_version, + "types" => types, + "identifiers" => identifiers, + "related_identifiers" => related_identifiers, + "funding_references" => funding_references, + "publication_year" => publication_year, + "dates" => dates, + "geo_locations" => geo_locations, + "rights_list" => rights_list, + "container" => container, + "content_url" => content_url, + "version_info" => version_info, + "formats" => formats, + "sizes" => sizes, + "language" => language, + "subjects" => subjects, + "xml" => xml, "is_active" => is_active, - "last_landing_page_status" => last_landing_page_status, - "last_landing_page_status_check" => last_landing_page_status_check, - "last_landing_page_content_type" => last_landing_page_content_type, + "landing_page" => landing_page, "aasm_state" => aasm_state, "schema_version" => schema_version, "metadata_version" => metadata_version, "reason" => reason, - "xml_encoded" => xml_encoded, "source" => source, "cache_key" => cache_key, - "published" => published, "registered" => registered, "created" => created, "updated" => updated, "client" => client.as_indexed_json, + "provider" => provider.as_indexed_json, "resource_type" => resource_type.try(:as_indexed_json), "media" => media.map { |m| m.try(:as_indexed_json) } } @@ -197,22 +303,27 @@ def as_indexed_json(options={}) def self.query_aggregations { - resource_types: { terms: { field: 'resource_type_id', size: 15, min_doc_count: 1 } }, - states: { terms: { field: 'aasm_state', size: 10, min_doc_count: 1 } }, - years: { date_histogram: { field: 'published', interval: 'year', min_doc_count: 1 } }, + resource_types: { terms: { field: 'types.resourceTypeGeneral', size: 15, min_doc_count: 1 } }, + states: { terms: { field: 'aasm_state', size: 15, min_doc_count: 1 } }, + years: { date_histogram: { field: 'publication_year', interval: 'year', min_doc_count: 1 } }, created: { date_histogram: { field: 'created', interval: 'year', min_doc_count: 1 } }, registered: { date_histogram: { field: 'registered', interval: 'year', min_doc_count: 1 } }, - providers: { terms: { field: 'provider_id', size: 10, min_doc_count: 1 } }, - clients: { terms: { field: 'client_id', size: 50, min_doc_count: 1 } }, - prefixes: { terms: { field: 'prefix', size: 10, min_doc_count: 1 } }, - schema_versions: { terms: { field: 'schema_version', size: 10, min_doc_count: 1 } }, - link_checks: { terms: { field: 'last_landing_page_status', size: 10, min_doc_count: 1 } }, - sources: { terms: { field: 'source', size: 10, min_doc_count: 1 } } + providers: { terms: { field: 'provider_id', size: 15, min_doc_count: 1 } }, + clients: { terms: { field: 'client_id', size: 15, min_doc_count: 1 } }, + prefixes: { terms: { field: 'prefix', size: 15, min_doc_count: 1 } }, + schema_versions: { terms: { field: 'schema_version', size: 15, min_doc_count: 1 } }, + link_checks_status: { terms: { field: 'landing_page.status', size: 15, min_doc_count: 1 } }, + link_checks_has_schema_org: { terms: { field: 'landing_page.hasSchemaOrg', size: 2, min_doc_count: 1 } }, + link_checks_schema_org_id: { value_count: { field: "landing_page.schemaOrgId" } }, + link_checks_dc_identifier: { value_count: { field: "landing_page.dcIdentifier" } }, + link_checks_citation_doi: { value_count: { field: "landing_page.citationDoi" } }, + links_checked: { value_count: { field: "landing_page.checked" } }, + sources: { terms: { field: 'source', size: 15, min_doc_count: 1 } }, } end def self.query_fields - ['doi^10', 'title_normalized^10', 'author_names^10', 'author_normalized.name^10', 'author_normalized.id^10', 'publisher^10', 'description_normalized^10', 'resource_type_id^10', 'resource_type_subtype^10', 'alternate_identifier.name^10', '_all'] + ['doi^10', 'titles.title^10', 'creator_names^10', 'creators.name^10', 'creators.id^10', 'publisher^10', 'descriptions.description^10', 'types.resourceTypeGeneral^10', 'subjects.subject^10', 'identifiers.identifier^10', 'related_identifiers.relatedIdentifier^10', '_all'] end def self.find_by_id(id, options={}) @@ -228,39 +339,86 @@ def self.find_by_id(id, options={}) }) end + def self.import_all(options={}) + from_date = options[:from_date].present? ? Date.parse(options[:from_date]) : Date.current + until_date = options[:until_date].present? ? Date.parse(options[:until_date]) : Date.current + + # get every day between from_date and until_date + (from_date..until_date).each do |d| + DoiImportByDayJob.perform_later(from_date: d.strftime("%F")) + puts "Queued importing for DOIs created on #{d.strftime("%F")}." + end + end + + def self.import_by_day(options={}) + return nil unless options[:from_date].present? + from_date = Date.parse(options[:from_date]) + + count = 0 + + logger = Logger.new(STDOUT) + + Doi.where(created: from_date.midnight..from_date.end_of_day).find_each do |doi| + begin + string = doi.current_metadata.present? ? doi.current_metadata.xml : nil + meta = doi.read_datacite(string: string, sandbox: doi.sandbox) + attrs = %w(creators contributors titles publisher publication_year types descriptions container sizes formats language dates identifiers related_identifiers funding_references geo_locations rights_list subjects content_url).map do |a| + [a.to_sym, meta[a]] + end.to_h.merge(schema_version: meta["schema_version"] || "http://datacite.org/schema/kernel-4", version_info: meta["version"], xml: string) + + doi.update_columns(attrs) + rescue TypeError, NoMethodError => error + logger.error "[MySQL] Error importing metadata for " + doi.doi + ": " + error.message + else + count += 1 + end + end + + if count > 0 + logger.info "[MySQL] Imported metadata for #{count} DOIs created on #{options[:from_date]}." + end + end + def self.index(options={}) from_date = options[:from_date].present? ? Date.parse(options[:from_date]) : Date.current until_date = options[:until_date].present? ? Date.parse(options[:until_date]) : Date.current + index_time = options[:index_time].presence || Time.zone.now.utc.iso8601 # get every day between from_date and until_date (from_date..until_date).each do |d| - DoiIndexByDayJob.perform_later(from_date: d.strftime("%F")) + DoiIndexByDayJob.perform_later(from_date: d.strftime("%F"), index_time: index_time) puts "Queued indexing for DOIs created on #{d.strftime("%F")}." - end + end end def self.index_by_day(options={}) return nil unless options[:from_date].present? from_date = Date.parse(options[:from_date]) - + index_time = options[:index_time].presence || Time.zone.now.utc.iso8601 + errors = 0 count = 0 logger = Logger.new(STDOUT) - Doi.where(created: from_date.midnight..from_date.end_of_day).not_indexed.find_in_batches(batch_size: 500) do |dois| + Doi.where(created: from_date.midnight..from_date.end_of_day).where("indexed < ?", index_time).find_in_batches(batch_size: 500) do |dois| response = Doi.__elasticsearch__.client.bulk \ index: Doi.index_name, type: Doi.document_type, body: dois.map { |doi| { index: { _id: doi.id, data: doi.as_indexed_json } } } + # log errors errors += response['items'].map { |k, v| k.values.first['error'] }.compact.length - count += dois.length + response['items'].select { |k, v| k.values.first['error'].present? }.each do |err| + logger.error "[Elasticsearch] " + err.inspect + end + dois.each { |doi| doi.update_column(:indexed, Time.zone.now) } + count += dois.length end if errors > 1 - logger.info "[Elasticsearch] #{errors} errors indexing #{count} DOIs created on #{options[:from_date]}." + logger.error "[Elasticsearch] #{errors} errors indexing #{count} DOIs created on #{options[:from_date]}." elsif count > 1 logger.info "[Elasticsearch] Indexed #{count} DOIs created on #{options[:from_date]}." end @@ -269,12 +427,12 @@ def self.index_by_day(options={}) count = 0 - Doi.where(created: from_date.midnight..from_date.end_of_day).not_indexed.find_each do |doi| + Doi.where(created: from_date.midnight..from_date.end_of_day).where("indexed < ?", index_time).find_each do |doi| IndexJob.perform_later(doi) - doi.update_column(:indexed, Time.zone.now) + doi.update_column(:indexed, Time.zone.now) count += 1 end - + logger.info "[Elasticsearch] Indexed #{count} DOIs created on #{options[:from_date]}." end @@ -283,7 +441,7 @@ def uid end def resource_type_id - resource_type_general.underscore.dasherize if resource_type_general.present? + types["resourceTypeGeneral"].underscore.dasherize if types.to_h["resourceTypeGeneral"].present? end def media_ids @@ -292,26 +450,14 @@ def media_ids def xml_encoded Base64.strict_encode64(xml) if xml.present? - rescue ArgumentError => exception + rescue ArgumentError => exception nil end - - def title_normalized - parse_attributes(title, content: "text", first: true) - end - def description_normalized - parse_attributes(description, content: "text", first: true) - end - - def author_normalized - Array.wrap(author) - end - - # author name in natural order: "John Smith" instead of "Smith, John" - def author_names - Array.wrap(author).map do |a| - if a["familyName"].present? + # creator name in natural order: "John Smith" instead of "Smith, John" + def creator_names + Array.wrap(creators).map do |a| + if a["familyName"].present? [a["givenName"], a["familyName"]].join(" ") elsif a["name"].to_s.include?(", ") a["name"].split(", ", 2).reverse.join(" ") @@ -334,8 +480,9 @@ def client_id end def client_id=(value) - r = cached_client_response(value) - return @client_id unless r.present? + r = ::Client.where(symbol: value).first + #r = cached_client_response(value) + fail ActiveRecord::RecordNotFound unless r.present? write_attribute(:datacentre, r.id) end @@ -356,10 +503,22 @@ def is_test_prefix? prefix == "10.5072" end + def registerable? + prefix != "10.5072" && url.present? + end + + # def is_valid? + # valid? && url.present? + # end + def is_registered_or_findable? %w(registered findable).include?(aasm_state) end + def validatable? + %w(registered findable).include?(aasm_state) || should_validate || only_validate + end + # update URL in handle system for registered and findable state # providers europ and ethz do their own handle registration def update_url @@ -374,7 +533,7 @@ def update_media media.delete_all Array.wrap(content_url).each do |c| - media << Media.create(url: c, media_type: content_format) + media << Media.create(url: c, media_type: formats) end end @@ -402,7 +561,7 @@ def current_media end def resource_type - cached_resource_type_response(resource_type_general.underscore.dasherize.downcase) if resource_type_general.present? + cached_resource_type_response(types["resourceTypeGeneral"].underscore.dasherize.downcase) if types.to_h["resourceTypeGeneral"].present? end def date_registered @@ -422,13 +581,13 @@ def event=(value) self.send(value) if %w(register publish hide).include?(value) end - # update state for all DOIs in state "undetermined" starting from from_date + # update state for all DOIs in state "" starting from from_date def self.set_state(from_date: nil) from_date ||= Time.zone.now - 1.day Doi.where("updated >= ?", from_date).where(aasm_state: '').find_each do |doi| - if doi.is_test_prefix? || (doi.is_active == "\x00" && doi.minted.blank?) + if doi.is_test_prefix? || (doi.is_active.getbyte(0) == 0 && doi.minted.blank?) state = "draft" - elsif doi.is_active == "\x00" && doi.minted.present? + elsif doi.is_active.to_s.getbyte(0) == 0 && doi.minted.present? state = "registered" else state = "findable" @@ -479,24 +638,75 @@ def self.set_url(from_date: nil) "Queued storing missing URL in database for DOIs updated since #{from_date.strftime("%F")}." end - # update metadata when any virtual attribute has changed - def update_metadata - changed_virtual_attributes = changed & %w(author title publisher date_published additional_type resource_type_general description content_size content_format) - - if changed_virtual_attributes.present? - @xml = datacite_xml - doc = Nokogiri::XML(xml, nil, 'UTF-8', &:noblanks) - ns = doc.collect_namespaces.find { |k, v| v.start_with?("http://datacite.org/schema/kernel") } - @schema_version = Array.wrap(ns).last || "http://datacite.org/schema/kernel-4" - attribute_will_change!(:xml) - end - - metadata.build(doi: self, xml: xml, namespace: schema_version) if (changed & %w(xml)).present? + # save to metadata table when xml has changed + def save_metadata + metadata.build(doi: self, xml: xml, namespace: schema_version) if xml.present? && xml_changed? end def set_defaults self.is_active = (aasm_state == "findable") ? "\x01" : "\x00" - self.version = version.present? ? version + 1 : 0 + self.version = version.present? ? version + 1 : 1 self.updated = Time.zone.now.utc.iso8601 end + + def self.migrate_landing_page(options={}) + logger = Logger.new(STDOUT) + logger.info "Starting migration" + + # Handle camel casing first. + Doi.where.not('last_landing_page_status_result' => nil).find_each do |doi| + begin + # First we try and fix into camel casing + result = doi.last_landing_page_status_result + mappings = { + "body-has-pid" => "bodyHasPid", + "dc-identifier" => "dcIdentifier", + "citation-doi" => "citationDoi", + "redirect-urls" => "redirectUrls", + "schema-org-id" => "schemaOrgId", + "has-schema-org" => "hasSchemaOrg", + "redirect-count" => "redirectCount", + "download-latency" => "downloadLatency" + } + result = result.map {|k, v| [mappings[k] || k, v] }.to_h +# doi.update_columns("last_landing_page_status_result": result) + + # Do a fix of the stored download Latency + # Sometimes was floating point precision, we dont need this + download_latency = result['downloadLatency'] + download_latency = download_latency.nil? ? download_latency : download_latency.round + + # Try to put the checked date into ISO8601 + # If we dont have one (there was legacy reasons) then set to unix epoch + checked = doi.last_landing_page_status_check + checked = checked.nil? ? Time.at(0) : checked + checked = checked.iso8601 + + # Next we want to build a new landing_page result. + landing_page = { + "checked" => checked, + "status" => doi.last_landing_page_status, + "url" => doi.last_landing_page, + "contentType" => doi.last_landing_page_content_type, + "error" => result['error'], + "redirectCount" => result['redirectCount'], + "redirectUrls" => result['redirectUrls'], + "downloadLatency" => download_latency, + "hasSchemaOrg" => result['hasSchemaOrg'], + "schemaOrgId" => result['schemaOrgId'], + "dcIdentifier" => result['dcIdentifier'], + "citationDoi" => result['citationDoi'], + "bodyHasPid" => result['bodyHasPid'], + } + + doi.update_columns("landing_page": landing_page) + + logger.info "Updated " + doi.doi + + rescue TypeError, NoMethodError => error + logger.error "Error updating landing page " + doi.doi + ": " + error.message + end + end + end + end diff --git a/app/models/provider.rb b/app/models/provider.rb index ccb8cea59..db1a18a0b 100644 --- a/app/models/provider.rb +++ b/app/models/provider.rb @@ -36,8 +36,8 @@ class Provider < ActiveRecord::Base validates_format_of :contact_email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, message: "contact_email should be an email" validates_format_of :website, :with => /https?:\/\/[\S]+/ , if: :website?, message: "Website should be an url" validates_inclusion_of :role_name, :in => %w( ROLE_ALLOCATOR ROLE_MEMBER ROLE_ADMIN ROLE_DEV ), :message => "Role %s is not included in the list" - validates_inclusion_of :organization_type, :in => %w(national_institution national_library academic_institution academic_library research_institution government_agency publisher professional_society service_provider vendor), :message => "organization type %s is not included in the list", if: :organization_type? - validates_inclusion_of :focus_area, :in => %w(biomedical_and_health_sciences earth_sciences humanities mathematics_and_computer_science physical_sciences_and_engineering social_sciences general), :message => "focus area %s is not included in the list", if: :focus_area? + validates_inclusion_of :organization_type, :in => %w(nationalInstitution nationalLibrary academicInstitution academicLibrary researchInstitution governmentAgency publisher professionalSociety serviceProvider vendor), :message => "organization type %s is not included in the list", if: :organization_type? + validates_inclusion_of :focus_area, :in => %w(biomedicalAndHealthSciences earthSciences humanities mathematicsAndComputerScience physicalSciencesAndEngineering socialSciences general), :message => "focus area %s is not included in the list", if: :focus_area? validate :freeze_symbol, :on => :update before_validation :set_region diff --git a/app/models/user.rb b/app/models/user.rb index 1e3fef236..cdfb5b789 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,6 +8,9 @@ class User # include helper module for setting emails via Mailgun API include Mailable + # include helper module for caching infrequently changing resources + include Cacheable + attr_accessor :name, :uid, :email, :role_id, :jwt, :password, :provider_id, :client_id, :beta_tester, :errors def initialize(credentials, options={}) @@ -38,6 +41,8 @@ def initialize(credentials, options={}) alias_attribute :orcid, :uid alias_attribute :id, :uid alias_attribute :flipper_id, :uid + alias_attribute :provider, :allocator + alias_attribute :client, :datacentre # Helper method to check for admin user def is_admin? @@ -54,18 +59,16 @@ def is_beta_tester? beta_tester end - def allocator + def provider return nil unless provider_id.present? - p = Provider.where(symbol: provider_id).first - p.id if p.present? + cached_provider_response(provider_id) end - def datacentre + def client return nil unless client_id.present? - c = Client.where(symbol: client_id).first - c.id if c.present? + cached_client_response(client_id) end def self.reset(username) diff --git a/app/serializers/client_prefix_serializer.rb b/app/serializers/client_prefix_serializer.rb index 7bf3219e5..44677417a 100644 --- a/app/serializers/client_prefix_serializer.rb +++ b/app/serializers/client_prefix_serializer.rb @@ -1,6 +1,6 @@ class ClientPrefixSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type "client-prefixes" set_id :uid cache_options enabled: true, cache_length: 24.hours diff --git a/app/serializers/client_serializer.rb b/app/serializers/client_serializer.rb index 8788e2a65..96c9fbf72 100644 --- a/app/serializers/client_serializer.rb +++ b/app/serializers/client_serializer.rb @@ -1,6 +1,6 @@ class ClientSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type :clients set_id :uid @@ -11,7 +11,7 @@ class ClientSerializer has_many :prefixes, record_type: :prefixes attribute :is_active do |object| - object.is_active == "\u0001" ? true : false + object.is_active.getbyte(0) == 1 ? true : false end attribute :has_password do |object| diff --git a/app/serializers/data_center_serializer.rb b/app/serializers/data_center_serializer.rb index 2e6a73234..0a92f7e01 100644 --- a/app/serializers/data_center_serializer.rb +++ b/app/serializers/data_center_serializer.rb @@ -7,7 +7,7 @@ class DataCenterSerializer attributes :title, :other_names, :prefixes, :member_id, :year, :created, :updated - belongs_to :member, record_type: :members, id_method_name: :provider_id + belongs_to :provider, key: :member, record_type: :members, serializer: :Member attribute :title do |object| object.name diff --git a/app/serializers/doi_serializer.rb b/app/serializers/doi_serializer.rb index 880473178..e74824873 100644 --- a/app/serializers/doi_serializer.rb +++ b/app/serializers/doi_serializer.rb @@ -1,55 +1,36 @@ class DoiSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type :dois set_id :uid - attributes :doi, :identifier, :url, :prefix, :suffix, :author, :title, :publisher, :resource_type_subtype, :description, :version, :metadata_version, :schema_version, :reason, :source, :state, :is_active, :landing_page, :published, :created, :registered, :updated, :xml, :cache_key + attributes :doi, :prefix, :suffix, :identifiers, :creators, :titles, :publisher, :container, :publication_year, :subjects, :contributors, :dates, :language, :types, :related_identifiers, :sizes, :formats, :version, :rights_list, :descriptions, :geo_locations, :funding_references, :xml, :url, :content_url, :metadata_version, :schema_version, :source, :is_active, :state, :reason, :landing_page, :created, :registered, :updated + attributes :prefix, :suffix, if: Proc.new { |object, params| params && params[:detail] } belongs_to :client, record_type: :clients - belongs_to :resource_type, record_type: :resource_types has_many :media - attribute :doi do |object| - object.doi.downcase - end - - attribute :author do |object| - object.author_normalized - end - - attribute :title do |object| - object.title_normalized + attribute :xml, if: Proc.new { |object, params| params && params[:detail] } do |object| + object.xml_encoded end - attribute :description do |object| - object.description_normalized + attribute :doi do |object| + object.doi.downcase end attribute :state do |object| object.aasm_state end - attribute :is_active do |object| - object.is_active == "\u0001" ? true : false - end - attribute :version do |object| - object.b_version + object.version_info end - attribute :xml do |object| - object.xml_encoded + attribute :is_active do |object| + object.is_active.to_s.getbyte(0) == 1 ? true : false end - attribute :landing_page, if: Proc.new { - |object, params| - params[:current_ability] && params[:current_ability].can?(:read_landing_page_results, object) == true - } do |object| - { status: object.last_landing_page_status, - "content-type" => object.last_landing_page_content_type, - checked: object.last_landing_page_status_check, - "result" => object.try(:last_landing_page_status_result) } + attribute :landing_page, if: Proc.new { |object, params| params[:current_ability] && params[:current_ability].can?(:read_landing_page_results, object) == true } do |object| + object.landing_page end - end diff --git a/app/serializers/media_serializer.rb b/app/serializers/media_serializer.rb index d5e879b61..ec109448f 100644 --- a/app/serializers/media_serializer.rb +++ b/app/serializers/media_serializer.rb @@ -1,6 +1,6 @@ class MediaSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type "media" set_id :uid cache_options enabled: true, cache_length: 24.hours diff --git a/app/serializers/metadata_serializer.rb b/app/serializers/metadata_serializer.rb index 7bd18b2ef..7194650d9 100644 --- a/app/serializers/metadata_serializer.rb +++ b/app/serializers/metadata_serializer.rb @@ -1,6 +1,6 @@ class MetadataSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type "metadata" set_id :uid cache_options enabled: true, cache_length: 24.hours diff --git a/app/serializers/prefix_serializer.rb b/app/serializers/prefix_serializer.rb index f0b3e085e..b642c2f63 100644 --- a/app/serializers/prefix_serializer.rb +++ b/app/serializers/prefix_serializer.rb @@ -1,6 +1,6 @@ class PrefixSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type :prefixes set_id :prefix cache_options enabled: true, cache_length: 24.hours diff --git a/app/serializers/provider_prefix_serializer.rb b/app/serializers/provider_prefix_serializer.rb index 3774fb4b1..b89aaf335 100644 --- a/app/serializers/provider_prefix_serializer.rb +++ b/app/serializers/provider_prefix_serializer.rb @@ -1,6 +1,6 @@ class ProviderPrefixSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type "provider-prefixes" set_id :uid attributes :created, :updated diff --git a/app/serializers/provider_serializer.rb b/app/serializers/provider_serializer.rb index 3d8490242..65aad32aa 100644 --- a/app/serializers/provider_serializer.rb +++ b/app/serializers/provider_serializer.rb @@ -1,6 +1,6 @@ class ProviderSerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type :providers set_id :uid @@ -13,7 +13,7 @@ class ProviderSerializer end attribute :is_active do |object| - object.is_active == "\u0001" ? true : false + object.is_active.getbyte(0) == 1 ? true : false end attribute :has_password do |object| diff --git a/app/serializers/repository_serializer.rb b/app/serializers/repository_serializer.rb index d8c932e68..df441b0e2 100644 --- a/app/serializers/repository_serializer.rb +++ b/app/serializers/repository_serializer.rb @@ -1,6 +1,6 @@ class RepositorySerializer include FastJsonapi::ObjectSerializer - set_key_transform :dash + set_key_transform :camel_lower set_type :repositories cache_options enabled: true, cache_length: 24.hours diff --git a/app/serializers/resource_type_serializer.rb b/app/serializers/resource_type_serializer.rb index d1920131d..17c4be62e 100644 --- a/app/serializers/resource_type_serializer.rb +++ b/app/serializers/resource_type_serializer.rb @@ -5,4 +5,4 @@ class ResourceTypeSerializer cache_options enabled: true, cache_length: 24.hours attributes :title, :updated -end +end \ No newline at end of file diff --git a/app/serializers/work_serializer.rb b/app/serializers/work_serializer.rb new file mode 100644 index 000000000..f5a216793 --- /dev/null +++ b/app/serializers/work_serializer.rb @@ -0,0 +1,92 @@ +class WorkSerializer + include FastJsonapi::ObjectSerializer + set_key_transform :dash + set_type :works + set_id :identifier + + attributes :doi, :identifier, :url, :author, :title, :container_title, :description, :resource_type_subtype, :data_center_id, :member_id, :resource_type_id, :version, :license, :schema_version, :results, :related_identifiers, :published, :registered, :checked, :updated, :media, :xml + + belongs_to :client, key: "data-center", record_type: "data-centers", serializer: :DataCenter + belongs_to :provider, key: :member, record_type: :members, serializer: :Member + belongs_to :resource_type, record_type: "resource-types", serializer: :ResourceType + + attribute :author do |object| + Array.wrap(object.creators).map do |c| + if (c["givenName"].present? || c["familyName"].present?) + { "given" => c["givenName"], + "family" => c["familyName"] }.compact + else + { "literal" => c["name"] }.presence + end + end + end + + attribute :doi do |object| + object.doi.downcase + end + + attribute :title do |object| + Array.wrap(object.titles).first.to_h.fetch("title", nil) + end + + attribute :description do |object| + Array.wrap(object.descriptions).first.to_h.fetch("title", nil) + end + + attribute :container_title do |object| + object.publisher + end + + attribute :resource_type_subtype do |object| + object.types.to_h.fetch("resourceType", nil) + end + + attribute :resource_type_id do |object| + rt = object.types.to_h.fetch("resourceTypeGeneral", nil) + if rt + rt.downcase.dasherize + else + nil + end + end + + attribute :data_center_id do |object| + object.client_id + end + + attribute :member_id do |object| + object.provider_id + end + + attribute :version do |object| + object.version_info + end + + attribute :schema_version do |object| + object.schema_version.to_s.split("-", 2).last.presence + end + + attribute :license do |object| + Array.wrap(object.rights_list).first.to_h.fetch("rightsUri", nil) + end + + attribute :results do |object| + [] + end + + attribute :related_identifiers do |object| + [] + end + + attribute :published do |object| + object.publication_year.to_s.presence + end + + attribute :xml do |object| + Base64.strict_encode64(object.xml) if object.xml.present? + end + + attribute :checked do |object| + nil + end +end \ No newline at end of file diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 000000000..6e6709219 --- /dev/null +++ b/bin/rspec @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require 'bundler/setup' +load Gem.bin_path('rspec-core', 'rspec') diff --git a/config/application.rb b/config/application.rb index 6ed5f7544..7e7eb0102 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,7 +32,7 @@ ENV['APPLICATION'] ||= "client-api" ENV['HOSTNAME'] ||= "lupo" ENV['MEMCACHE_SERVERS'] ||= "memcached:11211" -ENV['SITE_TITLE'] ||= "REST API" +ENV['SITE_TITLE'] ||= "DataCite REST API" ENV['LOG_LEVEL'] ||= "info" ENV['CONCURRENCY'] ||= "25" ENV['CDN_URL'] ||= "https://assets.datacite.org" @@ -60,7 +60,6 @@ class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.1 config.autoload_paths << Rails.root.join('lib') - config.autoload_paths << Rails.root.join("app", "models", "concerns") # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index e4c5bb7d4..477f2578c 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -5,6 +5,12 @@ class IdentifierError < RuntimeError; end ActiveModelSerializers::Adapter::JsonApi::Deserialization::InvalidDocument, JWT::DecodeError, JWT::VerificationError, + JSON::ParserError, + Nokogiri::XML::SyntaxError, + NoMethodError, + SocketError, + ActionDispatch::Http::Parameters::ParseError, + ActiveRecord::RecordNotUnique, ActiveRecord::RecordNotFound, AbstractController::ActionNotFound, ActionController::UnknownFormat, diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index 0663c200d..f89abdec4 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -14,8 +14,6 @@ Mime::Type.register "application/vnd.datacite.datacite+xml", :datacite, %w( application/x-datacite+xml ) Mime::Type.register "application/vnd.datacite.datacite+json", :datacite_json Mime::Type.register "application/vnd.schemaorg.ld+json", :schema_org -Mime::Type.register "application/rdf+xml", :rdf_xml -Mime::Type.register "text/turtle", :turtle Mime::Type.register "application/vnd.jats+xml", :jats Mime::Type.register "application/vnd.citationstyles.csl+json", :citeproc, %w( application/citeproc+json ) Mime::Type.register "application/vnd.codemeta.ld+json", :codemeta @@ -26,68 +24,45 @@ # register renderers for these Mime types # :citation and :datacite is handled differently ActionController::Renderers.add :datacite do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.xml - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[:datacite], - disposition: "attachment; filename=#{filename}.xml" + Array.wrap(obj).map { |o| o.xml }.join("\n") end ActionController::Renderers.add :citation do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.citation - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[:citation], - disposition: "attachment; filename=#{filename}.txt" -end - -ActionController::Renderers.add :turtle do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.send(:turtle) - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[:turtle], - disposition: "attachment; filename=#{filename}.ttl" + begin + Array.wrap(obj).map do |o| + o.style = options[:style] || "apa" + o.locale = options[:locale] || "en-US" + o.citation + end.join("\n\n") + rescue CSL::ParseError # unknown style and/or location + Array.wrap(obj).map do |o| + o.style = "apa" + o.locale = "en-US" + o.citation + end.join("\n\n") + end end %w(datacite_json schema_org crosscite citeproc codemeta).each do |f| ActionController::Renderers.add f.to_sym do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.send(f) - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[f.to_sym], - disposition: "attachment; filename=#{filename}.json" + if obj.is_a?(Array) + "[\n" + Array.wrap(obj).map { |o| o.send(f) }.join(",\n") + "\n]" + else + obj.send(f) + end end end -%w(crossref rdf_xml jats).each do |f| +%w(jats).each do |f| ActionController::Renderers.add f.to_sym do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.send(f) - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[f.to_sym], - disposition: "attachment; filename=#{filename}.xml" + Array.wrap(obj).map { |o| o.send(f) }.join("\n") end end ActionController::Renderers.add :bibtex do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.send("bibtex") - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[:bibtex], - disposition: "attachment; filename=#{filename}.bib" + Array.wrap(obj).map { |o| o.send("bibtex") }.join("\n") end ActionController::Renderers.add :ris do |obj, options| - uri = Addressable::URI.parse(obj.identifier) - data = obj.send("ris") - - filename = uri.path.gsub(/[^0-9A-Za-z.\-]/, '_') - send_data data.to_s, type: Mime[:ris], - disposition: "attachment; filename=#{filename}.ris" + Array.wrap(obj).map { |o| o.send("ris") }.join("\n\n") end diff --git a/config/initializers/turnout.rb b/config/initializers/turnout.rb new file mode 100644 index 000000000..df4a6ca76 --- /dev/null +++ b/config/initializers/turnout.rb @@ -0,0 +1,4 @@ +Turnout.configure do |config| + config.default_maintenance_page = Turnout::MaintenancePage::JSON + config.default_reason = "The site is temporarily down for maintenance. Please check https://status.datacite.org for more information." +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index a8a9cfe1d..0c3a6251a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,6 +7,29 @@ # send reset link post 'reset', :to => 'sessions#reset' + # content negotiation + get '/dois/application/vnd.datacite.datacite+xml/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :datacite } + get '/dois/application/vnd.datacite.datacite+json/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :datacite_json } + get '/dois/application/vnd.crosscite.crosscite+json/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :crosscite } + get '/dois/application/vnd.schemaorg.ld+json/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :schema_org } + get '/dois/application/vnd.codemeta.ld+json/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :codemeta } + get '/dois/application/vnd.citationstyles.csl+json/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :citeproc } + get '/dois/application/vnd.jats+xml/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :jats } + get '/dois/application/x-bibtex/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :bibtex } + get '/dois/application/x-research-info-systems/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :ris } + get '/dois/text/x-bibliography/:id', :to => 'dois#show', constraints: { :id => /.+/ }, defaults: { format: :citation } + + get '/dois/application/vnd.datacite.datacite+xml', :to => 'dois#index', defaults: { format: :datacite } + get '/dois/application/vnd.datacite.datacite+json', :to => 'dois#index', defaults: { format: :datacite_json } + get '/dois/application/vnd.crosscite.crosscite+json', :to => 'dois#index', defaults: { format: :crosscite } + get '/dois/application/vnd.schemaorg.ld+json', :to => 'dois#index', defaults: { format: :schema_org } + get '/dois/application/vnd.codemeta.ld+json', :to => 'dois#index', defaults: { format: :codemeta } + get '/dois/application/vnd.citationstyles.csl+json', :to => 'dois#index', defaults: { format: :citeproc } + get '/dois/application/vnd.jats+xml', :to => 'dois#index', defaults: { format: :jats } + get '/dois/application/x-bibtex', :to => 'dois#index', defaults: { format: :bibtex } + get '/dois/application/x-research-info-systems', :to => 'dois#index', defaults: { format: :ris } + get '/dois/text/x-bibliography', :to => 'dois#index', defaults: { format: :citation } + # manage DOIs post 'dois/validate', :to => 'dois#validate' post 'dois/status', :to => 'dois#status' @@ -24,6 +47,7 @@ post 'provider-prefixes/set-created', :to => 'provider_prefixes#set_created' resources :heartbeat, only: [:index] + resources :index, only: [:index] resources :clients, constraints: { :id => /.+/ } do resources :prefixes, constraints: { :id => /.+/ } @@ -54,19 +78,7 @@ # support for legacy routes resources :members, only: [:show, :index] resources :data_centers, only: [:show, :index], constraints: { :id => /.+/ }, path: "/data-centers" - - # content negotiation - get '/application/vnd.datacite.datacite+xml/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :datacite } - get '/application/vnd.datacite.datacite+json/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :datacite_json } - get '/application/vnd.crosscite.crosscite+json/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :crosscite } - get '/application/vnd.schemaorg.ld+json/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :schema_org } - get '/application/vnd.codemeta.ld+json/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :codemeta } - get '/application/vnd.citationstyles.csl+json/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :citeproc } - get '/application/vnd.jats+xml/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :jats } - get '/application/x-bibtex/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :bibtex } - get '/application/x-research-info-systems/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :ris } - get '/text/x-bibliography/:id', :to => 'index#show', constraints: { :id => /.+/ }, defaults: { format: :citation } - resources :index, path: '/', constraints: { :id => /.+/ }, only: [:show, :index] + resources :works, only: [:show, :index], constraints: { :id => /.+/ } # rescue routing errors #match "*path", to: "index#routing_error", via: :all diff --git a/db/migrate/20181102094810_add_schema_attributes.rb b/db/migrate/20181102094810_add_schema_attributes.rb new file mode 100644 index 000000000..93ed12734 --- /dev/null +++ b/db/migrate/20181102094810_add_schema_attributes.rb @@ -0,0 +1,26 @@ +class AddSchemaAttributes < ActiveRecord::Migration[5.2] + def change + add_column :dataset, :creator, :json + add_column :dataset, :contributor, :json + add_column :dataset, :titles, :json + add_column :dataset, :publisher, :text + add_column :dataset, :publication_year, :integer + add_column :dataset, :types, :json + add_column :dataset, :descriptions, :json + add_column :dataset, :periodical, :json + add_column :dataset, :sizes, :json + add_column :dataset, :formats, :json + add_column :dataset, :version_info, :string, limit: 191 + add_column :dataset, :language, :string, limit: 191 + add_column :dataset, :dates, :json + add_column :dataset, :alternate_identifiers, :json + add_column :dataset, :related_identifiers, :json + add_column :dataset, :funding_references, :json + add_column :dataset, :geo_locations, :json + add_column :dataset, :rights_list, :json + add_column :dataset, :subjects, :json + add_column :dataset, :schema_version, :string, limit: 191 + add_column :dataset, :content_url, :json + add_column :dataset, :xml, :binary, limit: 16777215 + end +end diff --git a/db/migrate/20181124062253_add_software_field.rb b/db/migrate/20181124062253_add_software_field.rb new file mode 100644 index 000000000..001409c18 --- /dev/null +++ b/db/migrate/20181124062253_add_software_field.rb @@ -0,0 +1,6 @@ +class AddSoftwareField < ActiveRecord::Migration[5.2] + def change + add_column :datacentre, :software, :string, limit: 191 + add_column :datacentre, :description, :text + end +end \ No newline at end of file diff --git a/db/migrate/20181129100131_add_landing_page_to_dataset.rb b/db/migrate/20181129100131_add_landing_page_to_dataset.rb new file mode 100644 index 000000000..55584af06 --- /dev/null +++ b/db/migrate/20181129100131_add_landing_page_to_dataset.rb @@ -0,0 +1,5 @@ +class AddLandingPageToDataset < ActiveRecord::Migration[5.2] + def change + add_column :dataset, :landing_page, :json + end +end diff --git a/db/migrate/20181130182349_rename_creator_column.rb b/db/migrate/20181130182349_rename_creator_column.rb new file mode 100644 index 000000000..3cdfc0a9d --- /dev/null +++ b/db/migrate/20181130182349_rename_creator_column.rb @@ -0,0 +1,7 @@ +class RenameCreatorColumn < ActiveRecord::Migration[5.2] + def change + rename_column :dataset, :creator, :creators + rename_column :dataset, :contributor, :contributors + add_column :dataset, :agency, :string, limit: 191, default: "DataCite" + end +end diff --git a/db/migrate/20181209231736_rename_doi_columns.rb b/db/migrate/20181209231736_rename_doi_columns.rb new file mode 100644 index 000000000..751fb1009 --- /dev/null +++ b/db/migrate/20181209231736_rename_doi_columns.rb @@ -0,0 +1,6 @@ +class RenameDoiColumns < ActiveRecord::Migration[5.2] + def change + rename_column :dataset, :alternate_identifiers, :identifiers + rename_column :dataset, :periodical, :container + end +end diff --git a/db/schema.rb b/db/schema.rb index 1b1a15770..d67b517f8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_10_23_235649) do +ActiveRecord::Schema.define(version: 2018_12_09_231736) do create_table "active_storage_attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t| t.string "name", limit: 191, null: false @@ -92,6 +92,8 @@ t.datetime "deleted_at" t.string "re3data" t.text "url" + t.string "software", limit: 191 + t.text "description" t.index ["allocator"], name: "FK6695D60546EBD781" t.index ["re3data"], name: "index_datacentre_on_re3data" t.index ["symbol"], name: "symbol", unique: true @@ -130,6 +132,30 @@ t.string "reason" t.string "source", limit: 191 t.datetime "indexed", precision: 3, default: "1970-01-01 00:00:00", null: false + t.json "creators" + t.json "contributors" + t.json "titles" + t.text "publisher" + t.integer "publication_year" + t.json "types" + t.json "descriptions" + t.json "container" + t.json "sizes" + t.json "formats" + t.string "version_info", limit: 191 + t.string "language", limit: 191 + t.json "dates" + t.json "identifiers" + t.json "related_identifiers" + t.json "funding_references" + t.json "geo_locations" + t.json "rights_list" + t.json "subjects" + t.string "schema_version", limit: 191 + t.json "content_url" + t.binary "xml", limit: 16777215 + t.string "agency", limit: 191, default: "DataCite" + t.json "landing_page" t.index ["aasm_state"], name: "index_dataset_on_aasm_state" t.index ["created", "indexed", "updated"], name: "index_dataset_on_created_indexed_updated" t.index ["datacentre"], name: "FK5605B47847B5F5FF" diff --git a/docker-compose.yml b/docker-compose.yml index f4543c10d..edd338905 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,7 @@ services: env_file: .env environment: - ELASTIC_PASSWORD=changeme + build: . image: datacite/lupo ports: - "8065:80" @@ -45,6 +46,8 @@ services: ES_JAVA_OPTS: -Xmx256m -Xms256m ELASTIC_PASSWORD: changeme xpack.security.enabled: "false" + http.cors.enabled: "true" + http.cors.allow-origin: "*" networks: - public healthcheck: diff --git a/lib/tasks/doi.rake b/lib/tasks/doi.rake index a5748b394..fe0658fc4 100644 --- a/lib/tasks/doi.rake +++ b/lib/tasks/doi.rake @@ -6,6 +6,27 @@ namespace :doi do puts response end + desc 'Import all DOIs' + task :import_all => :environment do + if ENV['YEAR'].present? + from_date = "#{ENV['YEAR']}-01-01" + until_date = "#{ENV['YEAR']}-12-31" + else + from_date = ENV['FROM_DATE'] || Date.current.strftime("%F") + until_date = ENV['UNTIL_DATE'] || Date.current.strftime("%F") + end + + Doi.import_all(from_date: from_date, until_date: until_date) + end + + desc 'Import DOIs per day' + task :import_by_day => :environment do + from_date = ENV['FROM_DATE'] || Date.current.strftime("%F") + + Doi.import_by_day(from_date: from_date) + puts "DOIs created on #{from_date} imported." + end + desc 'Index all DOIs' task :index => :environment do if ENV['YEAR'].present? @@ -16,7 +37,9 @@ namespace :doi do until_date = ENV['UNTIL_DATE'] || Date.current.strftime("%F") end - Doi.index(from_date: from_date, until_date: until_date) + index_time = Time.zone.now.utc.iso8601 + + Doi.index(from_date: from_date, until_date: until_date, index_time: index_time) end desc 'Index DOIs per day' @@ -50,4 +73,9 @@ namespace :doi do from_date = ENV['FROM_DATE'] || Time.zone.now - 1.month Doi.delete_test_dois(from_date: from_date) end + + desc 'Migrates landing page data handling camelCase changes at same time' + task :migrate_landing_page => :environment do + Doi.migrate_landing_page + end end diff --git a/lib/xml_schema_validator.rb b/lib/xml_schema_validator.rb new file mode 100644 index 000000000..37272c68f --- /dev/null +++ b/lib/xml_schema_validator.rb @@ -0,0 +1,54 @@ +class XmlSchemaValidator < ActiveModel::EachValidator + # mapping of DataCite schema properties to database fields + def schema_attributes(el) + schema = { + "date" => "dates", + "publicationYear" => "publication_year", + "alternateIdentifiers" => "identifiers", + "relatedIdentifiers" => "related_identifiers", + "geoLocations" => "geo_locations", + "rightsList" => "rights_list", + "fundingReferences" => "funding_references", + "version" => "version_info", + "resource" => "xml" + } + + schema[el] || el + end + + def get_valid_kernel(sv) + kernels = { + "http://datacite.org/schema/kernel-2.1" => "kernel-2.1", + "http://datacite.org/schema/kernel-2.2" => "kernel-2.2", + "http://datacite.org/schema/kernel-3.0" => "kernel-3", + "http://datacite.org/schema/kernel-3.1" => "kernel-3", + "http://datacite.org/schema/kernel-3" => "kernel-3", + "http://datacite.org/schema/kernel-4.0" => "kernel-4", + "http://datacite.org/schema/kernel-4.1" => "kernel-4", + "http://datacite.org/schema/kernel-4" => "kernel-4" + } + + kernels[sv] + end + + def validate_each(record, attribute, value) + kernel = get_valid_kernel(record.schema_version) + return false unless kernel.present? + + filepath = Bundler.rubygems.find_name('bolognese').first.full_gem_path + "/resources/#{kernel}/metadata.xsd" + schema = Nokogiri::XML::Schema(open(filepath)) + + schema.validate(Nokogiri::XML(value, nil, 'UTF-8')).reduce({}) do |sum, error| + location, level, source, text = error.message.split(": ", 4) + line, column = location.split(":", 2) + title = text.to_s.strip + " at line #{line}, column #{column}" if line.present? + source = source.split("}").last[0..-2] if line.present? + source = schema_attributes(source) if source.present? + record.errors[source.to_sym] << title + end + rescue Nokogiri::XML::SyntaxError => e + line, column, level, text = e.message.split(":", 4) + message = text.strip + " at line #{line}, column #{column}" + record.errors[:xml] << message + end +end diff --git a/public/.keep b/public/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index 37b576a4a..000000000 --- a/public/robots.txt +++ /dev/null @@ -1 +0,0 @@ -# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/spec/concerns/crosscitable_spec.rb b/spec/concerns/crosscitable_spec.rb index e0c5ac570..6ef5d94de 100644 --- a/spec/concerns/crosscitable_spec.rb +++ b/spec/concerns/crosscitable_spec.rb @@ -1,123 +1,429 @@ require 'rails_helper' describe Doi, vcr: true do - subject { create(:doi) } + let(:xml) { file_fixture('datacite.xml').read } + + subject { DoisController.new } context "from_xml" do it "from_xml" do string = file_fixture('datacite.xml').read - expect(subject.from_xml(string)).to be_nil - expect(subject.errors).to be_empty + expect(subject.from_xml(string)).to eq(string) end it "from_xml malformed" do string = file_fixture('datacite_malformed.xml').read - expect(subject.from_xml(string)).to eq(string) - expect(subject.errors.messages).to eq(xml: ["Premature end of data in tag resource line 2 at line 40, column 1"]) + expect { subject.from_xml(string) }.to raise_error(Nokogiri::XML::SyntaxError, "40:1: FATAL: Premature end of data in tag resource line 2") end end context "from_json" do it "from_json" do string = file_fixture('citeproc.json').read - expect(subject.from_json(string)).to be_nil - expect(subject.errors).to be_empty + expect(subject.from_json(string)).to eq(string) + end + + it "from_json starts with unexpected character" do + string = file_fixture('datacite.xml').read + expect { subject.from_json(string) }.to raise_error(JSON::ParserError, "unexpected character at line 1, column 1 [parse.c:671]") end it "from_json malformed" do string = file_fixture('citeproc_malformed.json').read - expect(subject.from_json(string)).to eq(string) - expect(subject.errors.messages).to eq(xml: ["Expected comma, not a string at line 4, column 9 [parse.c:381]"]) + expect { subject.from_json(string) }.to raise_error(JSON::ParserError, "expected comma, not a string at line 4, column 9 [parse.c:381]") end it "from_json duplicate keys" do string = file_fixture('citeproc_duplicate_keys.json').read - expect(subject.from_json(string)).to eq(string) - expect(subject.errors.messages).to eq(xml: ["The same key is defined more than once: id"]) + expect { subject.from_json(string) }.to raise_error(JSON::ParserError, "The same key is defined more than once: id") end end context "well_formed_xml" do it "from_xml" do - string = Base64.strict_encode64(file_fixture('datacite.xml').read) - expect(subject.well_formed_xml(string)).to eq(Base64.decode64(string)) - expect(subject.errors).to be_empty + string = file_fixture('datacite.xml').read + expect(subject.well_formed_xml(string)).to eq(string) end it "from_xml malformed" do - string = Base64.strict_encode64(file_fixture('datacite_malformed.xml').read) - expect(subject.well_formed_xml(string)).to eq(Base64.decode64(string)) - expect(subject.errors.messages).to eq(xml: ["Premature end of data in tag resource line 2 at line 40, column 1"]) + string = file_fixture('datacite_malformed.xml').read + expect { subject.well_formed_xml(string) }.to raise_error(Nokogiri::XML::SyntaxError, "40:1: FATAL: Premature end of data in tag resource line 2") end it "from_json" do - string = Base64.strict_encode64(file_fixture('citeproc.json').read) - expect(subject.well_formed_xml(string)).to eq(Base64.decode64(string)) - expect(subject.errors).to be_empty + string = file_fixture('citeproc.json').read + expect(subject.well_formed_xml(string)).to eq(string) + end + + it "from_json starts with unexpected character" do + string = 'abc' + expect { subject.well_formed_xml(string) }.to raise_error(JSON::ParserError, "unexpected character at line 1, column 1 [parse.c:671]") end it "from_json malformed" do - string = Base64.strict_encode64(file_fixture('citeproc_malformed.json').read) - expect(subject.well_formed_xml(string)).to eq(Base64.decode64(string)) - expect(subject.errors.messages).to eq(xml: ["Expected comma, not a string at line 4, column 9 [parse.c:381]"]) + string = file_fixture('citeproc_malformed.json').read + expect { subject.well_formed_xml(string) }.to raise_error(JSON::ParserError, "expected comma, not a string at line 4, column 9 [parse.c:381]") end it "from_json duplicate keys" do - string = Base64.strict_encode64(file_fixture('citeproc_duplicate_keys.json').read) - expect(subject.well_formed_xml(string)).to eq(Base64.decode64(string)) - expect(subject.errors.messages).to eq(xml: ["The same key is defined more than once: id"]) + string = file_fixture('citeproc_duplicate_keys.json').read + expect { subject.well_formed_xml(string) }.to raise_error(JSON::ParserError, "The same key is defined more than once: id") end end - context "get attributes" do - it "author" do - expect(subject.author).to eq("name"=>"D S") + context "get_content_type" do + it "datacite" do + string = file_fixture('datacite.xml').read + expect(subject.get_content_type(string)).to eq("xml") + end + + it "crossref" do + string = file_fixture('crossref.xml').read + expect(subject.get_content_type(string)).to eq("xml") + end + + it "crosscite" do + string = file_fixture('crosscite.json').read + expect(subject.get_content_type(string)).to eq("json") + end + + it "schema_org" do + string = file_fixture('schema_org.json').read + expect(subject.get_content_type(string)).to eq("json") + end + + it "codemeta" do + string = file_fixture('codemeta.json').read + expect(subject.get_content_type(string)).to eq("json") + end + + it "datacite_json" do + string = file_fixture('datacite.json').read + expect(subject.get_content_type(string)).to eq("json") + end + + it "bibtex" do + string = file_fixture('crossref.bib').read + expect(subject.get_content_type(string)).to eq("string") + end + + it "ris" do + string = file_fixture('crossref.ris').read + expect(subject.get_content_type(string)).to eq("string") + end + end + + context "parse_xml" do + it "from schema 4" do + string = file_fixture('datacite.xml').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("datacite") + expect(meta["doi"]).to eq("10.14454/4k3m-nyvg") + expect(meta["creators"]).to eq([{"familyName"=>"Fenner", "givenName"=>"Martin","name"=>"Fenner, Martin", + "nameIdentifiers"=> + [{"nameIdentifier"=>"https://orcid.org/0000-0003-1419-2405", + "nameIdentifierScheme"=>"ORCID"}], + "nameType"=>"Personal"}]) + expect(meta["titles"]).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("DataCite") + end + + it "from schema 3" do + string = file_fixture('datacite_schema_3.xml').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("datacite") + expect(meta["doi"]).to eq("10.5061/dryad.8515") + expect(meta["creators"].length).to eq(8) + expect(meta["creators"].first).to eq("familyName"=>"Ollomo", "givenName"=>"Benjamin", "name"=>"Ollomo, Benjamin", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) + expect(meta["publication_year"]).to eq("2011") + expect(meta["publisher"]).to eq("Dryad Digital Repository") + end + + it "from schema 2.2" do + string = file_fixture('datacite_schema_2.2.xml').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("datacite") + expect(meta["doi"]).to eq("10.5072/testpub") + expect(meta["creators"]).to eq([{"familyName"=>"Smith", "givenName"=>"John", "name"=>"Smith, John", "nameType"=>"Personal"}, {"name"=>"つまらないものですが"}]) + expect(meta["titles"]).to eq([{"title"=>"Właściwości rzutowań podprzestrzeniowych"}, {"title"=>"Translation of Polish titles", "titleType"=>"TranslatedTitle"}]) + expect(meta["publication_year"]).to eq("2010") + expect(meta["publisher"]).to eq("Springer") end - it "title" do - expect(subject.title).to eq("Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]") + it "from schema 4 missing creators" do + string = file_fixture('datacite_missing_creator.xml').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("datacite") + expect(meta["doi"]).to eq("10.5438/4k3m-nyvg") + expect(meta["creators"]).to be_empty + expect(meta["titles"]).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("DataCite") end - it "publisher" do - expect(subject.publisher).to eq("F1000 Research Limited") + it "from namespaced xml" do + string = file_fixture('ns0.xml').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("datacite") + # TODO + # expect(meta["doi"]).to eq("10.5438/4k3m-nyvg") + # expect(meta["creators"]).to be_empty + # expect(meta["titles"]).to eq([{"title"=>"Eating your own Dog Food"}]) + # expect(meta["publication_year"]).to eq("2016") + # expect(meta["publisher"]).to eq("DataCite") end - it "date_published" do - expect(subject.date_published).to eq("2017") + it "from crossref" do + string = file_fixture('crossref.xml').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("crossref") + expect(meta["doi"]).to eq("10.1371/journal.pone.0000030") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Ralser", "givenName"=>"Markus", "name"=>"Ralser, Markus", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes"}]) + expect(meta["publication_year"]).to eq("2006") + expect(meta["publisher"]).to eq("(:unav)") + expect(meta["container"]).to eq("firstPage"=>"e30", "identifier"=>"1932-6203", "identifierType"=>"ISSN", "issue"=>"1", "title"=>"PLoS ONE", "type"=>"Journal", "volume"=>"1") end - it "resource_type_general" do - expect(subject.resource_type_general).to eq("Text") + it "from crossref url" do + string = "https://doi.org/10.7554/elife.01567" + meta = subject.parse_xml(string) + + expect(meta["from"]).to eq("crossref") + expect(meta["doi"]).to eq("10.7554/elife.01567") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Sankar", "givenName"=>"Martial", "name"=>"Sankar, Martial", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(meta["publication_year"]).to eq("2014") + expect(meta["publisher"]).to eq("(:unav)") + expect(meta["container"]).to eq("identifier"=>"2050-084X", "identifierType"=>"ISSN", "title"=>"eLife", "type"=>"Journal", "volume"=>"3") + expect(meta["agency"]).to eq("Crossref") + end + + it "from datacite url" do + string = "https://doi.org/10.7272/q6g15xs4" + meta = subject.parse_xml(string) + + expect(meta["from"]).to eq("datacite") + expect(meta["doi"]).to eq("10.7272/q6g15xs4") + expect(meta["creators"].length).to eq(2) + expect(meta["creators"].first).to eq("affiliation"=>"UC San Francisco", "familyName"=>"Rodriguez", "givenName"=>"Robert", "name"=>"Rodriguez, Robert", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"NEXUS Head CT"}]) + expect(meta["publication_year"]).to eq("2017") + expect(meta["publisher"]).to eq("UC San Francisco") + expect(meta["agency"]).to eq("DataCite") + end + + it "from bibtex" do + string = file_fixture('crossref.bib').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("bibtex") + expect(meta["doi"]).to eq("10.7554/elife.01567") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Sankar", "givenName"=>"Martial", "name"=>"Sankar, Martial", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(meta["publication_year"]).to eq("2014") + expect(meta["publisher"]).to eq("{eLife} Sciences Organisation, Ltd.") + expect(meta["container"]).to eq("identifier"=>"2050-084X", "identifierType"=>"ISSN", "title"=>"eLife", "type"=>"Journal", "volume"=>"3") + end + + it "from ris" do + string = file_fixture('crossref.ris').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("ris") + expect(meta["doi"]).to eq("10.7554/elife.01567") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Sankar", "givenName"=>"Martial", "name"=>"Sankar, Martial", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(meta["publication_year"]).to eq("2014") + expect(meta["publisher"]).to eq("(:unav)") + expect(meta["container"]).to eq("title"=>"eLife", "type"=>"Journal", "volume"=>"3") + end + + it "from codemeta" do + string = file_fixture('codemeta.json').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("codemeta") + expect(meta["doi"]).to eq("10.5063/f1m61h5x") + expect(meta["creators"].length).to eq(3) + expect(meta["creators"].first).to eq("affiliation" => "NCEAS", + "familyName" => "Jones", + "givenName" => "Matt", + "name" => "Jones, Matt", + "nameIdentifiers" => [{"nameIdentifier"=>"https://orcid.org/0000-0003-0077-4738", "nameIdentifierScheme"=>"ORCID"}], + "nameType" => "Personal") + expect(meta["titles"]).to eq([{"title"=>"R Interface to the DataONE REST API"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("https://cran.r-project.org") + end + + it "from schema_org" do + string = file_fixture('schema_org.json').read + meta = subject.parse_xml(string) + + expect(meta["string"]).to eq(string) + expect(meta["from"]).to eq("schema_org") + expect(meta["doi"]).to eq("10.5438/4k3m-nyvg") + expect(meta["creators"].length).to eq(1) + expect(meta["creators"].first).to eq("familyName"=>"Fenner", "givenName"=>"Martin", "name" => "Fenner, Martin", + "nameIdentifiers" => [{"nameIdentifier"=>"https://orcid.org/0000-0003-1419-2405", "nameIdentifierScheme"=>"ORCID"}], + "nameType" => "Personal") + expect(meta["titles"]).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("DataCite") + end + + it "from schema_org url" do + string = "https://doi.pangaea.de/10.1594/PANGAEA.836178" + meta = subject.parse_xml(string) + + expect(meta["string"]).to start_with("{\"@context\":\"http://schema.org") + expect(meta["from"]).to eq("schema_org") + expect(meta["doi"]).to eq("10.1594/pangaea.836178") + expect(meta["creators"].length).to eq(8) + expect(meta["creators"].first).to eq("familyName"=>"Johansson", "givenName"=>"Emma", "name"=>"Johansson, Emma", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Hydrological and meteorological investigations in a lake near Kangerlussuaq, west Greenland"}]) + expect(meta["publication_year"]).to eq("2014") + expect(meta["publisher"]).to eq("PANGAEA") end end - context "set attributes" do - it "author" do - author = { "name" => "Carberry, Josiah"} - subject.author = author - expect(subject.author).to eq(author) + context "update_xml" do + it "from schema 4" do + string = file_fixture('datacite.xml').read + subject = create(:doi, xml: string) + + # TODO + # expect(subject.doi).to eq("10.14454/4k3m-nyvg") + # expect(subject.creators).to eq([{"familyName"=>"Fenner", "givenName"=>"Martin", "id"=>"https://orcid.org/0000-0003-1419-2405", "name"=>"Fenner, Martin", "type"=>"Person"}]) + # expect(subject.titles).to eq([{"title"=>"Eating your own Dog Food"}]) + # expect(subject.publication).to eq("2016") + # expect(meta["publisher"]).to eq("DataCite") + end + + it "from schema 3" do + string = file_fixture('datacite_schema_3.xml').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.5061/dryad.8515") + expect(meta["creators"].length).to eq(8) + expect(meta["creators"].first).to eq("familyName"=>"Ollomo", "givenName"=>"Benjamin", "name"=>"Ollomo, Benjamin", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) + expect(meta["publication_year"]).to eq("2011") + expect(meta["publisher"]).to eq("Dryad Digital Repository") + end + + it "from schema 2.2" do + string = file_fixture('datacite_schema_2.2.xml').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.5072/testpub") + expect(meta["creators"]).to eq([{"familyName"=>"Smith", "givenName"=>"John", "name"=>"Smith, John", "nameType"=>"Personal"}, {"name"=>"つまらないものですが"}]) + expect(meta["titles"]).to eq([{"title"=>"Właściwości rzutowań podprzestrzeniowych"}, {"title"=>"Translation of Polish titles", "titleType"=>"TranslatedTitle"}]) + expect(meta["publication_year"]).to eq("2010") + expect(meta["publisher"]).to eq("Springer") end - it "title" do - title = "Referee report." - subject.title = title - expect(subject.title).to eq(title) + it "from schema 4 missing creators" do + string = file_fixture('datacite_missing_creator.xml').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.5438/4k3m-nyvg") + expect(meta["creators"]).to be_empty + expect(meta["titles"]).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("DataCite") + end + + it "from crossref" do + string = file_fixture('crossref.xml').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.1371/journal.pone.0000030") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Ralser", "givenName"=>"Markus", "name"=>"Ralser, Markus", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes"}]) + expect(meta["publication_year"]).to eq("2006") + expect(meta["publisher"]).to eq("(:unav)") + expect(meta["container"]).to eq("firstPage"=>"e30", "identifier"=>"1932-6203", "identifierType"=>"ISSN", "issue"=>"1", "title"=>"PLoS ONE", "type"=>"Journal", "volume"=>"1") + end + + it "from bibtex" do + string = file_fixture('crossref.bib').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.7554/elife.01567") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Sankar", "givenName"=>"Martial", "name"=>"Sankar, Martial", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(meta["publication_year"]).to eq("2014") + expect(meta["publisher"]).to eq("{eLife} Sciences Organisation, Ltd.") + expect(meta["container"]).to eq("identifier"=>"2050-084X", "identifierType"=>"ISSN", "title"=>"eLife", "type"=>"Journal", "volume"=>"3") end - it "publisher" do - publisher = "Zenodo" - subject.publisher = publisher - expect(subject.publisher).to eq(publisher) + it "from ris" do + string = file_fixture('crossref.ris').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.7554/elife.01567") + expect(meta["creators"].length).to eq(5) + expect(meta["creators"].first).to eq("familyName"=>"Sankar", "givenName"=>"Martial", "name"=>"Sankar, Martial", "nameType"=>"Personal") + expect(meta["titles"]).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(meta["publication_year"]).to eq("2014") + expect(meta["publisher"]).to eq("(:unav)") + expect(meta["container"]).to eq("title"=>"eLife", "type"=>"Journal", "volume"=>"3") end - it "date_published" do - expect(subject.date_published).to eq("2017") + it "from codemeta" do + string = file_fixture('codemeta.json').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.5063/f1m61h5x") + expect(meta["creators"].length).to eq(3) + expect(meta["creators"].first).to eq("affiliation" => "NCEAS", + "familyName" => "Jones", + "givenName" => "Matt", + "name" => "Jones, Matt", + "nameIdentifiers" => [{"nameIdentifier"=>"https://orcid.org/0000-0003-0077-4738", "nameIdentifierScheme"=>"ORCID"}], + "nameType" => "Personal") + expect(meta["titles"]).to eq([{"title"=>"R Interface to the DataONE REST API"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("https://cran.r-project.org") end - it "resource_type_general" do - resource_type_general = "Software" - subject.resource_type_general = resource_type_general - expect(subject.resource_type_general).to eq(resource_type_general) + it "from schema_org" do + string = file_fixture('schema_org.json').read + meta = subject.parse_xml(string) + + expect(meta["doi"]).to eq("10.5438/4k3m-nyvg") + expect(meta["creators"].length).to eq(1) + expect(meta["creators"].first).to eq("familyName"=>"Fenner", "givenName"=>"Martin", "name" => "Fenner, Martin", + "nameIdentifiers" => [{"nameIdentifier"=>"https://orcid.org/0000-0003-1419-2405", "nameIdentifierScheme"=>"ORCID"}], + "nameType" => "Personal") + expect(meta["titles"]).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(meta["publication_year"]).to eq("2016") + expect(meta["publisher"]).to eq("DataCite") end end end diff --git a/spec/factories/default.rb b/spec/factories/default.rb index c096458be..11d3bb357 100644 --- a/spec/factories/default.rb +++ b/spec/factories/default.rb @@ -26,9 +26,146 @@ doi { ("10.14454/" + Faker::Internet.password(8)).downcase } url { Faker::Internet.url } - is_active { true } - xml { "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxyZXNvdXJjZSB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC0zIGh0dHA6Ly9zY2hlbWEuZGF0YWNpdGUub3JnL21ldGEva2VybmVsLTMvbWV0YWRhdGEueHNkIiB4bWxucz0iaHR0cDovL2RhdGFjaXRlLm9yZy9zY2hlbWEva2VybmVsLTMiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPjxpZGVudGlmaWVyIGlkZW50aWZpZXJUeXBlPSJET0kiPjEwLjUyNTYvZjEwMDByZXNlYXJjaC44NTcwLnI2NDIwPC9pZGVudGlmaWVyPjxjcmVhdG9ycz48Y3JlYXRvcj48Y3JlYXRvck5hbWU+ZCBzPC9jcmVhdG9yTmFtZT48L2NyZWF0b3I+PC9jcmVhdG9ycz48dGl0bGVzPjx0aXRsZT5SZWZlcmVlIHJlcG9ydC4gRm9yOiBSRVNFQVJDSC0zNDgyIFt2ZXJzaW9uIDU7IHJlZmVyZWVzOiAxIGFwcHJvdmVkLCAxIGFwcHJvdmVkIHdpdGggcmVzZXJ2YXRpb25zXTwvdGl0bGU+PC90aXRsZXM+PHB1Ymxpc2hlcj5GMTAwMCBSZXNlYXJjaCBMaW1pdGVkPC9wdWJsaXNoZXI+PHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+PHJlc291cmNlVHlwZSByZXNvdXJjZVR5cGVHZW5lcmFsPSJUZXh0Ii8+PC9yZXNvdXJjZT4=" } - aasm_state { "draft" } + types { { + "resourceTypeGeneral": "Dataset", + "resourceType": "DataPackage", + "schemaOrg": "Dataset", + "citeproc": "dataset", + "bibtex": "misc", + "ris": "DATA" + }} + creators { [ + { + "type": "Person", + "name": "Benjamin Ollomo", + "givenName": "Benjamin", + "familyName": "Ollomo" + }, + { + "type": "Person", + "name": "Patrick Durand", + "givenName": "Patrick", + "familyName": "Durand" + }, + { + "type": "Person", + "name": "Franck Prugnolle", + "givenName": "Franck", + "familyName": "Prugnolle" + }, + { + "type": "Person", + "name": "Emmanuel J. P. Douzery", + "givenName": "Emmanuel J. P.", + "familyName": "Douzery" + }, + { + "type": "Person", + "name": "Céline Arnathau", + "givenName": "Céline", + "familyName": "Arnathau" + }, + { + "type": "Person", + "name": "Dieudonné Nkoghe", + "givenName": "Dieudonné", + "familyName": "Nkoghe" + }, + { + "type": "Person", + "name": "Eric Leroy", + "givenName": "Eric", + "familyName": "Leroy" + }, + { + "type": "Person", + "name": "François Renaud", + "givenName": "François", + "familyName": "Renaud" + } + ] } + titles {[ + { + "title": "Data from: A new malaria agent in African hominids." + }] } + publisher {"Dryad Digital Repository" } + subjects {[ + { + "subject": "Phylogeny" + }, + { + "subject": "Malaria" + }, + { + "subject": "Parasites" + }, + { + "subject": "Taxonomy" + }, + { + "subject": "Mitochondrial genome" + }, + { + "subject": "Africa" + }, + { + "subject": "Plasmodium" + } + ]} + dates { [ + { + "date": "2011", + "dateType": "Issued" + } + ]} + publication_year { 2011 } + identifiers { [ + { + "identifierType": "citation", + "identifier": "Ollomo B, Durand P, Prugnolle F, Douzery EJP, Arnathau C, Nkoghe D, Leroy E, Renaud F (2009) A new malaria agent in African hominids. PLoS Pathogens 5(5): e1000446." + } + ]} + version { "1" } + rights_list {[ + { + "rightsUri": "http://creativecommons.org/publicdomain/zero/1.0" + } + ]} + related_identifiers {[ + { + "relatedIdentifier": "10.5061/dryad.8515/1", + "relatedIdentifierType": "DOI", + "relationType": "HasPart" + }, + { + "relatedIdentifier": "10.5061/dryad.8515/2", + "relatedIdentifierType": "DOI", + "relationType": "HasPart" + }, + { + "relatedIdentifier": "10.1371/journal.ppat.1000446", + "relatedIdentifierType": "DOI", + "relationType": "IsReferencedBy" + }, + { + "relatedIdentifier": "10.1371/journal.ppat.1000446", + "relatedIdentifierType": "DOI", + "relationType": "IsSupplementTo" + }, + { + "relatedIdentifier": "19478877", + "relatedIdentifierType": "PMID", + "relationType": "IsReferencedBy" + }, + { + "relatedIdentifier": "19478877", + "relatedIdentifierType": "PMID", + "relationType": "IsSupplementTo" + } + ]} + schema_version { "http://datacite.org/schema/kernel-4" } + source { "test" } + regenerate { true } created { Faker::Time.backward(14, :evening) } minted { Faker::Time.backward(15, :evening) } updated { Faker::Time.backward(5, :evening) } diff --git a/spec/fixtures/files/crosscite.json b/spec/fixtures/files/crosscite.json index e724fd3f2..7447ae152 100644 --- a/spec/fixtures/files/crosscite.json +++ b/spec/fixtures/files/crosscite.json @@ -1,39 +1,62 @@ { "id": "https://doi.org/10.5281/zenodo.48440", "doi": "10.5281/zenodo.48440", - "type": "SoftwareSourceCode", - "additional_type": "Software", - "citeproc_type": "other", - "bibtex_type": "misc", - "ris_type": "COMP", - "resource_type_general": "Software", - "resource_type": "Software", - "author": { + "types":{ + "resourceTypeGeneral": "Software", + "resourceType": "Software", + "schemaOrg": "SoftwareSourceCode", + "citeproc": "other", + "bibtex": "misc", + "ris": "COMP" + }, + "creators": [{ "type": "Person", "name": "Kristian Garza", "givenName": "Kristian", "familyName": "Garza" - }, - "title": "Analysis Tools for Crossover Experiment of UI using Choice Architecture", + }], + "titles": [{ + "title": "Analysis Tools for Crossover Experiment of UI using Choice Architecture" + }], "publisher": "Zenodo", - "keywords": ["choice architecture", "crossover experiment", "hci"], - "date_published": "2016-03-27", - "alternate_name": { - "type": "URL", - "name": "http://zenodo.org/record/48440" + "subjects": [ + { + "subject": "choice architecture" + }, + { + "subject": "crossover experiment" + }, + { + "subject": "hci" + } + ], + "dates": { + "date": "2016-03-27", + "dateType": "Issued" }, - "license": [{ - "name": "Open Access" + "publication_year": "2016", + "identifiers": [ + { + "identifierType": "URL", + "identifier": "http://zenodo.org/record/48440" + }, + { + "identifierType": "DOI", + "identifier": "https://doi.org/10.5281/zenodo.48440" + } + ], + "rights_list": [{ + "rights": "Open Access" }, { - "id": "https://creativecommons.org/licenses/by-nc-sa/4.0", - "name": "Creative Commons Attribution-NonCommercial-ShareAlike" + "rightsUri": "https://creativecommons.org/licenses/by-nc-sa/4.0", + "rights": "Creative Commons Attribution-NonCommercial-ShareAlike" } ], - "description": { - "type": "Abstract", - "text": "This tools are used to analyse the data produced by the Crosssover Experiment I designed to test Choice Architecture techniques as UI interventions in a SEEk4Science data catalogue. It contains:\n\n- Data structures for the experimental data.
\n- Visualisation functions
\n- Analysis functions\n\n## Installation\n\n- R
\n- python
\n- ipython 4\n\nClone and use.\n\n## Usage\n\n
\n```python
\nsource('parallel_plot.r')
\nwith(z, parallelset(trt,response, freq=count, alpha=0.2))
\n```\n\n
\n## Contributing\n\n1. Fork it!
\n2. Create your feature branch: `git checkout -b my-new-feature`
\n3. Commit your changes: `git commit -am 'Add some feature'`
\n4. Push to the branch: `git push origin my-new-feature`
\n5. Submit a pull request :D\n\n
\n## License\n\nThis work supports my PhD Thesis at University of Manchester." - }, + "descriptions": [{ + "descriptionType": "Abstract", + "description": "This tools are used to analyse the data produced by the Crosssover Experiment I designed to test Choice Architecture techniques as UI interventions in a SEEk4Science data catalogue. It contains:\n\n- Data structures for the experimental data.
\n- Visualisation functions
\n- Analysis functions\n\n## Installation\n\n- R
\n- python
\n- ipython 4\n\nClone and use.\n\n## Usage\n\n
\n```python
\nsource('parallel_plot.r')
\nwith(z, parallelset(trt,response, freq=count, alpha=0.2))
\n```\n\n
\n## Contributing\n\n1. Fork it!
\n2. Create your feature branch: `git checkout -b my-new-feature`
\n3. Commit your changes: `git commit -am 'Add some feature'`
\n4. Push to the branch: `git push origin my-new-feature`
\n5. Submit a pull request :D\n\n
\n## License\n\nThis work supports my PhD Thesis at University of Manchester." + }], "schema_version": "http://datacite.org/schema/kernel-4", "provider": "DataCite", "state": "findable" diff --git a/spec/fixtures/files/datacite.json b/spec/fixtures/files/datacite.json new file mode 100644 index 000000000..cd97f822b --- /dev/null +++ b/spec/fixtures/files/datacite.json @@ -0,0 +1,79 @@ +{ + "id": "https://doi.org/10.5438/4k3m-nyvg", + "types": { + "resourceTypeGeneral": "Text", + "resourceType": "BlogPosting", + "schemaOrg": "ScholarlyArticle", + "citeproc": "article-journal", + "bibtex": "article", + "ris": "RPRT" + }, + "doi": "10.5438/4K3M-NYVG", + "creators": [{ + "type": "Person", + "id": "http://orcid.org/0000-0003-1419-2405", + "name": "Fenner, Martin", + "givenName": "Martin", + "familyName": "Fenner" + }], + "titles": [{ + "title": "Eating your own Dog Food" + }], + "publisher": "DataCite", + "publicationYear": "2016", + "subjects": [ + { + "subject": "datacite" + }, + { + "subject": "doi" + }, + { + "subject": "metadata" + } + ], + "dates": [ + { + "dateType": "Created", + "date": "2016-12-20" + }, + { + "dateType": "Issued", + "date": "2016-12-20" + }, + { + "dateType": "Updated", + "date": "2016-12-20" + } + ], + "alternateIdentifiers": [ + { + "alternateIdentifierType": "Local accession number", + "alternateIdentifier": "MS-49-3632-5083" + } + ], + "relatedIdentifiers": [ + { + "relatedIdentifier": "10.5438/0000-00ss", + "relatedIdentifierType": "DOI", + "relationType": "IsPartOf" + }, + { + "relatedIdentifier": "10.5438/0012", + "relatedIdentifierType": "DOI", + "relationType": "References" + }, + { + "relatedIdentifier": "10.5438/55e5-t5c0", + "relatedIdentifierType": "DOI", + "relationType": "References" + } + ], + "version": "1.0", + "descriptions": [{ + "descriptionType": "Abstract", + "description": "Eating your own dog food is a slang term to describe that an organization should itself use the products and services it provides. For DataCite this means that we should use DOIs with appropriate metadata and strategies for long-term preservation for..." + }], + "schemaVersion": "http://datacite.org/schema/kernel-4", + "provider": "DataCite" +} diff --git a/spec/fixtures/files/datacite.xml b/spec/fixtures/files/datacite.xml index df0eb65ac..459fe40e2 100644 --- a/spec/fixtures/files/datacite.xml +++ b/spec/fixtures/files/datacite.xml @@ -1,6 +1,6 @@ - 10.5438/4K3M-NYVG + 10.14454/4K3M-NYVG Fenner, Martin @@ -37,4 +37,4 @@ Eating your own dog food is a slang term to describe that an organization should itself use the products and services it provides. For DataCite this means that we should use DOIs with appropriate metadata and strategies for long-term preservation for... - + \ No newline at end of file diff --git a/spec/fixtures/files/datacite_89.json b/spec/fixtures/files/datacite_89.json index 6c348c930..a75d2fd63 100644 --- a/spec/fixtures/files/datacite_89.json +++ b/spec/fixtures/files/datacite_89.json @@ -2,21 +2,9 @@ { "type": "dois", "attributes": { - "doi": "10.24425/119496", + "doi": "10.14454/119496", "xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48cmVzb3VyY2UgeG1sbnM9Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC00IiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC00IGh0dHA6Ly9zY2hlbWEuZGF0YWNpdGUub3JnL21ldGEva2VybmVsLTQuMS9tZXRhZGF0YS54c2QiPjxpZGVudGlmaWVyIGlkZW50aWZpZXJUeXBlPSJET0kiPjEwLjI0NDI1LzExOTQ5NjwvaWRlbnRpZmllcj48Y3JlYXRvcnM+PGNyZWF0b3I+PGNyZWF0b3JOYW1lPig6dW5hdik8L2NyZWF0b3JOYW1lPjwvY3JlYXRvcj48L2NyZWF0b3JzPjx0aXRsZXM+PHRpdGxlPjMyPC90aXRsZT48L3RpdGxlcz48cHVibGlzaGVyPkNvbW1pdHRlZSBmb3IgUHN5Y2hvbG9naWNhbCBTY2llbmNlIFBBUzwvcHVibGlzaGVyPjxwdWJsaWNhdGlvblllYXI+MjAxODwvcHVibGljYXRpb25ZZWFyPjxyZXNvdXJjZVR5cGUgcmVzb3VyY2VUeXBlR2VuZXJhbD0iVGV4dCI+QXJ0eWt1xYI8L3Jlc291cmNlVHlwZT48ZGF0ZXM+PGRhdGUgZGF0ZVR5cGU9Iklzc3VlZCI+MjAxODwvZGF0ZT48L2RhdGVzPjwvcmVzb3VyY2U+", - "validate": "true" - }, - "relationships": { - "client": { - "data": { - "type": "clients", - "id": "PSNC.PAN-CC" - } - } + "event": "register" } - }, - "controller": "dois", - "action": "update", - "id": "10.24425/119496", - "format": "jsonapi" + } } \ No newline at end of file diff --git a/spec/fixtures/files/datacite_f1000.xml b/spec/fixtures/files/datacite_f1000.xml new file mode 100644 index 000000000..cf83f9c4d --- /dev/null +++ b/spec/fixtures/files/datacite_f1000.xml @@ -0,0 +1 @@ +10.5256/f1000research.8570.r6420d sReferee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]F1000 Research Limited2017 \ No newline at end of file diff --git a/spec/fixtures/files/datacite_with_whitespace.xml b/spec/fixtures/files/datacite_with_whitespace.xml new file mode 100644 index 000000000..7718668e7 --- /dev/null +++ b/spec/fixtures/files/datacite_with_whitespace.xml @@ -0,0 +1,133 @@ + + + 10.20387/BONARES-R0P3-X8GN + + + Schucknecht, Anne + Anne + Schucknecht + Karlsruhe Institute für Technologie, Institut für Meteorologie und Klimafor-schung, IMK-IFU, Garmisch-Partenkirchen, Deutschland + + + Schneider, Katrin + Katrin + Schneider + Karlsruhe Institute für Technologie, Institut für Meteorologie und Klimafor-schung, IMK-IFU, Garmisch-Partenkirchen, Deutschland + + + Kiese, Ralf + Ralf + Kiese + Karlsruhe Institute für Technologie, Institut für Meteorologie und Klimafor-schung, IMK-IFU, Garmisch-Partenkirchen, Deutschland + + + Wiesmeier, Martin + Martin + Wiesmeier + Technische Universität München, Lehrstuhl für Bodenkunde, Freising, Deutschland; Bayrische Landesanstalt für Landwirtschaft, Institut für Ökologischen Landbau, Bodenkultur und Ressourcenschutz, Freising, Deutschland + + + Schloter, Michael + Michael + Schloter + Technische Universität München, Lehrstuhl für Bodenkunde, Freising, Deutschland; Helmholtz Zentrum München, Abteilung für vergleichende Mikrobiom-analysen, Neuherberg, Deutschland + + + Jentsch, Anke + Anke + Jentsch + Universität Bayreuth, Professur für Störungsökologie, Bayreuth, Deutschland + + + Köllner, Thomas + Thomas + Köllner + Universität Bayreuth, Professur für ökologische Dienstleistungen, Bayreuth, Deutschland + + + Dannenmann, Michael + Michael + Dannenmann + Karlsruher Institut für Technologie (KIT) – Campus Alpin Institut für Meteorologie und Klimaforschung, Atmosphärische Umweltforschung (IMK-IFU) + + + + SUSALPS Conference 2018 – Book of Abstracts: Montane and alpine grasslands under climate change – ways in a sustainable future + + BonaRes Centre for Soil Research + 2018 + + BonaRes + montane and alpine grasslands + soil organic matter + microbiome + plant diversity and productivity + biogeochemical cycles + remote sensing + ecosystem services + alpine farming + + + + Kiese, Ralf + Ralf + Kiese + Karlsruhe Institute für Technologie, Institut für Meteorologie und Klimafor-schung, IMK-IFU, Garmisch-Partenkirchen, Deutschland + + + + + + +2018-11-23 + + en + Document + + 1270 + + + PDF + + + Creative Commons Attribution 4.0 International + + + +From 18 to 20 September 2018, the SUSALPS Conference "Montane and alpine grasslands under climate change – ways in a sustainable future" was held in Garmisch-Partenkirchen, Germany. More than 60 participants from nine nations attended the conference. The event covered a broad scope and offered the opportunity to discuss both fundamental research and practical approaches in grassland management. At the conference's closing day, ex-cursions took place to the SUSALPS experimental areas in Fendt and on the Brunnenkopfalm. This publication provides the abstracts of all oral and poster presentations. + + + + German Federal Ministry of Education and Research + https://doi.org/10.13039/501100002347 + 031B0027A + Sustainable use of alpine and pre-alpine grassland soils in a changing climate - Subproject A + + + Federal Ministry of Education and Research + https://doi.org/10.13039/501100002347 + 031B0027B + Sustainable use of alpine and pre-alpine grassland soils in a changing climate - Subproject B + + + Federal Ministry of Education and Research + https://doi.org/10.13039/501100002347 + 031B0027C + Sustainable use of alpine and pre-alpine grassland soils in a changing climate - Subproject C + + + Federal Ministry of Education and Research + https://doi.org/10.13039/501100002347 + 031B0027D + Sustainable use of alpine and pre-alpine grassland soils in a changing climate - Subproject D + + + Federal Ministry of Education and Research + https://doi.org/10.13039/501100002347 + 031B0027E + Sustainable use of alpine and pre-alpine grassland soils in a changing climate - Subproject E + + + \ No newline at end of file diff --git a/spec/fixtures/files/schema_org_topmed.json b/spec/fixtures/files/schema_org_topmed.json index cfa6feb27..603cf85de 100644 --- a/spec/fixtures/files/schema_org_topmed.json +++ b/spec/fixtures/files/schema_org_topmed.json @@ -42,7 +42,7 @@ "@type": "Dataset", "@id": "https://doi.org/10.23725/2g4s-qv04" }, - "funding": { + "funder": { "@type": "Organization", "@id": "https://doi.org/10.13039/100000050", "name": "National Heart, Lung, and Blood Institute (NHLBI)" diff --git a/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_crossref_url.yml b/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_crossref_url.yml new file mode 100644 index 000000000..d72b8bac2 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_crossref_url.yml @@ -0,0 +1,82 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7554 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Sat, 01 Dec 2018 14:44:43 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - 0bcab827-0d7e-44ab-abca-fe09a72ba2cf + Etag: + - W/"381993dc8a6b0f20960c96a3639c0284" + X-Runtime: + - '0.169950' + X-Powered-By: + - Phusion Passenger 5.3.7 + Server: + - nginx/1.14.0 + Phusion Passenger 5.3.7 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7554","type":"prefixes","attributes":{"registration-agency":"Crossref","created":null,"updated":"2016-09-21T21:07:27Z"},"relationships":{"clients":{"data":[]},"providers":{"data":[]}}},"included":[]}' + http_version: + recorded_at: Sat, 01 Dec 2018 14:44:43 GMT +- request: + method: get + uri: http://www.crossref.org/openurl/?format=unixref&id=doi:10.7554/elife.01567&noredirect=true&pid=tech@datacite.org + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/xml + response: + status: + code: 200 + message: OK + headers: + Server: + - Apache-Coyote/1.1 + Crossref-Deployment-Name: + - qs2-2 + Content-Type: + - text/xml;charset=UTF-8 + Content-Language: + - en-US + Date: + - Sat, 01 Dec 2018 14:44:43 GMT + Connection: + - close + body: + encoding: ASCII-8BIT + string: !binary |- + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGRvaV9yZWNvcmRzPg0KICA8ZG9pX3JlY29yZCBvd25lcj0iMTAuNzU1NCIgdGltZXN0YW1wPSIyMDE4LTA4LTIzIDA5OjQxOjQ5Ij4NCiAgICA8Y3Jvc3NyZWY+DQogICAgICA8am91cm5hbD4NCiAgICAgICAgPGpvdXJuYWxfbWV0YWRhdGEgbGFuZ3VhZ2U9ImVuIj4NCiAgICAgICAgICA8ZnVsbF90aXRsZT5lTGlmZTwvZnVsbF90aXRsZT4NCiAgICAgICAgICA8aXNzbiBtZWRpYV90eXBlPSJlbGVjdHJvbmljIj4yMDUwLTA4NFg8L2lzc24+DQogICAgICAgIDwvam91cm5hbF9tZXRhZGF0YT4NCiAgICAgICAgPGpvdXJuYWxfaXNzdWU+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8am91cm5hbF92b2x1bWU+DQogICAgICAgICAgICA8dm9sdW1lPjM8L3ZvbHVtZT4NCiAgICAgICAgICA8L2pvdXJuYWxfdm9sdW1lPg0KICAgICAgICA8L2pvdXJuYWxfaXNzdWU+DQogICAgICAgIDxqb3VybmFsX2FydGljbGUgcHVibGljYXRpb25fdHlwZT0iZnVsbF90ZXh0IiByZWZlcmVuY2VfZGlzdHJpYnV0aW9uX29wdHM9ImFueSI+DQogICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgIDx0aXRsZT5BdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvdGl0bGU+DQogICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgPGNvbnRyaWJ1dG9ycz4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJmaXJzdCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPk1hcnRpYWw8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlNhbmthcjwvc3VybmFtZT4NCiAgICAgICAgICAgICAgPGFmZmlsaWF0aW9uPkRlcGFydG1lbnQgb2YgUGxhbnQgTW9sZWN1bGFyIEJpb2xvZ3ksIFVuaXZlcnNpdHkgb2YgTGF1c2FubmUsIExhdXNhbm5lLCBTd2l0emVybGFuZDwvYWZmaWxpYXRpb24+DQogICAgICAgICAgICA8L3BlcnNvbl9uYW1lPg0KICAgICAgICAgICAgPHBlcnNvbl9uYW1lIGNvbnRyaWJ1dG9yX3JvbGU9ImF1dGhvciIgc2VxdWVuY2U9ImFkZGl0aW9uYWwiPg0KICAgICAgICAgICAgICA8Z2l2ZW5fbmFtZT5LYWlzYTwvZ2l2ZW5fbmFtZT4NCiAgICAgICAgICAgICAgPHN1cm5hbWU+TmllbWluZW48L3N1cm5hbWU+DQogICAgICAgICAgICAgIDxhZmZpbGlhdGlvbj5EZXBhcnRtZW50IG9mIFBsYW50IE1vbGVjdWxhciBCaW9sb2d5LCBVbml2ZXJzaXR5IG9mIExhdXNhbm5lLCBMYXVzYW5uZSwgU3dpdHplcmxhbmQ8L2FmZmlsaWF0aW9uPg0KICAgICAgICAgICAgPC9wZXJzb25fbmFtZT4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJhZGRpdGlvbmFsIj4NCiAgICAgICAgICAgICAgPGdpdmVuX25hbWU+TGF1cmE8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlJhZ25pPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPklvYW5uaXM8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlhlbmFyaW9zPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+Vml0YWwtSVQsIFN3aXNzIEluc3RpdHV0ZSBvZiBCaW9pbmZvcm1hdGljcywgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPkNocmlzdGlhbiBTPC9naXZlbl9uYW1lPg0KICAgICAgICAgICAgICA8c3VybmFtZT5IYXJkdGtlPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgPC9jb250cmlidXRvcnM+DQogICAgICAgICAgPGFic3RyYWN0Pg0KICAgICAgICAgICAgPHA+QW1vbmcgdmFyaW91cyBhZHZhbnRhZ2VzLCB0aGVpciBzbWFsbCBzaXplIG1ha2VzIG1vZGVsIG9yZ2FuaXNtcyBwcmVmZXJyZWQgc3ViamVjdHMgb2YgaW52ZXN0aWdhdGlvbi4gWWV0LCBldmVuIGluIG1vZGVsIHN5c3RlbXMgZGV0YWlsZWQgYW5hbHlzaXMgb2YgbnVtZXJvdXMgZGV2ZWxvcG1lbnRhbCBwcm9jZXNzZXMgYXQgY2VsbHVsYXIgbGV2ZWwgaXMgc2V2ZXJlbHkgaGFtcGVyZWQgYnkgdGhlaXIgc2NhbGUuIEZvciBpbnN0YW5jZSwgc2Vjb25kYXJ5IGdyb3d0aCBvZiBBcmFiaWRvcHNpcyBoeXBvY290eWxzIGNyZWF0ZXMgYSByYWRpYWwgcGF0dGVybiBvZiBoaWdobHkgc3BlY2lhbGl6ZWQgdGlzc3VlcyB0aGF0IGNvbXByaXNlcyBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHN0YXJ0aW5nIGZyb20gYSBmZXcgZG96ZW4uIFRoaXMgZHluYW1pYyBwcm9jZXNzIGlzIGRpZmZpY3VsdCB0byBmb2xsb3cgYmVjYXVzZSBvZiBpdHMgc2NhbGUgYW5kIGJlY2F1c2UgaXQgY2FuIG9ubHkgYmUgaW52ZXN0aWdhdGVkIGludmFzaXZlbHksIHByZWNsdWRpbmcgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBjZWxsIHByb2xpZmVyYXRpb24sIGRpZmZlcmVudGlhdGlvbiwgYW5kIHBhdHRlcm5pbmcgZXZlbnRzIGludm9sdmVkLiBUbyBvdmVyY29tZSBzdWNoIGxpbWl0YXRpb24sIHdlIGVzdGFibGlzaGVkIGFuIGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d5IGFwcHJvYWNoLiBXZSBhY3F1aXJlZCBoeXBvY290eWwgY3Jvc3Mtc2VjdGlvbnMgZnJvbSB0aWxlZCBoaWdoLXJlc29sdXRpb24gaW1hZ2VzIGFuZCBleHRyYWN0ZWQgdGhlaXIgaW5mb3JtYXRpb24gY29udGVudCB1c2luZyBjdXN0b20gaGlnaC10aHJvdWdocHV0IGltYWdlIHByb2Nlc3NpbmcgYW5kIHNlZ21lbnRhdGlvbi4gQ291cGxlZCB3aXRoIGF1dG9tYXRlZCBjZWxsIHR5cGUgcmVjb2duaXRpb24gdGhyb3VnaCBtYWNoaW5lIGxlYXJuaW5nLCB3ZSBjb3VsZCBlc3RhYmxpc2ggYSBjZWxsdWxhciByZXNvbHV0aW9uIGF0bGFzIHRoYXQgcmV2ZWFscyB2YXNjdWxhciBtb3JwaG9keW5hbWljcyBkdXJpbmcgc2Vjb25kYXJ5IGdyb3d0aCwgZm9yIGV4YW1wbGUgZXF1aWRpc3RhbnQgcGhsb2VtIHBvbGUgZm9ybWF0aW9uLjwvcD4NCiAgICAgICAgICA8L2Fic3RyYWN0Pg0KICAgICAgICAgIDxhYnN0cmFjdCBhYnN0cmFjdC10eXBlPSJleGVjdXRpdmUtc3VtbWFyeSI+DQogICAgICAgICAgICA8cD5PdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgbGl2aW5nIHdvcmxkIGhhcyBiZWVuIGFkdmFuY2VkIGdyZWF0bHkgYnkgc3R1ZGllcyBvZiDigJhtb2RlbCBvcmdhbmlzbXPigJksIHN1Y2ggYXMgbWljZSwgemVicmFmaXNoLCBhbmQgZnJ1aXQgZmxpZXMuIFN0dWR5aW5nIHRoZXNlIGNyZWF0dXJlcyBoYXMgYmVlbiBjcnVjaWFsIHRvIHVuY292ZXJpbmcgdGhlIGdlbmVzIHRoYXQgY29udHJvbCBob3cgb3VyIGJvZGllcyBkZXZlbG9wIGFuZCBncm93LCBhbmQgYWxzbyB0byBkaXNjb3ZlciB0aGUgZ2VuZXRpYyBiYXNpcyBvZiBkaXNlYXNlcyBzdWNoIGFzIGNhbmNlci48L3A+DQogICAgICAgICAgICA8cD5UaGFsZSBjcmVzc+KAlG9yIEFyYWJpZG9wc2lzIHRoYWxpYW5hIHRvIGdpdmUgaXRzIGZvcm1hbCBuYW1l4oCUaXMgdGhlIG1vZGVsIG9yZ2FuaXNtIG9mIGNob2ljZSBmb3IgbWFueSBwbGFudCBiaW9sb2dpc3RzLiBUaGlzIHRpbnkgd2VlZCBoYXMgYmVlbiB3aWRlbHkgc3R1ZGllZCBiZWNhdXNlIGl0IGNhbiBjb21wbGV0ZSBpdHMgbGlmZWN5Y2xlLCBmcm9tIHNlZWQgdG8gc2VlZCwgaW4gYWJvdXQgNiB3ZWVrcywgYW5kIGJlY2F1c2UgaXRzIHJlbGF0aXZlbHkgc21hbGwgZ2Vub21lIHNpbXBsaWZpZXMgdGhlIHNlYXJjaCBmb3IgZ2VuZXMgdGhhdCBjb250cm9sIHNwZWNpZmljIHRyYWl0cy4gSG93ZXZlciwgYXMgd2l0aCBvdGhlciBtdWNoLXN0dWRpZWQgbW9kZWwgc3lzdGVtcywgdW5kZXJzdGFuZGluZyB0aGUgY2hhbmdlcyB0aGF0IHVuZGVycGluIHRoZSBkZXZlbG9wbWVudCBvZiBzb21lIG9mIHRoZSBtb3JlIGNvbXBsZXggdGlzc3VlcyBpbiBBcmFiaWRvcHNpcyBoYXMgYmVlbiBzZXZlcmVseSBoYW1wZXJlZCBieSB0aGUgc2hlYXIgbnVtYmVyIG9mIGNlbGxzIGludm9sdmVkLjwvcD4NCiAgICAgICAgICAgIDxwPkFmdGVyIGl0IGhhcyBlbWVyZ2VkIGZyb20gdGhlIHNlZWQsIHRoZSBwbGFudOKAmXMgZmlyc3Qgc3RlbSB3aWxsIGRldmVsb3AgZnJvbSBhIGZldyBkb3plbiBjZWxscyBpbiB3aWR0aCB0byBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHdpdGggaGlnaGx5IHNwZWNpYWxpemVkIHRpc3N1ZXMgYXJyYW5nZWQgaW4gYSBjb21wbGV4IHBhdHRlcm4gb2YgY29uY2VudHJpYyBjaXJjbGVzLiBBbHRob3VnaCB0aGlzIHN0ZW0gdGhpY2tlbmluZyBwcm9jZXNzIHJlcHJlc2VudHMgYSBtYWpvciBkZXZlbG9wbWVudGFsIGNoYW5nZSBpbiBtYW55IHBsYW50c+KAlGZyb20gQXJhYmlkb3BzaXMgdG8gb2FrIHRyZWVz4oCUaXQgaGFzIGJlZW4gdW5kZXItcmVzZWFyY2hlZC4gVGhpcyBpcyBwYXJ0bHkgYmVjYXVzZSBpdCBpbnZvbHZlcyBzbyBtYW55IGRpZmZlcmVudCBjZWxscywgYW5kIGFsc28gYmVjYXVzZSBpdCBjYW4gb25seSBiZSBvYnNlcnZlZCBpbiB0aGluIHNlY3Rpb25zIGN1dCBvdXQgb2YgdGhlIHBsYW504oCZcyBzdGVtLjwvcD4NCiAgICAgICAgICAgIDxwPk5vdyBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gaGF2ZSBkZXZlbG9wZWQgYSBub3ZlbCBhcHByb2FjaCwgdGVybWVkIOKAmGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d54oCZLCB0byBvdmVyY29tZSB0aGVzZSBwcm9ibGVtcy4gVGhpcyBzdHJhdGVneSBpbnZvbHZlcyDigJh0ZWFjaGluZ+KAmSBhIGNvbXB1dGVyIHRvIGF1dG9tYXRpY2FsbHkgcmVjb2duaXplIGRpZmZlcmVudCBwbGFudCBjZWxscyBhbmQgdG8gbWVhc3VyZSB0aGVpciBpbXBvcnRhbnQgZmVhdHVyZXMgaW4gaGlnaC1yZXNvbHV0aW9uIGltYWdlcyBvZiB0aXNzdWUgc2VjdGlvbnMuIFRoZSByZXN1bHRpbmcg4oCYbWFw4oCZIG9mIHRoZSBkZXZlbG9waW5nIHN0ZW3igJR3aGljaCByZXF1aXJlZCBvdmVyIDgwMCBociBvZiBjb21wdXRpbmcgdGltZSB0byBjb21wbGV0ZeKAlHJldmVhbHMgdGhlIGNoYW5nZXMgdG8gY2VsbHMgYW5kIHRpc3N1ZXMgYXMgdGhleSBkZXZlbG9wIHRoYXQgYWxsb3cgdGhlIHRyYW5zcG9ydCBvZiB3YXRlciwgc3VnYXJzIGFuZCBudXRyaWVudHMgYmV0d2VlbiB0aGUgYWJvdmUtIGFuZCBiZWxvdy1ncm91bmQgb3JnYW5zLiBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gc3VnZ2VzdCB0aGF0IHRoZWlyIG5vdmVsIGFwcHJvYWNoIGNvdWxkLCBpbiB0aGUgZnV0dXJlLCBhbHNvIGJlIGFwcGxpZWQgdG8gc3R1ZHkgdGhlIGRldmVsb3BtZW50IG9mIG90aGVyIHRpc3N1ZXMgYW5kIG9yZ2FuaXNtcywgaW5jbHVkaW5nIGFuaW1hbHMuPC9wPg0KICAgICAgICAgIDwvYWJzdHJhY3Q+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8cHVibGlzaGVyX2l0ZW0+DQogICAgICAgICAgICA8aXRlbV9udW1iZXIgaXRlbV9udW1iZXJfdHlwZT0iYXJ0aWNsZV9udW1iZXIiPmUwMTU2NzwvaXRlbV9udW1iZXI+DQogICAgICAgICAgICA8aWRlbnRpZmllciBpZF90eXBlPSJkb2kiPjEwLjc1NTQvZUxpZmUuMDE1Njc8L2lkZW50aWZpZXI+DQogICAgICAgICAgPC9wdWJsaXNoZXJfaXRlbT4NCiAgICAgICAgICA8cHJvZ3JhbSBuYW1lPSJmdW5kcmVmIj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5FTUJPIGxvbmd0ZXJtIHBvc3QtZG9jdG9yYWwgZmVsbG93c2hpcHM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5NYXJpZSBIZWltLVZvZWd0bGluPC9hc3NlcnRpb24+DQogICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+DQogICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX2lkZW50aWZpZXIiIHByb3ZpZGVyPSJjcm9zc3JlZiI+NTAxMTAwMDA2MzkwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxwcm9ncmFtIG5hbWU9IkFjY2Vzc0luZGljYXRvcnMiPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0iYW0iPmh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzMuMC88L2xpY2Vuc2VfcmVmPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InRkbSI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxjcm9zc21hcms+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3ZlcnNpb24+MTwvY3Jvc3NtYXJrX3ZlcnNpb24+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3BvbGljeT5lTGlmZXNjaWVuY2VzPC9jcm9zc21hcmtfcG9saWN5Pg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgICA8Y3Jvc3NtYXJrX2RvbWFpbj4NCiAgICAgICAgICAgICAgICA8ZG9tYWluPnd3dy5lbGlmZXNjaWVuY2VzLm9yZzwvZG9tYWluPg0KICAgICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW4+DQogICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5fZXhjbHVzaXZlPmZhbHNlPC9jcm9zc21hcmtfZG9tYWluX2V4Y2x1c2l2ZT4NCiAgICAgICAgICAgIDxjdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icmVjZWl2ZWQiIGxhYmVsPSJSZWNlaXZlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIwIj4yMDEzLTA5LTIwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iYWNjZXB0ZWQiIGxhYmVsPSJBY2NlcHRlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIxIj4yMDEzLTEyLTI0PC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icHVibGlzaGVkIiBsYWJlbD0iUHVibGlzaGVkIiBncm91cF9uYW1lPSJwdWJsaWNhdGlvbl9oaXN0b3J5IiBncm91cF9sYWJlbD0iUHVibGljYXRpb24gSGlzdG9yeSIgb3JkZXI9IjIiPjIwMTQtMDItMTE8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iZnVuZHJlZiI+DQogICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgRU1CTw0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIj5odHRwOi8vZHguZG9pLm9yZy8xMC4xMzAzOS81MDExMDAwMDMwNDM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX25hbWUiPg0KICAgICAgICAgICAgICAgICAgICBTd2lzcyBOYXRpb25hbCBTY2llbmNlIEZvdW5kYXRpb24NCiAgICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfaWRlbnRpZmllciI+aHR0cDovL2R4LmRvaS5vcmcvMTAuMTMwMzkvNTAxMTAwMDAxNzExPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIiBwcm92aWRlcj0iY3Jvc3NyZWYiPmh0dHA6Ly9keC5kb2kub3JnLzEwLjEzMDM5LzUwMTEwMDAwNjM5MDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iQWNjZXNzSW5kaWNhdG9ycyI+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89ImFtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0idGRtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgICAgPC9jdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgPC9jcm9zc21hcms+DQogICAgICAgICAgPHByb2dyYW0+DQogICAgICAgICAgICA8cmVsYXRlZF9pdGVtPg0KICAgICAgICAgICAgICA8ZGVzY3JpcHRpb24+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvZGVzY3JpcHRpb24+DQogICAgICAgICAgICAgIDxpbnRlcl93b3JrX3JlbGF0aW9uIGlkZW50aWZpZXItdHlwZT0iZG9pIiByZWxhdGlvbnNoaXAtdHlwZT0iaXNTdXBwbGVtZW50ZWRCeSI+MTAuNTA2MS9kcnlhZC5iODM1azwvaW50ZXJfd29ya19yZWxhdGlvbj4NCiAgICAgICAgICAgIDwvcmVsYXRlZF9pdGVtPg0KICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICA8YXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgICA8YXJjaGl2ZSBuYW1lPSJDTE9DS1NTIiAvPg0KICAgICAgICAgIDwvYXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3PC9kb2k+DQogICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NzwvcmVzb3VyY2U+DQogICAgICAgICAgICA8Y29sbGVjdGlvbiBwcm9wZXJ0eT0idGV4dC1taW5pbmciPg0KICAgICAgICAgICAgICA8aXRlbT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2UgbWltZV90eXBlPSJhcHBsaWNhdGlvbi9wZGYiPmh0dHBzOi8vY2RuLmVsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2VsaWZlLTAxNTY3LXYxLnBkZjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvaXRlbT4NCiAgICAgICAgICAgICAgPGl0ZW0+DQogICAgICAgICAgICAgICAgPHJlc291cmNlIG1pbWVfdHlwZT0iYXBwbGljYXRpb24veG1sIj5odHRwczovL2Nkbi5lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9lbGlmZS0wMTU2Ny12MS54bWw8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2l0ZW0+DQogICAgICAgICAgICA8L2NvbGxlY3Rpb24+DQogICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICA8Y2l0YXRpb25fbGlzdD4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmU8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Qm9ua2U8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT40MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTgxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkFQTCByZWd1bGF0ZXMgdmFzY3VsYXIgdGlzc3VlIGlkZW50aXR5IGluIEFyYWJpZG9wc2lzPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmF0dXJlMDIxMDA8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+R2VuZXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+QnJlbm5lcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Mjwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40MTM8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA5PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+SW4gdGhlIGJlZ2lubmluZyB3YXMgdGhlIHdvcm08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTUzNC9nZW5ldGljcy4xMDkuMTA0OTc2PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPlBoeXNpb2xvZ2lhIFBsYW50YXJ1bTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5DaGFmZmV5PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjU5NDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TZWNvbmRhcnkgeHlsZW0gZGV2ZWxvcG1lbnQgaW4gQXJhYmlkb3BzaXM6IGEgbW9kZWwgZm9yIHdvb2QgZm9ybWF0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzQvai4xMzk5LTMwNTQuMjAwMi4xMTQwNDEzLng8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TmV1cmFsIGNvbXB1dGF0aW9uPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkNoYW5nPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjExOTwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDE8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5UcmFpbmluZyBudS1zdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyczogdGhlb3J5IGFuZCBhbGdvcml0aG1zPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExNjIvMDg5OTc2NjAxNzUwMzk5MzM1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk1hY2hpbmUgTGVhcm5pbmc8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Q29ydGVzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjczPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5NTwvY1llYXI+DQogICAgICAgICAgICAgIDxkb2kgcHJvdmlkZXI9ImNyb3NzcmVmIj4xMC4xMDA3L0JGMDA5OTQwMTg8L2RvaT4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+U3VwcG9ydC12ZWN0b3IgTmV0d29ya3M8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNiI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkRldmVsb3BtZW50PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkRvbGFuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE5PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjcxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5MzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNlbGx1bGFyIG9yZ2FuaXNhdGlvbiBvZiB0aGUgQXJhYmlkb3BzaXMgdGhhbGlhbmEgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI3Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+U2VtaW5hcnMgaW4gQ2VsbCAmYW1wOyBEZXZlbG9wbWVudGFsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RWxvPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTA5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TdGVtIGNlbGwgZnVuY3Rpb24gZHVyaW5nIHBsYW50IHZhc2N1bGFyIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMTYvai5zZW1jZGIuMjAwOS4wOS4wMDk8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI4Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+RGV2ZWxvcG1lbnQ8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RXRjaGVsbHM8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4xNDA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjIyNDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTM8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5XT1g0IGFuZCBXT1gxNCBhY3QgZG93bnN0cmVhbSBvZiB0aGUgUFhZIHJlY2VwdG9yIGtpbmFzZSB0byByZWd1bGF0ZSBwbGFudCB2YXNjdWxhciBwcm9saWZlcmF0aW9uIGluZGVwZW5kZW50bHkgb2YgYW55IHJvbGUgaW4gdmFzY3VsYXIgb3JnYW5pc2F0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEyNDIvZGV2LjA5MTMxNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QTE9TIEdlbmV0aWNzPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkV0Y2hlbGxzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+ODwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT5lMTAwMjk5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudCB2YXNjdWxhciBjZWxsIGRpdmlzaW9uIGlzIG1haW50YWluZWQgYnkgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBQWFkgYW5kIGV0aHlsZW5lIHNpZ25hbGxpbmc8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTM3MS9qb3VybmFsLnBnZW4uMTAwMjk5NzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TW9sZWN1bGFyIFN5c3RlbXMgQmlvbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5GdWNoczwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MzcwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNsdXN0ZXJpbmcgcGhlbm90eXBlIHBvcHVsYXRpb25zIGJ5IGdlbm9tZS13aWRlIFJOQWkgYW5kIG11bHRpcGFyYW1ldHJpYyBpbWFnaW5nPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbXNiLjIwMTAuMjU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxMSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkJpbyBTeXN0ZW1zPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkdyYW5xdmlzdDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjExMDwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42MDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5CYVNBUi1BIHRvb2wgaW4gUiBmb3IgZnJlcXVlbmN5IGRldGVjdGlvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouYmlvc3lzdGVtcy4yMDEyLjA3LjAwNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+Q3VycmVudCBPcGluaW9uIGluIFBsYW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+R3Jvb3ZlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjk8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NTU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGV2ZWxvcG1lbnRhbCBtZWNoYW5pc21zIHJlZ3VsYXRpbmcgc2Vjb25kYXJ5IGdyb3d0aCBpbiB3b29keSBwbGFudHM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLnBiaS4yMDA1LjExLjAxMzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEzIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIyPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MTg8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+VERJRiBwZXB0aWRlIHNpZ25hbGluZyByZWd1bGF0ZXMgdmFzY3VsYXIgc3RlbSBjZWxsIHByb2xpZmVyYXRpb24gdmlhIHRoZSBXT1g0IGhvbWVvYm94IGdlbmUgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNS90cGMuMTEwLjA3NjA4MzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjE0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMgb2YgdGhlIFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjEwNTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT4xNTIwODwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDg8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5Ob24tY2VsbC1hdXRvbm9tb3VzIGNvbnRyb2wgb2YgdmFzY3VsYXIgc3RlbSBjZWxsIGZhdGUgYnkgYSBDTEUgcGVwdGlkZS9yZWNlcHRvciBzeXN0ZW08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjA4MDg0NDQxMDU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkNlbGw8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+TWV5ZXJvd2l0ejwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjU2PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BcmFiaWRvcHNpcywgYSB1c2VmdWwgd2VlZDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2LzAwOTItODY3NCg4OSk5MDkwMC04PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5TY2llbmNlPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk1leWVyb3dpdHo8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yOTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTQ4MjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudHMgY29tcGFyZWQgdG8gYW5pbWFsczogdGhlIGJyb2FkZXN0IGNvbXBhcmF0aXZlIHN0dWR5IG9mIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExMjYvc2NpZW5jZS4xMDY2NjA5PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTciPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QbGFudCBQaHlzaW9sPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk5pZW1pbmVuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM1PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjY1MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDQ8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BIHdlZWQgZm9yIHdvb2Q/IEFyYWJpZG9wc2lzIGFzIGEgZ2VuZXRpYyBtb2RlbCBmb3IgeHlsZW0gZGV2ZWxvcG1lbnQ8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNC9wcC4xMDQuMDQwMjEyPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTgiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmUgQmlvdGVjaG5vbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5Ob2JsZTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjI0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjE1NjU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+V2hhdCBpcyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmU/PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmJ0MTIwNi0xNTY1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5Qcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcyBvZiB0aGUgVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk9sc29uPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+Nzc8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTUxNjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODA8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5DbGFzc2lmaWNhdGlvbiBvZiBjdWx0dXJlZCBtYW1tYWxpYW4gY2VsbHMgYnkgc2hhcGUgYW5hbHlzaXMgYW5kIHBhdHRlcm4gcmVjb2duaXRpb248L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjc3LjMuMTUxNjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+QmlvaW5mb3JtYXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+UGF1PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+OTc5PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkVCSW1hZ2XigJNhbiBSIHBhY2thZ2UgZm9yIGltYWdlIHByb2Nlc3Npbmcgd2l0aCBhcHBsaWNhdGlvbnMgdG8gY2VsbHVsYXIgcGhlbm90eXBlczwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDkzL2Jpb2luZm9ybWF0aWNzL2J0cTA0NjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIxIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5SYWduaTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIzPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjEzMjI8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDExPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TW9iaWxlIGdpYmJlcmVsbGluIGRpcmVjdGx5IHN0aW11bGF0ZXMgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHh5bGVtIGV4cGFuc2lvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTA1L3RwYy4xMTEuMDg0MDIwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjIiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5EcnlhZCBEaWdpdGFsIFJlcG9zaXRvcnk8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2Fua2FyPC9hdXRob3I+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDE0PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC41MDYxL2RyeWFkLmI4MzVrPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjMiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DdXJyZW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2lib3V0PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTg8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NDU4PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwODwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkZsb3dlcmluZyBhcyBhIGNvbmRpdGlvbiBmb3IgeHlsZW0gZXhwYW5zaW9uIGluIEFyYWJpZG9wc2lzIGh5cG9jb3R5bCBhbmQgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouY3ViLjIwMDguMDIuMDcwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjQiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5UaGUgTmV3IFBoeXRvbG9naXN0PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlNwaWNlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Njwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT41Nzc8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RXZvbHV0aW9uIG9mIGRldmVsb3BtZW50IG9mIHZhc2N1bGFyIGNhbWJpYSBhbmQgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTExL2ouMTQ2OS04MTM3LjIwMTAuMDMyMzYueDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjI1Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TWFjaGluZSBWaXNpb24gYW5kIEFwcGxpY2F0aW9uczwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5UaGVyaWF1bHQ8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yMzwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42NTk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+Q2VsbCBtb3JwaG9sb2d5IGNsYXNzaWZpY2F0aW9uIGFuZCBjbHV0dGVyIG1pdGlnYXRpb24gaW4gcGhhc2UtY29udHJhc3QgbWljcm9zY29weSBpbWFnZXMgdXNpbmcgbWFjaGluZSBsZWFybmluZzwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDA3L3MwMDEzOC0wMTEtMDM0NS05PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DZWxsPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlV5dHRld2FhbDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE0OTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40Mzk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TWVjaGFuaWNhbCBzdHJlc3MgYWN0cyB2aWEga2F0YW5pbiB0byBhbXBsaWZ5IGRpZmZlcmVuY2VzIGluIGdyb3d0aCByYXRlIGJldHdlZW4gYWRqYWNlbnQgY2VsbHMgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLmNlbGwuMjAxMi4wMi4wNDg8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyNyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk5hdHVyZSBDZWxsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+WWluPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+ODYwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkEgc2NyZWVuIGZvciBtb3JwaG9sb2dpY2FsIGNvbXBsZXhpdHkgaWRlbnRpZmllcyByZWd1bGF0b3JzIG9mIHN3aXRjaC1saWtlIHRyYW5zaXRpb25zIGJldHdlZW4gZGlzY3JldGUgY2VsbCBzaGFwZXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAzOC9uY2IyNzY0PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgIDwvY2l0YXRpb25fbGlzdD4NCiAgICAgICAgICA8Y29tcG9uZW50X2xpc3Q+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5BYnN0cmFjdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDE8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNhYnN0cmFjdDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPmVMaWZlIGRpZ2VzdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDI8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNkaWdlc3Q8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgMS4gQ2VsbHVsYXIgbGV2ZWwgYW5hbHlzaXMgb2YgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGguPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEpIExpZ2h0IG1pY3Jvc2NvcHkgb2YgY3Jvc3Mgc2VjdGlvbnMgb2J0YWluZWQgZnJvbSBBcmFiaWRvcHNpcyBoeXBvY290eWxzIChvcmdhbiBwb3NpdGlvbiBpbGx1c3RyYXRlZCBmb3IgYSA5LWRheS1vbGQgc2VlZGxpbmcsIGxvd2VyIGxlZnQpIGF0IDkgZGFnICh1cHBlciBsZWZ0KSBhbmQgMzUgZGFnIChyaWdodCkuIFNpemUgYmFycyBhcmUgMTAwIM68bS4gQmx1ZSBHVVMgc3RhaW5pbmcgZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiBhbiBBUEw6OkdVUyByZXBvcnRlciBnZW5lIGluIHRoaXMgQ29sLTAgYmFja2dyb3VuZCBsaW5lIG1hcmtzIHBobG9lbSBidW5kbGVzLiAoQikgT3ZlcnZpZXcgb2YgdGhlIGRldmVsb3BtZW50YWwgc2VyaWVzICh0aW1lIHBvaW50cyBhbmQgZGlzdGluY3Qgc2FtcGxlcyBwZXIgZ2Vub3R5cGUpIGFuYWx5emVkIGluIHRoaXMgc3R1ZHkuIChDKSBFeGFtcGxlIG9mIGEgaGlnaC1yZXNvbHV0aW9uIGh5cG9jb3R5bCBzZWN0aW9uIGltYWdlIGFzc2VtYmxlZCBmcm9tIDExIMOXIDExIHRpbGVzLiAoRCkgVGhlIHNhbWUgaW1hZ2UgYWZ0ZXIgcHJlLXByb2Nlc3NpbmcgYW5kIGJpbmFyaXphdGlvbiwgYW5kIChFKSBzdWJzZXF1ZW50IHNlZ21lbnRhdGlvbiB1c2luZyBhIHdhdGVyc2hlZCBhbGdvcml0aG0uIChGKSBOdW1iZXIgb2YgbWlzLXNlZ21lbnRlZCBjZWxscyBhcyBkZXRlcm1pbmVkIGJ5IGNhcmVmdWwgdmlzdWFsIGluc3BlY3Rpb24gaW4gMTIgc2VjdGlvbnMsIHBsb3R0ZWQgYWdhaW5zdCB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIHBlciBzZWN0aW9uIChsb2cgc2NhbGUpLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDM8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWcxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDIuIFRoZSDigJhRdWFudGl0YXRpdmUgSGlzdG9sb2d54oCZIGFwcHJvYWNoLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBPdmVydmlldyBvZiB0aGUgY29tcHV0YXRpb25hbCBwaXBlbGluZSBmcm9tIGltYWdlIGFjcXVpc2l0aW9uIHRvIGFuYWx5c2lzLiAoQikg4oCYUGhlbm9wcmludHPigJkgZm9yIHRoZSBkaWZmZXJlbnQgZ2Vub3R5cGVzIGFuZCBkZXZlbG9wbWVudGFsIHN0YWdlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA0PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSAy4oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gZXhhbXBsZSBvZiBjbGFzc2lmaWVyIHNlbGVjdGlvbiB0aHJvdWdoIFYtZm9sZCBjcm9zcyB2YWxpZGF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBncmVlbiBhcnJvdyBwb2ludHMgb3V0IHRoZSBzZWxlY3RlZCBmZWF0dXJlIGNvbWJpbmF0aW9uIGFjY29yZGluZyB0byB0aGUgY3JpdGVyaWEgb2YgbWluaW11bSBudW1iZXIgb2YgZmVhdHVyZXMgd2l0aCB0aGUgaGlnaGVzdCBwZXJmb3JtYW5jZSBhbmQgdGhlIGxvd2VzdCB2YXJpYXRpb24gKHRoZSByYWRpdXNWIGZlYXR1cmUgd2FzIGV4Y2x1ZGVkIGR1ZSB0byBpdHMgcHV0YXRpdmUgdmFyaWF0aW9uIGluIHRpc3N1ZSBsb2NhdGlvbikuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnMnMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDMuIFByb2dyZXNzaW9uIG9mIHRpc3N1ZSBwcm9saWZlcmF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIG9mIHRoZSBwaGVub3ByaW50cyBzaG93biBpbiBGaWd1cmUgMkIsIHBlcmZvcm1lZCB3aXRoIG5vcm1hbGl6ZWQgdmFsdWVzIChTdXBwbGVtZW50YXJ5IGZpbGUgNCkuIFRoZSBpbmxheSBzY3JlZXBsb3QgZGlzcGxheXMgdGhlIHByb3BvcnRpb24gb2YgdG90YWwgdmFyaWF0aW9uIGV4cGxhaW5lZCBieSBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQuIChC4oCTRSkgQ29tcGFyYXRpdmUgcGxvdHMgb2YgcGFyYW1ldGVyIHByb2dyZXNzaW9uIGluIHRoZSB0d28gZ2Vub3R5cGVzLiBJbiAoRCksIHh5bGVtIHJlcHJlc2VudHMgY29tYmluZWQgdmVzc2VsLCBwYXJlbmNoeW1hLCBhbmQgZmliZXIgY2VsbHMsIHBobG9lbSByZXByZXNlbnRzIGNvbWJpbmVkIHBobG9lbSBwYXJlbmNoeW1hIGFuZCBidW5kbGUgY2VsbHMuIEVycm9yIGJhcnMgaW5kaWNhdGUgc3RhbmRhcmQgZXJyb3IuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNjwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzM8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgNC4gQmltb2RhbCBkaXN0cmlidXRpb24gb2YgaW5jbGluZSBhbmdsZSBhY2NvcmRpbmcgdG8gcG9zaXRpb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEgYW5kIEIpIFNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGNlbGwgaW5jbGluZSBhbmdsZSBpbGx1c3RyYXRlcyB0aGUgdmFzY3VsYXIgb3JnYW5pemF0aW9uIGluIExlciAoQikgYXMgY29tcGFyZWQgdG8gQ29sLTAgKEEpIGF0IGxhdGVyIHN0YWdlcyBvZiBkZXZlbG9wbWVudCwgZm9yIGV4YW1wbGUgMzAgZGFnLiBUaGUgc2l6ZSBvZiB0aGUgZGlzYyBpbmNyZWFzZXMgd2l0aCB0aGUgYXJlYSBvZiB0aGUgY2VsbC4gQmx1ZSBjb2xvciBpbmRpY2F0ZXMgcmFkaWFsIGNlbGwgb3JpZW50YXRpb24sIHJlZCBvcnRob3JhZGlhbC4gKEMgYW5kIEQpIFZpb2xpbiBwbG90cyBvZiBpbmNsaW5lIGFuZ2xlIGRpc3RyaWJ1dGlvbiwgaWxsdXN0cmF0aW5nIGluY3JlYXNpbmdseSBiaW1vZGFsIGRpc3RyaWJ1dGlvbiBjb2luY2lkZW50IHdpdGggcmVmaW5lZCB2YXNjdWxhciBvcmdhbml6YXRpb24gYW5kIGRpZmZlcmVudCBkeW5hbWljcyBvZiB0aGUgcHJvY2VzcyBpbiB0aGUgdHdvIGdlbm90eXBlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA3PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnNDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA04oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gaWxsdXN0cmF0aW9uIG9mIHRoZSBpbmNsaW5lIGFuZ2xlLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBpbmNsaW5lIGlzIHRoZSBhbmdsZSBiZXR3ZWVuIHRoZSBzZWN0aW9uIHJhZGl1cyB0aHJvdWdoIHRoZSBjZW50ZXIgb2YgYW4gZWxsaXBzZSBmaXQgdG8gYSBjZWxsIGFuZCB0aGUgbWFqb3IgYXhpcyBvZiB0aGF0IGVsbGlwc2UgZXh0ZW5kZWQgdG93YXJkcyB0aGUgeCBheGlzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDg8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9maWd1cmVzI2ZpZzRzMTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA1LiBEaXN0aW5jdCBsb2NhbCBvcmdhbml6YXRpb24gb2YgaW5jbGluZSBhbmdsZSBkdXJpbmcgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGggcHJvZ3Jlc3Npb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEHigJNKKSBEZW5zaXR5IHBsb3RzIG9mIGNlbGwgaW5jbGluZSBhbmdsZSB2cyByYWRpYWwgcG9zaXRpb24gZm9yIHRoZSB0d28gZ2Vub3R5cGVzIGF0IHRoZSBpbmRpY2F0ZWQgZGV2ZWxvcG1lbnRhbCBzdGFnZXMsIHJlcHJlc2VudGluZyBhbGwgY2VsbHMgYWNyb3NzIGFsbCBzZWN0aW9ucyBmb3IgYSBnaXZlbiB0aW1lIHBvaW50LiBUaGUgcmVkIGxpbmVzIHJlcHJlc2VudCB0aGUgZml0IG9mIHRoZXNlIGNsb3VkIGRpc3RyaWJ1dGlvbnMgd2l0aCBsb2NhbGx5IHdlaWdodGVkIGxpbmVhciByZWdyZXNzaW9uIChpLmUuLCBsb3dlc3MpLCByZXZlYWxpbmcgdGhlIGVzc2VudGlhbCBkYXRhIHRyZW5kcy4gQWxsIHNlY3Rpb25zIHdlcmUgbm9ybWFsaXplZCBmcm9tIDAuMCAodGhlIG1hbnVhbGx5IGRlZmluZWQgY2VudGVyKSB0byAxLjAgKHRoZSBhdmVyYWdlIHJhZGl1cyBpbiBhIHNldCBvZiBzZWN0aW9ucyBhcyBkZXRlcm1pbmVkIGJ5IHRoZSBhdmVyYWdlIGRpc3RhbmNlIG9mIHRoZSBvdXRlcm1vc3QgY2VsbHMgZnJvbSB0aGUgY2VudGVyIGZvciBpbmRpdmlkdWFsIHNlY3Rpb25zKS4gQm94IHBsb3RzIGluZGljYXRlIHRoZSBxdWFydGlsZXMgb2YgdGhlIHJhZGlhbiBkaXN0cmlidXRpb24gZm9yIGVhY2ggY2VsbC10eXBlIGNsYXNzIGFuZCBhcmUgcGxhY2VkIGF0IHRoZSBhdmVyYWdlIHBvc2l0aW9uIG9mIHRoZSBjZWxsIHR5cGUgd2l0aCByZXNwZWN0IHRvIHRoZSB5IGF4aXMuIE91dGxpZXJzIGFyZSBzaG93biBhcyBjaXJjbGVzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDk8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWc1PC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDXigJRmaWd1cmUgc3VwcGxlbWVudCAxLiBBbmFseXNpcyBvZiBjZWxsIG51bWJlciBpbiBkZWZpbmVkIHh5bGVtIHJlZ2lvbnMgb2YgZGlmZmVyZW50IHNpemUuPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+Q2VsbCBudW1iZXIgaW4gYSBjaXJjbGUgb2YgMjAw4oCTNTAwIHBpeGVscyBhcm91bmQgdGhlIHNlY3Rpb24gY2VudGVycyBmb3IgQ29sLTAuIENlbGwgY291bnQgaW4gYSBjb25zdGFudCBhcmVhIG9mIHh5bGVtIG92ZXIgdGltZSBhY3Jvc3MgYWxsIGF2ZXJhZ2VkIGFjcm9zcyBhbGwgc2VjdGlvbnMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnNXMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDYuIE1hcHBpbmcgb2YgcGhsb2VtIHBvbGUgcGF0dGVybmluZy48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgRXhhbXBsZSBvZiBHYXVzc2lhbiBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSBvZiB0aGUgbG9jYXRpb24gb2YgcHJlZGljdGVkIHBobG9lbSBidW5kbGVzIGNlbGxzIGluIGEgMzAgZGFnIENvbC0wIHNlY3Rpb24uIEhpZ2ggZGVuc2l0eSByZXByZXNlbnRzIHBobG9lbSBwb2xlcy4gKEIpIEV4YW1wbGUgb2YgYW4gYW5hbHlzaXMgb2YgZW1lcmdpbmcgcGhsb2VtIHBvbGUgcG9zaXRpb24gaW4gYSAzMCBkYWcgQ29sLTAgc2VjdGlvbi4gVGhlIHBsb3QgcmVwcmVzZW50cyBhIHBpeGVsIGludGVuc2l0eSBtYXAgYWZ0ZXIgbm9pc2UgcmVkdWN0aW9uIGFsb25nIGEgY2lyY3VsYXIgcmVnaW9uIG9mIGludGVyZXN0IGFjcm9zcyB0aGUgZW1lcmdpbmcgcGhsb2VtIHBvbGVzLiBJbnRlbnNpdHkgcGVha3MgYXJlIGR1ZSB0byBHVVMgc3RhaW5pbmcgY29uZmVycmVkIHRvIHBobG9lbSBidW5kbGVzIGJ5IGFuIEFQTDo6R1VTIHJlcG9ydGVyIGNvbnN0cnVjdC4gKEMpIFByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gb2YgdGhlIGRhdGEgc2hvd24gaW4gKEIpIG9idGFpbmVkIGZyb20gYW4gYXV0b21hdGVkIEJheWVzaWFuIG1vZGVsLiBUaGUgZG9taW5hbnQgc2luZ2xlIHBlYWsgaW5kaWNhdGVzIGEgY29uc3RhbnQgYXJjIGRpc3RhbmNlIG9mIGNhLiA2MiBwaXhlbCBiZXR3ZWVuIHRoZSBwaGxvZW0gcG9sZXMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzY8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgMS48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgQW4gZXhwbGFuYXRpb24gb2YgdGhlIGV4dHJhY3RlZCBwYXJhbWV0ZXJzIHRoYXQgZGVzY3JpYmUgdGhlIGNlbGx1bGFyIGZlYXR1cmVzLiAoQikgU3VtbWFyeSBpbmZvcm1hdGlvbiBvZiB0aGUgaGFuZC1sYWJlbGVkIHRyYWluaW5nIHNldCBmb3Igc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nLiAoQykgRGVmaW5pdGlvbiBvZiB0aGUgY2xhc3NpZmllcnMgc2VsZWN0ZWQgZm9yIGFuYWx5c2lzLiAoRCkgU3VtbWFyeSBvZiB0aGUgY2xhc3NpZmllciBwYXJhbWV0ZXJzIGZvciBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcuIChFKSBPdmVydmlldyBvZiB0aGUgY2VsbCB0eXBlIGNsYXNzZXMgcmVjb2duaXplZCBieSB0aGUgc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoIGFuZCB0aGVpciBhc3NpZ25tZW50IGNvZGVzIHVzZWQgaW4gRGF0YSBGaWxlcyAzIGFuZCA0Ljwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEyPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDEtZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAyLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIENvbC0wIHNlY3Rpb25zLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEzPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDItZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAzLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIExlciBzZWN0aW9ucy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0QzLWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgNC48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT5UaGUgbm9ybWFsaXplZCB2YWx1ZXMgb2YgdGhlIHBoZW5vcHJpbnRzIChGaWd1cmUgMkIpIHVzZWQgZm9yIFBDQS48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0Q0LWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5EZWNpc2lvbiBsZXR0ZXI8L3RpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9InRleHQvcGxhaW4iIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDE2PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjU0ExPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+QXV0aG9yIHJlc3BvbnNlPC90aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJ0ZXh0L3BsYWluIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNzwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I1NBMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICA8L2NvbXBvbmVudF9saXN0Pg0KICAgICAgICA8L2pvdXJuYWxfYXJ0aWNsZT4NCiAgICAgIDwvam91cm5hbD4NCiAgICA8L2Nyb3NzcmVmPg0KICA8L2RvaV9yZWNvcmQ+DQo8L2RvaV9yZWNvcmRzPg== + http_version: + recorded_at: Sat, 01 Dec 2018 14:44:44 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_datacite_url.yml b/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_datacite_url.yml new file mode 100644 index 000000000..ff4904ed4 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_datacite_url.yml @@ -0,0 +1,99 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7272 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 12:23:35 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - 7ac3e7b6-6d0c-46fe-932d-5c0bce43449b + Etag: + - W/"434d63d02889e47275389a930375c569" + X-Runtime: + - '0.024104' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7272","type":"prefixes","attributes":{"registration-agency":"DataCite","created":"2012-03-15T09:47:35.000Z","updated":null},"relationships":{"clients":{"data":[{"id":"cdl.ucsfctsi","type":"clients"}]},"providers":{"data":[{"id":"cdl","type":"providers"}]}}},"included":[{"id":"cdl.ucsfctsi","type":"clients","attributes":{"name":"UCSF + Clinical & Translational Science Institute (CTSI)","symbol":"CDL.UCSFCTSI","year":2012,"contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","domains":"*","url":null,"created":"2012-07-10T20:59:53.000Z","updated":"2018-08-26T02:35:12.000Z","is-active":true,"has-password":true},"relationships":{"provider":{"data":{"id":"cdl","type":"providers"}},"prefixes":{"data":[{"id":"10.5072","type":"prefixes"},{"id":"10.7272","type":"prefixes"}]}}},{"id":"cdl","type":"providers","attributes":{"name":"California + Digital Library","symbol":"CDL","website":"http://ezid.cdlib.org","contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","phone":null,"description":"California + Digital Library (CDL) handles scholarly information at every stage of its + life. CDL provides technology and expertise to help the University of California + (UC) collect, publish, access, and preserve its full range of information + resources. CDL partners with organizations outside the UCs on far-reaching + problems.\n\nCDL offers DataCite DOIs to University of California scholars + and researchers via the EZID service. EZID is also available as a general + identifier service to help educational, non-profit, governmental and commercial + clients create and manage globally unique identifiers for data and other sources.","region":"AMER","country":"US","logo-url":"https://assets.datacite.org/images/members/cdl.png","organization-type":"academic_institution","focus-area":"general","is-active":true,"has-password":true,"joined":"2009-12-01","created":"2010-01-01T00:00:00.000Z","updated":"2018-10-24T20:47:51.000Z"},"relationships":{"prefixes":{"data":[{"id":"10.5062","type":"prefixes"},{"id":"10.5063","type":"prefixes"},{"id":"10.5065","type":"prefixes"},{"id":"10.5068","type":"prefixes"},{"id":"10.5069","type":"prefixes"},{"id":"10.5070","type":"prefixes"},{"id":"10.5072","type":"prefixes"},{"id":"10.6071","type":"prefixes"},{"id":"10.6072","type":"prefixes"},{"id":"10.6074","type":"prefixes"},{"id":"10.6075","type":"prefixes"},{"id":"10.6076","type":"prefixes"},{"id":"10.6078","type":"prefixes"},{"id":"10.6079","type":"prefixes"},{"id":"10.6080","type":"prefixes"},{"id":"10.6081","type":"prefixes"},{"id":"10.6085","type":"prefixes"},{"id":"10.6086","type":"prefixes"},{"id":"10.7265","type":"prefixes"},{"id":"10.7268","type":"prefixes"},{"id":"10.7269","type":"prefixes"},{"id":"10.7270","type":"prefixes"},{"id":"10.7271","type":"prefixes"},{"id":"10.7272","type":"prefixes"},{"id":"10.7276","type":"prefixes"},{"id":"10.7279","type":"prefixes"},{"id":"10.7280","type":"prefixes"},{"id":"10.7281","type":"prefixes"},{"id":"10.7282","type":"prefixes"},{"id":"10.7284","type":"prefixes"},{"id":"10.7285","type":"prefixes"},{"id":"10.7286","type":"prefixes"},{"id":"10.7288","type":"prefixes"},{"id":"10.7291","type":"prefixes"},{"id":"10.7292","type":"prefixes"},{"id":"10.7293","type":"prefixes"},{"id":"10.7295","type":"prefixes"},{"id":"10.7296","type":"prefixes"},{"id":"10.7297","type":"prefixes"},{"id":"10.7299","type":"prefixes"},{"id":"10.7300","type":"prefixes"},{"id":"10.4246","type":"prefixes"},{"id":"10.7908","type":"prefixes"},{"id":"10.7911","type":"prefixes"},{"id":"10.7913","type":"prefixes"},{"id":"10.7914","type":"prefixes"},{"id":"10.7916","type":"prefixes"},{"id":"10.7918","type":"prefixes"},{"id":"10.7919","type":"prefixes"},{"id":"10.7920","type":"prefixes"},{"id":"10.7921","type":"prefixes"},{"id":"10.7922","type":"prefixes"},{"id":"10.7925","type":"prefixes"},{"id":"10.7927","type":"prefixes"},{"id":"10.7928","type":"prefixes"},{"id":"10.7929","type":"prefixes"},{"id":"10.7932","type":"prefixes"},{"id":"10.7933","type":"prefixes"},{"id":"10.7934","type":"prefixes"},{"id":"10.7939","type":"prefixes"},{"id":"10.7940","type":"prefixes"},{"id":"10.7941","type":"prefixes"},{"id":"10.7942","type":"prefixes"},{"id":"10.7943","type":"prefixes"},{"id":"10.7944","type":"prefixes"},{"id":"10.7945","type":"prefixes"},{"id":"10.7946","type":"prefixes"},{"id":"10.13022","type":"prefixes"},{"id":"10.13026","type":"prefixes"},{"id":"10.13025","type":"prefixes"},{"id":"10.15147","type":"prefixes"},{"id":"10.15146","type":"prefixes"},{"id":"10.15142","type":"prefixes"},{"id":"10.15144","type":"prefixes"},{"id":"10.15145","type":"prefixes"},{"id":"10.15140","type":"prefixes"},{"id":"10.15141","type":"prefixes"},{"id":"10.15139","type":"prefixes"},{"id":"10.1184","type":"prefixes"},{"id":"10.15784","type":"prefixes"},{"id":"10.15779","type":"prefixes"},{"id":"10.15780","type":"prefixes"},{"id":"10.15781","type":"prefixes"},{"id":"10.15782","type":"prefixes"},{"id":"10.17612","type":"prefixes"},{"id":"10.17610","type":"prefixes"},{"id":"10.17611","type":"prefixes"},{"id":"10.17614","type":"prefixes"},{"id":"10.17615","type":"prefixes"},{"id":"10.17602","type":"prefixes"},{"id":"10.17603","type":"prefixes"},{"id":"10.17908","type":"prefixes"},{"id":"10.17916","type":"prefixes"},{"id":"10.17915","type":"prefixes"},{"id":"10.17918","type":"prefixes"},{"id":"10.17919","type":"prefixes"},{"id":"10.17911","type":"prefixes"},{"id":"10.17913","type":"prefixes"},{"id":"10.17920","type":"prefixes"},{"id":"10.18123","type":"prefixes"},{"id":"10.18119","type":"prefixes"},{"id":"10.18118","type":"prefixes"},{"id":"10.18117","type":"prefixes"},{"id":"10.18115","type":"prefixes"},{"id":"10.18431","type":"prefixes"},{"id":"10.18436","type":"prefixes"},{"id":"10.18437","type":"prefixes"},{"id":"10.18439","type":"prefixes"},{"id":"10.18737","type":"prefixes"},{"id":"10.18736","type":"prefixes"},{"id":"10.18739","type":"prefixes"},{"id":"10.18734","type":"prefixes"},{"id":"10.20353","type":"prefixes"},{"id":"10.20354","type":"prefixes"},{"id":"10.20352","type":"prefixes"},{"id":"10.20357","type":"prefixes"},{"id":"10.20358","type":"prefixes"},{"id":"10.20359","type":"prefixes"},{"id":"10.21228","type":"prefixes"},{"id":"10.21229","type":"prefixes"},{"id":"10.21224","type":"prefixes"},{"id":"10.21222","type":"prefixes"},{"id":"10.21223","type":"prefixes"},{"id":"10.21221","type":"prefixes"},{"id":"10.21237","type":"prefixes"},{"id":"10.21238","type":"prefixes"},{"id":"10.21239","type":"prefixes"},{"id":"10.21236","type":"prefixes"},{"id":"10.21430","type":"prefixes"},{"id":"10.21433","type":"prefixes"},{"id":"10.21431","type":"prefixes"},{"id":"10.21421","type":"prefixes"},{"id":"10.21422","type":"prefixes"},{"id":"10.21424","type":"prefixes"},{"id":"10.21425","type":"prefixes"},{"id":"10.21426","type":"prefixes"},{"id":"10.21427","type":"prefixes"},{"id":"10.21428","type":"prefixes"},{"id":"10.21418","type":"prefixes"},{"id":"10.21416","type":"prefixes"},{"id":"10.21414","type":"prefixes"},{"id":"10.21972","type":"prefixes"},{"id":"10.21973","type":"prefixes"},{"id":"10.21975","type":"prefixes"},{"id":"10.21976","type":"prefixes"},{"id":"10.21977","type":"prefixes"},{"id":"10.21978","type":"prefixes"},{"id":"10.21990","type":"prefixes"},{"id":"10.21983","type":"prefixes"},{"id":"10.21980","type":"prefixes"},{"id":"10.21986","type":"prefixes"},{"id":"10.5195","type":"prefixes"},{"id":"10.25334","type":"prefixes"},{"id":"10.25337","type":"prefixes"},{"id":"10.25338","type":"prefixes"},{"id":"10.25342","type":"prefixes"},{"id":"10.25349","type":"prefixes"},{"id":"10.25348","type":"prefixes"},{"id":"10.25347","type":"prefixes"},{"id":"10.25346","type":"prefixes"},{"id":"10.25345","type":"prefixes"},{"id":"10.25344","type":"prefixes"},{"id":"10.25352","type":"prefixes"},{"id":"10.25350","type":"prefixes"},{"id":"10.25351","type":"prefixes"},{"id":"10.26081","type":"prefixes"}]}}}]}' + http_version: + recorded_at: Fri, 07 Dec 2018 12:23:35 GMT +- request: + method: get + uri: https://search.test.datacite.org/api?fl=doi,url,xml,state,allocator_symbol,datacentre_symbol,media,minted,updated&q=doi:10.7272/q6g15xs4&wt=json + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 12:23:35 GMT + Content-Type: + - application/json;charset=UTF-8 + Connection: + - keep-alive + Server: + - nginx/1.10.3 (Ubuntu) + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + Access-Control-Expose-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + body: + encoding: ASCII-8BIT + string: '{"responseHeader":{"status":0,"QTime":0},"response":{"numFound":1,"start":0,"docs":[{"datacentre_symbol":"CDL.UCSFCTSI","url":"https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4","xml":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJlc291cmNlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyBodHRwOi8vc2NoZW1hLmRhdGFjaXRlLm9yZy9tZXRhL2tlcm5lbC0zL21ldGFkYXRhLnhzZCI+CiAgPGlkZW50aWZpZXIgaWRlbnRpZmllclR5cGU9IkRPSSI+MTAuNzI3Mi9RNkcxNVhTNDwvaWRlbnRpZmllcj4KICA8Y3JlYXRvcnM+CiAgICA8Y3JlYXRvcj4KICAgICAgPGNyZWF0b3JOYW1lPlJvZHJpZ3VleiwgUm9iZXJ0PC9jcmVhdG9yTmFtZT4KICAgICAgPGFmZmlsaWF0aW9uPlVDIFNhbiBGcmFuY2lzY288L2FmZmlsaWF0aW9uPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZT5Nb3dlciwgV2lsbGlhbTwvY3JlYXRvck5hbWU+CiAgICAgIDxhZmZpbGlhdGlvbj5VQ0xBPC9hZmZpbGlhdGlvbj4KICAgIDwvY3JlYXRvcj4KICA8L2NyZWF0b3JzPgogIDx0aXRsZXM+CiAgICA8dGl0bGU+TkVYVVMgSGVhZCBDVDwvdGl0bGU+CiAgPC90aXRsZXM+CiAgPHB1Ymxpc2hlcj5VQyBTYW4gRnJhbmNpc2NvPC9wdWJsaXNoZXI+CiAgPHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+CiAgPGxhbmd1YWdlPmVuPC9sYW5ndWFnZT4KICA8cmVzb3VyY2VUeXBlIHJlc291cmNlVHlwZUdlbmVyYWw9IkRhdGFzZXQiLz4KICA8c2l6ZXM+CiAgICA8c2l6ZT4xNDk1MjA0IGJ5dGVzPC9zaXplPgogIDwvc2l6ZXM+CiAgPHZlcnNpb24+MTwvdmVyc2lvbj4KICA8cmlnaHRzTGlzdD4KICAgIDxyaWdodHMgcmlnaHRzVVJJPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyI+Q3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiA0LjAgSW50ZXJuYXRpb25hbCAoQ0MgQlkgNC4wKTwvcmlnaHRzPgogIDwvcmlnaHRzTGlzdD4KICA8ZGVzY3JpcHRpb25zPgogICAgPGRlc2NyaXB0aW9uIGRlc2NyaXB0aW9uVHlwZT0iQWJzdHJhY3QiPgogICAgICBCYWNrZ3JvdW5kIENsaW5pY2lhbnMsIGFmcmFpZCBvZiBtaXNzaW5nIGludHJhY3JhbmlhbCBpbmp1cmllcywgbGliZXJhbGx5CiAgICAgIG9idGFpbiBjb21wdXRlZCB0b21vZ3JhcGhpYyAoQ1QpIGhlYWQgaW1hZ2luZyBpbiBibHVudCB0cmF1bWEgcGF0aWVudHMuCiAgICAgIFByaW9yIHdvcmsgc3VnZ2VzdHMgdGhhdCBjbGluaWNhbCBjcml0ZXJpYSAoTkVYVVMgSGVhZCBDVCBkZWNpc2lvbgogICAgICBpbnN0cnVtZW50KSBjYW4gcmVsaWFibHkgaWRlbnRpZnkgcGF0aWVudHMgd2l0aCBpbXBvcnRhbnQgaW5qdXJpZXMsIHdoaWxlCiAgICAgIGV4Y2x1ZGluZyBpbmp1cnksIGFuZCB0aGUgbmVlZCBmb3IgaW1hZ2luZyBpbiBtYW55IHBhdGllbnRzLiBNZXRob2RzIFdlCiAgICAgIGNvbmR1Y3RlZCBhIHByb3NwZWN0aXZlIG9ic2VydmF0aW9uYWwgc3R1ZHkgb2YgdGhlIE5FWFVTIEhlYWQgQ1QgZGVjaXNpb24KICAgICAgaW5zdHJ1bWVudCAoREkpIHRoYXQgcmVxdWlyZXMgcGF0aWVudHMgdG8gbWVldCBlaWdodCBjcml0ZXJpYSB0byBhY2hpZXZlCiAgICAgIOKAnGxvdy1yaXNr4oCdIGNsYXNzaWZpY2F0aW9uLiBXZSBleGFtaW5lZCB0aGUgaW5zdHJ1bWVudOKAmXMgcGVyZm9ybWFuY2UgaW4KICAgICAgaWRlbnRpZnlpbmcgcGF0aWVudHMgcmVxdWlyaW5nIG5ldXJvbG9naWNhbCBpbnRlcnZlbnRpb24gZnJvbSBhbW9uZyBhCiAgICAgIGNvaG9ydCBvZiAxMSw3NzAgYmx1bnQgaGVhZCBpbmp1cnkgcGF0aWVudHMuIFJlc3VsdHMgVGhlIE5FWFVTIEhlYWQgQ1QgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA0MjAgb2YgNDIwIHBhdGllbnRzIHJlcXVpcmluZyBuZXVyb2xvZ2ljYWwKICAgICAgaW50ZXJ2ZW50aW9uIChzZW5zaXRpdml0eSwgMTAwLjAlIFs5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBbQ0ldOiA5OS4xJSDigJMKICAgICAgMTAwLjAlXSkuIFRoZSBpbnN0cnVtZW50IGFzc2lnbmVkIGxvdy1yaXNrIHN0YXR1cyB0byAyLDgyMyBvZiAxMSwzNTAKICAgICAgcGF0aWVudHMgd2hvIGRpZCBub3QgcmVxdWlyZSBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChzcGVjaWZpY2l0eSwgMjQuOSUKICAgICAgWzk1JSBDSTogMjQuMSUgLSAyNS43JV0pLiBOb25lIG9mIHRoZSAyLDgyMyBsb3ctcmlzayBwYXRpZW50cyByZXF1aXJlZAogICAgICBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChOUFYsIDEwMC4wJSBbOTUlIENJOiA5OS45JSAtIDEwMC4wJV0pLiBUaGUgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA3NTkgb2YgNzY3IHBhdGllbnRzIHdpdGggc2lnbmlmaWNhbnQKICAgICAgaW50cmFjcmFuaWFsIGluanVyaWVzIChzZW5zaXRpdml0eSwgOTkuMCUgWzk1JSBDSTogOTguMCUgLSA5OS42JV0pLiBUaGUKICAgICAgaW5zdHJ1bWVudCBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgdG8gMiw4MTUgb2YgMTEsMDAzIHBhdGllbnRzIHdobyBkaWQKICAgICAgbm90IGhhdmUgc2lnbmlmaWNhbnQgaW5qdXJpZXMgKHNwZWNpZmljaXR5LCAyNS42JSBbOTUlIENJOiAyNC44JSAtCiAgICAgIDI2LjQlXSkuIFNpZ25pZmljYW50IGluanVyaWVzIHdlcmUgYWJzZW50IGluIDIsODE1IG9mIHRoZSAyLDgyMyBwYXRpZW50cwogICAgICBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgKE5QViwgOTkuNyUgWzk1JSBDSTogOTkuNCUgLSA5OS45JV0pLiBDb25jbHVzaW9ucwogICAgICBUaGUgTkVYVVMgSGVhZCBDVCBESSByZWxpYWJseSBpZGVudGlmaWVzIGJsdW50IHRyYXVtYSBwYXRpZW50cyB3aG8gcmVxdWlyZQogICAgICBoZWFkIENUIGltYWdpbmcsIGFuZCBjb3VsZCBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSB1c2Ugb2YgQ1QgaW1hZ2luZy4KICAgIDwvZGVzY3JpcHRpb24+CiAgICA8ZGVzY3JpcHRpb24gZGVzY3JpcHRpb25UeXBlPSJNZXRob2RzIj5Qcm9zcGVjdGl2ZSBtdWx0aWNlbnRlcjwvZGVzY3JpcHRpb24+CiAgPC9kZXNjcmlwdGlvbnM+CiAgPGdlb0xvY2F0aW9ucz4KICAgIDxnZW9Mb2NhdGlvbj4KICAgICAgPGdlb0xvY2F0aW9uUG9pbnQ+MzcuMjUwMjIgLTExOS43NTEyNjwvZ2VvTG9jYXRpb25Qb2ludD4KICAgICAgPGdlb0xvY2F0aW9uUGxhY2U+Q2FsaWZvcm5pYSwgVVNBPC9nZW9Mb2NhdGlvblBsYWNlPgogICAgPC9nZW9Mb2NhdGlvbj4KICA8L2dlb0xvY2F0aW9ucz4KPC9yZXNvdXJjZT4=","allocator_symbol":"CDL","minted":"2018-12-07T09:48:20Z","state":"findable","updated":"2018-12-07T09:48:20Z","doi":"10.7272/Q6G15XS4"}]}} + +' + http_version: + recorded_at: Fri, 07 Dec 2018 12:23:35 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_schema_org_url.yml b/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_schema_org_url.yml new file mode 100644 index 000000000..f807ae151 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Doi/parse_xml/from_schema_org_url.yml @@ -0,0 +1,92 @@ +--- +http_interactions: +- request: + method: get + uri: https://doi.pangaea.de/10.1594/PANGAEA.836178 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Server: + - PANGAEA/1.0 + Date: + - Thu, 29 Nov 2018 12:17:36 GMT + Transfer-Encoding: + - chunked + Vary: + - Accept + - Accept-Encoding + Link: + - ;rel="cite-as", ;rel="describedby";type="application/ld+json", + ;rel="describedby";type="application/x-research-info-systems", + ;rel="describedby";type="application/x-bibtex", + ;rel="item";type="application/zip", + ;rel="author", ;rel="author" + Content-Type: + - text/html;charset=UTF-8 + X-Ua-Compatible: + - IE=Edge + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + body: + encoding: ASCII-8BIT + string: !binary |- + PCFET0NUWVBFIGh0bWw+DQo8aHRtbCBsYW5nPSJlbiI+DQo8aGVhZD4KPG1ldGEgY2hhcnNldD0iVVRGLTgiPg0KPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLCBtaW5pbXVtLXNjYWxlPTEsIG1heGltdW0tc2NhbGU9MSwgdXNlci1zY2FsYWJsZT1ubyI+DQo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzP2ZhbWlseT1PcGVuK1NhbnM6NDAwLDYwMCw0MDBpdGFsaWMsNzAwLDcwMGl0YWxpYyw2MDBpdGFsaWMsMzAwLDMwMGl0YWxpYyw4MDAsODAwaXRhbGljIj4KPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2Jvb3RzdHJhcC0yNGNvbC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiPgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3YuODQ3NWExMmQwNTQxMTMxN2YzZDk5MzU5YjAxN2EyNjMvY3NzL3BhbmdhZWEuY3NzIj4KPCEtLVtpZiBsdGUgSUUgOV0+DQo8c3R5bGU+I3RvcGljcy1wdWxsZG93bi13cmFwcGVyIGxhYmVsOmFmdGVyIHsgZGlzcGxheTpub25lOyB9PC9zdHlsZT4NCjwhW2VuZGlmXS0tPg0KPGxpbmsgcmVsPSJzaG9ydGN1dCBpY29uIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2Zhdmljb24uaWNvIj4NCjxsaW5rIHJlbD0iaWNvbiIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9hc3NldHMvdi44NDc1YTEyZDA1NDExMzE3ZjNkOTkzNTliMDE3YTI2My9mYXZpY29uLmljbyIgdHlwZT0iaW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uIj4NCjxsaW5rIHJlbD0iaW1hZ2Vfc3JjIiB0eXBlPSJpbWFnZS9wbmciIGhyZWY9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3NvY2lhbC1pY29ucy9wYW5nYWVhLXNoYXJlLnBuZyI+DQo8bWV0YSBwcm9wZXJ0eT0ib2c6aW1hZ2UiIGNvbnRlbnQ9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3NvY2lhbC1pY29ucy9wYW5nYWVhLXNoYXJlLnBuZyI+DQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9qcXVlcnkvMS4xMi40L2pxdWVyeS5taW4uanMiPjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9qcXVlcnkubWF0Y2hIZWlnaHQvMC43LjAvanF1ZXJ5Lm1hdGNoSGVpZ2h0LW1pbi5qcyI+PC9zY3JpcHQ+CjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9Ii8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2pxdWVyeS5hcHBlYXIvMC40LjEvanF1ZXJ5LmFwcGVhci5taW4uanMiPjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2Jvb3RzdHJhcC0yNGNvbC9qcy9ib290c3RyYXAubWluLmpzIj48L3NjcmlwdD4KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0iLy93d3cucGFuZ2FlYS5kZS9hc3NldHMvdi44NDc1YTEyZDA1NDExMzE3ZjNkOTkzNTliMDE3YTI2My9qcy9kYXRhY29tYm8tbWluLmpzIj48L3NjcmlwdD4KPHRpdGxlPkpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTQpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgbGFrZSBuZWFyIEthbmdlcmx1c3N1YXEsIHdlc3QgR3JlZW5sYW5kPC90aXRsZT4KPG1ldGEgbmFtZT0idGl0bGUiIGNvbnRlbnQ9Ikh5ZHJvbG9naWNhbCBhbmQgbWV0ZW9yb2xvZ2ljYWwgaW52ZXN0aWdhdGlvbnMgaW4gYSBsYWtlIG5lYXIgS2FuZ2VybHVzc3VhcSwgd2VzdCBHcmVlbmxhbmQiIC8+CjxtZXRhIG5hbWU9ImF1dGhvciIgY29udGVudD0iSm9oYW5zc29uLCBFbW1hOyBCZXJnbHVuZCwgU3RlbjsgTGluZGJvcmcsIFRvYmlhczsgUGV0cm9uZSwgSm9oYW5uZXM7IHZhbiBBcywgRGlyazsgR3VzdGFmc3NvbiwgTGFycy1Hw7ZyYW47IE7DpHNsdW5kLCBKZW5zLU92ZTsgTGF1ZG9uLCBIamFsbWFyIiAvPgo8bWV0YSBuYW1lPSJkYXRlIiBjb250ZW50PSIyMDE0LTA5LTI1IiAvPgo8bWV0YSBuYW1lPSJkZXNjcmlwdGlvbiIgY29udGVudD0iSm9oYW5zc29uLCBFbW1hOyBCZXJnbHVuZCwgU3RlbjsgTGluZGJvcmcsIFRvYmlhczsgUGV0cm9uZSwgSm9oYW5uZXM7IHZhbiBBcywgRGlyazsgR3VzdGFmc3NvbiwgTGFycy1Hw7ZyYW47IE7DpHNsdW5kLCBKZW5zLU92ZTsgTGF1ZG9uLCBIamFsbWFyICgyMDE0KTogSHlkcm9sb2dpY2FsIGFuZCBtZXRlb3JvbG9naWNhbCBpbnZlc3RpZ2F0aW9ucyBpbiBhIGxha2UgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZC4gUEFOR0FFQSwgaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgsIFN1cHBsZW1lbnQgdG86IEpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTUpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldC4gRWFydGggU3lzdGVtIFNjaWVuY2UgRGF0YSwgNygxKSwgOTMtMTA4LCBodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIgLz4KPG1ldGEgbmFtZT0iZ2VvLnBvc2l0aW9uIiBjb250ZW50PSI2Ny4xMjU5NDA7LTUwLjE4MDM3MCIgLz4KPG1ldGEgbmFtZT0iSUNCTSIgY29udGVudD0iNjcuMTI1OTQwLCAtNTAuMTgwMzcwIiAvPgo8IS0tQkVHSU46IER1YmxpbiBDb3JlIGRlc2NyaXB0aW9uLS0+CjxsaW5rIHJlbD0ic2NoZW1hLkRDIiBocmVmPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgLz4KPGxpbmsgcmVsPSJzY2hlbWEuRENURVJNUyIgaHJlZj0iaHR0cDovL3B1cmwub3JnL2RjL3Rlcm1zLyIgLz4KPG1ldGEgbmFtZT0iREMudGl0bGUiIGNvbnRlbnQ9Ikh5ZHJvbG9naWNhbCBhbmQgbWV0ZW9yb2xvZ2ljYWwgaW52ZXN0aWdhdGlvbnMgaW4gYSBsYWtlIG5lYXIgS2FuZ2VybHVzc3VhcSwgd2VzdCBHcmVlbmxhbmQiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IkpvaGFuc3NvbiwgRW1tYSIgLz4KPG1ldGEgbmFtZT0iREMuY3JlYXRvciIgY29udGVudD0iQmVyZ2x1bmQsIFN0ZW4iIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IkxpbmRib3JnLCBUb2JpYXMiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IlBldHJvbmUsIEpvaGFubmVzIiAvPgo8bWV0YSBuYW1lPSJEQy5jcmVhdG9yIiBjb250ZW50PSJ2YW4gQXMsIERpcmsiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9Ikd1c3RhZnNzb24sIExhcnMtR8O2cmFuIiAvPgo8bWV0YSBuYW1lPSJEQy5jcmVhdG9yIiBjb250ZW50PSJOw6RzbHVuZCwgSmVucy1PdmUiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IkxhdWRvbiwgSGphbG1hciIgLz4KPG1ldGEgbmFtZT0iREMucHVibGlzaGVyIiBjb250ZW50PSJQQU5HQUVBIiAvPgo8bWV0YSBuYW1lPSJEQy5zb3VyY2UiIGNvbnRlbnQ9IlN1cHBsZW1lbnQgdG86IEpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTUpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldC4gRWFydGggU3lzdGVtIFNjaWVuY2UgRGF0YSwgNygxKSwgOTMtMTA4LCBodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIgLz4KPG1ldGEgbmFtZT0iREMuZGF0ZSIgY29udGVudD0iMjAxNC0wOS0yNSIgc2NoZW1lPSJEQ1RFUk1TLlczQ0RURiIgLz4KPG1ldGEgbmFtZT0iREMudHlwZSIgY29udGVudD0iRGF0YXNldCIgLz4KPG1ldGEgbmFtZT0iREMubGFuZ3VhZ2UiIGNvbnRlbnQ9ImVuIiBzY2hlbWU9IkRDVEVSTVMuUkZDMzA2NiIgLz4KPG1ldGEgbmFtZT0iRENURVJNUy5saWNlbnNlIiBzY2hlbWU9IkRDVEVSTVMuVVJJIiBjb250ZW50PSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLyIgLz4KPG1ldGEgbmFtZT0iREMuaWRlbnRpZmllciIgY29udGVudD0iaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgiIHNjaGVtZT0iRENURVJNUy5VUkkiIC8+CjxtZXRhIG5hbWU9IkRDLmZvcm1hdCIgY29udGVudD0iYXBwbGljYXRpb24vemlwLCA1NjYzLjAga0J5dGVzIiAvPgo8bWV0YSBuYW1lPSJEQy5yZWxhdGlvbiIgY29udGVudD0iTWFwIG9mIFR3byBCb2F0IExha2UgaW4gR3JlZW5sYW5kIChqcGcgMTMgTUIpIHdpdGggcG9zaXRpb24gb2Ygc2FtcGxpbmcgc2l0ZXMgKFVSSTogaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC90d29ib2F0bGFrZV9ncmVlbmxhbmQuanBnKSIgLz4KPG1ldGEgbmFtZT0iREMucmVsYXRpb24iIGNvbnRlbnQ9IlRpbWUgbGFwcyBwaG90b3Mgb2YgbGFrZSAyMDEyLTA5LTA1IHRvIDIwMTMtMDgtMTQgKG1vdiBmaWxlLCB6aXBwZWQgMjA1IE1CKSAoVVJJOiBodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L1RpbWVsYXBzZV9UQkwuemlwKSIgLz4KPCEtLUVORDogRHVibGluIENvcmUgZGVzY3JpcHRpb24tLT4KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0iLy9tYXBzLmdvb2dsZWFwaXMuY29tL21hcHMvYXBpL2pzP3Y9MyZhbXA7bGFuZ3VhZ2U9ZW4mYW1wO2tleT1BSXphU3lEU2lWalBTNVl2YW5ac0VINFJ2SzBnRXI0NlVvLTFyQ1EiPjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+Lyo8IVtDREFUQVsqL2pRdWVyeShmdW5jdGlvbigkKSB7IHJldHVybiBpbml0aWFsaXplU21hbGxEYXRhc2V0R01hcCg4MzYxNzgsJ2hhc2g9YzY2NjkzY2JiZjZjNDkyYjEwYmU4M2Q0NDliOWY0NzUnLG5ldyBnb29nbGUubWFwcy5MYXRMbmdCb3VuZHMobmV3IGdvb2dsZS5tYXBzLkxhdExuZyg2Ny4xMjU5NCwtNTAuMTgwMzcpLG5ldyBnb29nbGUubWFwcy5MYXRMbmcoNjcuMTI1OTQsLTUwLjE4MDM3KSksdW5kZWZpbmVkKTsgfSk7LypdXT4qLzwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL2QxYnhoOHVhczFtbnc3LmNsb3VkZnJvbnQubmV0L2Fzc2V0cy9lbWJlZC5qcyI+PC9zY3JpcHQ+CjxsaW5rIHJlbD0iY2l0ZS1hcyIgaHJlZj0iaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgiPgo8bGluayByZWw9ImRlc2NyaWJlZGJ5IiBocmVmPSJodHRwczovL2RvaS5wYW5nYWVhLmRlLzEwLjE1OTQvUEFOR0FFQS44MzYxNzg/Zm9ybWF0PW1ldGFkYXRhX2pzb25sZCIgdHlwZT0iYXBwbGljYXRpb24vbGQranNvbiI+CjxsaW5rIHJlbD0iZGVzY3JpYmVkYnkiIGhyZWY9Imh0dHBzOi8vZG9pLnBhbmdhZWEuZGUvMTAuMTU5NC9QQU5HQUVBLjgzNjE3OD9mb3JtYXQ9Y2l0YXRpb25fcmlzIiB0eXBlPSJhcHBsaWNhdGlvbi94LXJlc2VhcmNoLWluZm8tc3lzdGVtcyI+CjxsaW5rIHJlbD0iZGVzY3JpYmVkYnkiIGhyZWY9Imh0dHBzOi8vZG9pLnBhbmdhZWEuZGUvMTAuMTU5NC9QQU5HQUVBLjgzNjE3OD9mb3JtYXQ9Y2l0YXRpb25fYmlidGV4IiB0eXBlPSJhcHBsaWNhdGlvbi94LWJpYnRleCI+CjxsaW5rIHJlbD0iaXRlbSIgaHJlZj0iaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC9qb2hhbnNzb25fZXRhbC0yMDE0LnppcCIgdHlwZT0iYXBwbGljYXRpb24vemlwIj4KPGxpbmsgcmVsPSJhdXRob3IiIGhyZWY9Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMi02NTUzLTg5ODIiPgo8bGluayByZWw9ImF1dGhvciIgaHJlZj0iaHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAxLTYwNTgtMTQ2NiI+CjxzY3JpcHQgdHlwZT0iYXBwbGljYXRpb24vbGQranNvbiI+eyJAY29udGV4dCI6Imh0dHA6Ly9zY2hlbWEub3JnLyIsIkBpZCI6Imh0dHBzOi8vZG9pLm9yZy8xMC4xNTk0L1BBTkdBRUEuODM2MTc4IiwiQHR5cGUiOiJEYXRhc2V0IiwiaWRlbnRpZmllciI6Imh0dHBzOi8vZG9pLm9yZy8xMC4xNTk0L1BBTkdBRUEuODM2MTc4IiwidXJsIjoiaHR0cHM6Ly9kb2kucGFuZ2FlYS5kZS8xMC4xNTk0L1BBTkdBRUEuODM2MTc4IiwiY3JlYXRvciI6W3siQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoiSm9oYW5zc29uIiwiZ2l2ZW5OYW1lIjoiRW1tYSIsImVtYWlsIjoiZW1tYS5qb2hhbnNzb25Ac2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJCZXJnbHVuZCIsImdpdmVuTmFtZSI6IlN0ZW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6IkxpbmRib3JnIiwiZ2l2ZW5OYW1lIjoiVG9iaWFzIiwiZW1haWwiOiJ0b2JpYXMubGluZGJvcmdAc2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJQZXRyb25lIiwiZ2l2ZW5OYW1lIjoiSm9oYW5uZXMiLCJlbWFpbCI6ImpvaGFubmVzLnBldHJvbmVAc2tiLnNlIn0seyJAaWQiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIiwiQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoidmFuIEFzIiwiZ2l2ZW5OYW1lIjoiRGlyayIsImlkZW50aWZpZXIiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJHdXN0YWZzc29uIiwiZ2l2ZW5OYW1lIjoiTGFycy1Hw7ZyYW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6Ik7DpHNsdW5kIiwiZ2l2ZW5OYW1lIjoiSmVucy1PdmUifSx7IkBpZCI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYiLCJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJMYXVkb24iLCJnaXZlbk5hbWUiOiJIamFsbWFyIiwiaWRlbnRpZmllciI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYifV0sIm5hbWUiOiJIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgbGFrZSBuZWFyIEthbmdlcmx1c3N1YXEsIHdlc3QgR3JlZW5sYW5kIiwicHVibGlzaGVyIjp7IkB0eXBlIjoiT3JnYW5pemF0aW9uIiwibmFtZSI6IlBBTkdBRUEiLCJkaXNhbWJpZ3VhdGluZ0Rlc2NyaXB0aW9uIjoiRGF0YSBQdWJsaXNoZXIgZm9yIEVhcnRoICYgRW52aXJvbm1lbnRhbCBTY2llbmNlIiwidXJsIjoiaHR0cHM6Ly93d3cucGFuZ2FlYS5kZS8ifSwiaW5jbHVkZWRJbkRhdGFDYXRhbG9nIjp7IkB0eXBlIjoiRGF0YUNhdGFsb2ciLCJuYW1lIjoiUEFOR0FFQSIsImRpc2FtYmlndWF0aW5nRGVzY3JpcHRpb24iOiJEYXRhIFB1Ymxpc2hlciBmb3IgRWFydGggJiBFbnZpcm9ubWVudGFsIFNjaWVuY2UiLCJ1cmwiOiJodHRwczovL3d3dy5wYW5nYWVhLmRlLyJ9LCJkYXRlUHVibGlzaGVkIjoiMjAxNC0wOS0yNSIsImNpdGF0aW9uIjpbeyJAaWQiOiJodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIsIkB0eXBlIjoiUHVibGljYXRpb25Jc3N1ZSIsImlkZW50aWZpZXIiOiJodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIsInVybCI6Imh0dHBzOi8vZG9pLm9yZy8xMC41MTk0L2Vzc2QtNy05My0yMDE1IiwiY3JlYXRvciI6W3siQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoiSm9oYW5zc29uIiwiZ2l2ZW5OYW1lIjoiRW1tYSIsImVtYWlsIjoiZW1tYS5qb2hhbnNzb25Ac2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJCZXJnbHVuZCIsImdpdmVuTmFtZSI6IlN0ZW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6IkxpbmRib3JnIiwiZ2l2ZW5OYW1lIjoiVG9iaWFzIiwiZW1haWwiOiJ0b2JpYXMubGluZGJvcmdAc2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJQZXRyb25lIiwiZ2l2ZW5OYW1lIjoiSm9oYW5uZXMiLCJlbWFpbCI6ImpvaGFubmVzLnBldHJvbmVAc2tiLnNlIn0seyJAaWQiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIiwiQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoidmFuIEFzIiwiZ2l2ZW5OYW1lIjoiRGlyayIsImlkZW50aWZpZXIiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJHdXN0YWZzc29uIiwiZ2l2ZW5OYW1lIjoiTGFycy1Hw7ZyYW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6Ik7DpHNsdW5kIiwiZ2l2ZW5OYW1lIjoiSmVucy1PdmUifSx7IkBpZCI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYiLCJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJMYXVkb24iLCJnaXZlbk5hbWUiOiJIamFsbWFyIiwiaWRlbnRpZmllciI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYifV0sIm5hbWUiOiJIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldCIsImRhdGVQdWJsaXNoZWQiOiIyMDE1IiwiaXNzdWVOdW1iZXIiOiI3KDEpIiwicGFnaW5hdGlvbiI6IjkzLTEwOCIsImlzUGFydE9mIjp7IkB0eXBlIjoiQ3JlYXRpdmVXb3JrU2VyaWVzIiwibmFtZSI6IkVhcnRoIFN5c3RlbSBTY2llbmNlIERhdGEifX0seyJAaWQiOiJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L3R3b2JvYXRsYWtlX2dyZWVubGFuZC5qcGciLCJAdHlwZSI6IldlYlBhZ2UiLCJpZGVudGlmaWVyIjoiaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC90d29ib2F0bGFrZV9ncmVlbmxhbmQuanBnIiwidXJsIjoiaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC90d29ib2F0bGFrZV9ncmVlbmxhbmQuanBnIiwibmFtZSI6Ik1hcCBvZiBUd28gQm9hdCBMYWtlIGluIEdyZWVubGFuZCAoanBnIDEzIE1CKSB3aXRoIHBvc2l0aW9uIG9mIHNhbXBsaW5nIHNpdGVzIn0seyJAaWQiOiJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L1RpbWVsYXBzZV9UQkwuemlwIiwiQHR5cGUiOiJXZWJQYWdlIiwiaWRlbnRpZmllciI6Imh0dHA6Ly9zdG9yZS5wYW5nYWVhLmRlL1B1YmxpY2F0aW9ucy9Kb2hhbnNzb25FX2V0X2FsXzIwMTQvVGltZWxhcHNlX1RCTC56aXAiLCJ1cmwiOiJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L1RpbWVsYXBzZV9UQkwuemlwIiwibmFtZSI6IlRpbWUgbGFwcyBwaG90b3Mgb2YgbGFrZSAyMDEyLTA5LTA1IHRvIDIwMTMtMDgtMTQgKG1vdiBmaWxlLCB6aXBwZWQgMjA1IE1CKSJ9XSwiZGVzY3JpcHRpb24iOiJGZXcgaHlkcm9sb2dpY2FsIHN0dWRpZXMgaGF2ZSBiZWVuIG1hZGUgaW4gR3JlZW5sYW5kLCBvdGhlciB0aGFuIG9uIGdsYWNpYWwgaHlkcm9sb2d5IGFzc29jaWF0ZWQgd2l0aCB0aGUgaWNlIHNoZWV0LiBVbmRlcnN0YW5kaW5nIHBlcm1hZnJvc3QgaHlkcm9sb2d5IGFuZCBoeWRyb2NsaW1hdGljIGNoYW5nZSBhbmQgdmFyaWFiaWxpdHksIGhvd2V2ZXIsIHByb3ZpZGVzIGtleSBpbmZvcm1hdGlvbiBmb3IgdW5kZXJzdGFuZGluZyBjbGltYXRlIGNoYW5nZSBlZmZlY3RzIGFuZCBmZWVkYmFja3MgaW4gdGhlIEFyY3RpYyBsYW5kc2NhcGUuIFRoaXMgcGFwZXIgcHJlc2VudHMgYSBuZXcgZXh0ZW5zaXZlIGFuZCBkZXRhaWxlZCBoeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIG9wZW4gYWNjZXNzIGRhdGFzZXQsIHdpdGggaGlnaCB0ZW1wb3JhbCByZXNvbHV0aW9uIGZyb20gYSAxLjU2IGttKioyIHBlcm1hZnJvc3QgY2F0Y2htZW50IHdpdGggYSBsYWtlIHVuZGVybGFpbiBieSBhIHRocm91Z2ggdGFsaWsgY2xvc2UgdG8gdGhlIGljZSBzaGVldCBpbiB0aGUgS2FuZ2VybHVzc3VhcSByZWdpb24sIHdlc3Rlcm4gR3JlZW5sYW5kLiBUaGUgcGFwZXIgZGVzY3JpYmVzIHRoZSBoeWRyb2xvZ2ljYWwgc2l0ZSBpbnZlc3RpZ2F0aW9ucyBhbmQgdXRpbGl6ZWQgZXF1aXBtZW50LCBhcyB3ZWxsIGFzIHRoZSBkYXRhIGNvbGxlY3Rpb24gYW5kIHByb2Nlc3NpbmcuIFRoZSBpbnZlc3RpZ2F0aW9ucyB3ZXJlIHBlcmZvcm1lZCBiZXR3ZWVuIDIwMTAgYW5kIDIwMTMuIFRoZSBoaWdoIHNwYXRpYWwgcmVzb2x1dGlvbiwgd2l0aGluIHRoZSBpbnZlc3RpZ2F0ZWQgYXJlYSwgb2YgdGhlIGRhdGFzZXQgbWFrZXMgaXQgaGlnaGx5IHN1aXRhYmxlIGZvciB2YXJpb3VzIGRldGFpbGVkIGh5ZHJvbG9naWNhbCBhbmQgZWNvbG9naWNhbCBzdHVkaWVzIG9uIGNhdGNobWVudCBzY2FsZS4iLCJzcGF0aWFsQ292ZXJhZ2UiOnsiQHR5cGUiOiJQbGFjZSIsImdlbyI6eyJAdHlwZSI6Ikdlb0Nvb3JkaW5hdGVzIiwibGF0aXR1ZGUiOjY3LjEyNTk0LCJsb25naXR1ZGUiOi01MC4xODAzN319LCJpbkxhbmd1YWdlIjoiZW4iLCJsaWNlbnNlIjoiaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzMuMC8iLCJkaXN0cmlidXRpb24iOnsiQHR5cGUiOiJEYXRhRG93bmxvYWQiLCJmaWxlRm9ybWF0IjoiYXBwbGljYXRpb24vemlwIiwiY29udGVudFVybCI6Imh0dHA6Ly9zdG9yZS5wYW5nYWVhLmRlL1B1YmxpY2F0aW9ucy9Kb2hhbnNzb25FX2V0X2FsXzIwMTQvam9oYW5zc29uX2V0YWwtMjAxNC56aXAifX08L3NjcmlwdD4KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPi8qPCFbQ0RBVEFbKi8NCihmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCihpW3JdLnE9aVtyXS5xfHxbXSkucHVzaChhcmd1bWVudHMpfSxpW3JdLmw9MSpuZXcgRGF0ZSgpO2E9cy5jcmVhdGVFbGVtZW50KG8pLA0KbT1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKG8pWzBdO2EuYXN5bmM9MTthLnNyYz1nO20ucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYSxtKQ0KfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCcvL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KZ2EoJ2NyZWF0ZScsICdVQS0zMDYyNDE1MC0xJywgJ3BhbmdhZWEuZGUnKTsNCmdhKCdzZXQnLCAnYW5vbnltaXplSXAnLCB0cnVlKTsNCmdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQovKl1dPiovPC9zY3JpcHQ+DQo8L2hlYWQ+DQo8Ym9keSBjbGFzcz0iaG9tZXBhZ2UtbGF5b3V0Ij4NCjxkaXYgaWQ9ImhlYWRlci13cmFwcGVyIj4NCiAgPGRpdiBjbGFzcz0iY29udGFpbmVyLWZsdWlkIj4NCiAgICA8aGVhZGVyIGNsYXNzPSJyb3ciPjwhLS0gdm9sbGUgU2NyZWVuLUJyZWl0ZSAtLT4NCiAgICAgIDxkaXYgY2xhc3M9ImNvbnRlbnQtd3JhcHBlciI+PCEtLSBtYXguIEJyZWl0ZSAtLT4NCiAgICAgICAgPGRpdiBpZD0ibG9naW4tYXJlYS13cmFwcGVyIiBjbGFzcz0iaGlkZGVuLXByaW50Ij48ZGl2IGlkPSJsb2dpbi1hcmVhIj48c3BhbiBpZD0idXNlci1uYW1lIj5Ob3QgbG9nZ2VkIGluPC9zcGFuPjxhIGlkPSJzaWdudXAtYnV0dG9uIiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1wbHVzLXNpZ24gc2VsZi1yZWZlcmVyLWxpbmsiIHRpdGxlPSJTaWduIFVwIC8gQ3JlYXRlIEFjY291bnQiIGFyaWEtbGFiZWw9IlNpZ24gdXAiIHRhcmdldD0iX3NlbGYiIHJlbD0ibm9mb2xsb3ciIGhyZWY9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvdXNlci9zaWdudXAucGhwP3JlZmVyZXI9aHR0cHMlM0ElMkYlMkZ3d3cucGFuZ2FlYS5kZSUyRiIgZGF0YS10ZW1wbGF0ZT0iaHR0cHM6Ly93d3cucGFuZ2FlYS5kZS91c2VyL3NpZ251cC5waHA/cmVmZXJlcj0jdSMiPjwvYT48YSBpZD0ibG9naW4tYnV0dG9uIiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1sb2ctaW4gc2VsZi1yZWZlcmVyLWxpbmsiIHRpdGxlPSJMb2cgSW4iIGFyaWEtbGFiZWw9IkxvZyBpbiIgdGFyZ2V0PSJfc2VsZiIgcmVsPSJub2ZvbGxvdyIgaHJlZj0iaHR0cHM6Ly93d3cucGFuZ2FlYS5kZS91c2VyL2xvZ2luLnBocD9yZWZlcmVyPWh0dHBzJTNBJTJGJTJGd3d3LnBhbmdhZWEuZGUlMkYiIGRhdGEtdGVtcGxhdGU9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvdXNlci9sb2dpbi5waHA/cmVmZXJlcj0jdSMiPjwvYT48L2Rpdj48L2Rpdj4NCiAgICAgICAgPGRpdiBjbGFzcz0iYmxpbmRzcGFsdGUgaGVhZGVyLWJsb2NrIGNvbC1sZy0zIGNvbC1tZC00Ij48L2Rpdj4NCiAgICAgICAgDQogICAgICAgIDxkaXYgaWQ9ImhlYWRlci1sb2dvLWJsb2NrIiBjbGFzcz0iaGVhZGVyLWJsb2NrIGNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS00IGNvbC14cy04Ij4NCiAgICAgICAgICA8ZGl2IGlkPSJwYW5nYWVhLWxvZ28iPg0KICAgICAgICAgICAgPGEgdGl0bGU9IlBBTkdBRUEgaG9tZSIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8iIGNsYXNzPSJob21lLWxpbmsiPjxpbWcgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2xheW91dC1pbWFnZXMvcGFuZ2FlYS1sb2dvLnBuZyIgYWx0PSJQQU5HQUVBIGhvbWUiPjwvYT4NCiAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgPC9kaXY+DQogICAgICAgIA0KICAgICAgICA8ZGl2IGlkPSJoZWFkZXItbWlkLWJsb2NrIiBjbGFzcz0iaGVhZGVyLWJsb2NrIGNvbC1sZy0xMiBjb2wtbWQtOSBjb2wtc20tMjAgY29sLXhzLTE2Ij4NCiAgICAgICAgICA8ZGl2IGlkPSJwYW5nYWVhLWxvZ28taGVhZGxpbmUiPg0KICAgICAgICAgICAgUEFOR0FFQTxzcGFuIGNsYXNzPSJwdW5rdCI+Ljwvc3Bhbj4NCiAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICA8ZGl2IGlkPSJwYW5nYWVhLWxvZ28tc2xvZ2FuIj4NCiAgICAgICAgICAgIDxzcGFuPkRhdGEgUHVibGlzaGVyIGZvciBFYXJ0aCAmYW1wOyA8L3NwYW4+PHNwYW4gY2xhc3M9Im5vd3JhcCI+RW52aXJvbm1lbnRhbCBTY2llbmNlPC9zcGFuPg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDxkaXYgaWQ9InNlYXJjaC1hcmVhLWhlYWRlciIgY2xhc3M9InJvdyI+PC9kaXY+DQogICAgICAgIDwvZGl2Pg0KICAgICAgICANCiAgICAgICAgPGRpdiBpZD0iaGVhZGVyLW1haW4tbWVudS1ibG9jayIgY2xhc3M9ImhlYWRlci1ibG9jayBoaWRkZW4tcHJpbnQgY29sLWxnLTYgY29sLW1kLTcgY29sLXNtLTI0IGNvbC14cy0yNCI+DQogICAgICAgICAgPG5hdiBpZD0ibWFpbi1uYXYiPg0KICAgICAgICAgICAgPHVsPg0KICAgICAgICAgICAgICA8bGkgaWQ9Im1lbnUtc2VhcmNoIj4NCiAgICAgICAgICAgICAgICA8IS0tIGNsYXNzIG9uIGxpbmsgaXMgaW1wb3J0YW50LCBkb24ndCBjaGFuZ2UhISEgLS0+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8iIGNsYXNzPSJob21lLWxpbmsiPlNlYXJjaDwvYT4NCiAgICAgICAgICAgICAgPC9saT4NCiAgICAgICAgICAgICAgPGxpIGlkPSJtZW51LXN1Ym1pdCI+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9zdWJtaXQvIj5TdWJtaXQ8L2E+DQogICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICAgIDxsaSBpZD0ibWVudS1hYm91dCI+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9hYm91dC8iPkFib3V0PC9hPg0KICAgICAgICAgICAgICA8L2xpPg0KICAgICAgICAgICAgICA8bGkgaWQ9Im1lbnUtY29udGFjdCI+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9jb250YWN0LyI+Q29udGFjdDwvYT4NCiAgICAgICAgICAgICAgPC9saT4NCiAgICAgICAgICAgIDwvdWw+DQogICAgICAgICAgPC9uYXY+DQogICAgICAgICAgPGRpdiBjbGFzcz0iY2xlYXJmaXgiPjwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvaGVhZGVyPg0KICA8L2Rpdj4NCjwvZGl2Pg0KPGRpdiBpZD0iZmxleC13cmFwcGVyIj4NCjxkaXYgaWQ9Im1haW4tY29udGFpbmVyIiBjbGFzcz0iY29udGFpbmVyLWZsdWlkIj4NCjxkaXYgaWQ9Im1haW4tcm93IiBjbGFzcz0icm93IG1haW4tcm93Ij4NCjxkaXYgaWQ9Im1haW4iIGNsYXNzPSJjb2wtbGctMjQgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KPGRpdiBpZD0iZGF0YXNldCI+CjxkaXYgY2xhc3M9InJvdyI+PGRpdiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTI0IGNvbC14cy0yNCBoaWRkZW4teHMgaGlkZGVuLXNtIj48ZGl2IGNsYXNzPSJ0aXRsZSBjaXRhdGlvbiBpbnZpc2libGUtdG9wLWJvcmRlciI+Q2l0YXRpb246PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIHRvcC1ib3JkZXIiPjxkaXYgaWQ9ImdtYXAtZGF0YXNldC13cmFwcGVyIiBjbGFzcz0iZ21hcC13cmFwcGVyIGhpZGRlbi1wcmludCBoaWRkZW4teHMgaGlkZGVuLXNtIGNvbC1sZy04IGNvbC1tZC04IGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImVtYmVkLXJlc3BvbnNpdmUgZW1iZWQtcmVzcG9uc2l2ZS00YnkzIj48ZGl2IGlkPSJnbWFwLWRhdGFzZXQiIGNsYXNzPSJlbWJlZC1yZXNwb25zaXZlLWl0ZW0iPjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjxoMSBjbGFzcz0iaGFuZ2luZyBjaXRhdGlvbiI+PHN0cm9uZz48YSBjbGFzcz0icG9wb3Zlci1saW5rIGxpbmstdW5zdHlsZWQiIGhyZWY9IiMiIGRhdGEtdGl0bGU9IiZsdDtzcGFuJmd0O0pvaGFuc3NvbiwgRW1tYSZsdDthIGNsYXNzPSZxdW90O3NlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2gmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyByZWw9JnF1b3Q7bm9mb2xsb3cmcXVvdDsgdGl0bGU9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0pvaGFuc3NvbiwgRW1tYScuLi4mcXVvdDsgYXJpYS1sYWJlbD0mcXVvdDtTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnSm9oYW5zc29uLCBFbW1hJyZxdW90OyBocmVmPSZxdW90Oy8vd3d3LnBhbmdhZWEuZGUvP3E9YXV0aG9yJTNBZW1haWwlM0FlbW1hLmpvaGFuc3NvbiU0MHNrYi5zZSZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7bWFpbC1saW5rIHRleHQtbm93cmFwIHdpZGUtaWNvbi1saW5rJnF1b3Q7IGhyZWY9JnF1b3Q7bWFpbHRvOmVtbWEuam9oYW5zc29uQHNrYi5zZSZxdW90OyZndDtlbW1hLmpvaGFuc3NvbkBza2Iuc2UmbHQ7L2EmZ3Q7Jmx0Oy9kaXYmZ3Q7JiMxMDsmbHQ7L2RpdiZndDsmIzEwOyI+Sm9oYW5zc29uLCBFbW1hPC9hPjsgQmVyZ2x1bmQsIFN0ZW47IDxhIGNsYXNzPSJwb3BvdmVyLWxpbmsgbGluay11bnN0eWxlZCIgaHJlZj0iIyIgZGF0YS10aXRsZT0iJmx0O3NwYW4mZ3Q7TGluZGJvcmcsIFRvYmlhcyZsdDthIGNsYXNzPSZxdW90O3NlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2gmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyByZWw9JnF1b3Q7bm9mb2xsb3cmcXVvdDsgdGl0bGU9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0xpbmRib3JnLCBUb2JpYXMnLi4uJnF1b3Q7IGFyaWEtbGFiZWw9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0xpbmRib3JnLCBUb2JpYXMnJnF1b3Q7IGhyZWY9JnF1b3Q7Ly93d3cucGFuZ2FlYS5kZS8/cT1hdXRob3IlM0FlbWFpbCUzQXRvYmlhcy5saW5kYm9yZyU0MHNrYi5zZSZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7bWFpbC1saW5rIHRleHQtbm93cmFwIHdpZGUtaWNvbi1saW5rJnF1b3Q7IGhyZWY9JnF1b3Q7bWFpbHRvOnRvYmlhcy5saW5kYm9yZ0Bza2Iuc2UmcXVvdDsmZ3Q7dG9iaWFzLmxpbmRib3JnQHNrYi5zZSZsdDsvYSZndDsmbHQ7L2RpdiZndDsmIzEwOyZsdDsvZGl2Jmd0OyYjMTA7Ij5MaW5kYm9yZywgVG9iaWFzPC9hPjsgPGEgY2xhc3M9InBvcG92ZXItbGluayBsaW5rLXVuc3R5bGVkIiBocmVmPSIjIiBkYXRhLXRpdGxlPSImbHQ7c3BhbiZndDtQZXRyb25lLCBKb2hhbm5lcyZsdDthIGNsYXNzPSZxdW90O3NlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2gmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyByZWw9JnF1b3Q7bm9mb2xsb3cmcXVvdDsgdGl0bGU9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ1BldHJvbmUsIEpvaGFubmVzJy4uLiZxdW90OyBhcmlhLWxhYmVsPSZxdW90O1NlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdQZXRyb25lLCBKb2hhbm5lcycmcXVvdDsgaHJlZj0mcXVvdDsvL3d3dy5wYW5nYWVhLmRlLz9xPWF1dGhvciUzQWVtYWlsJTNBam9oYW5uZXMucGV0cm9uZSU0MHNrYi5zZSZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7bWFpbC1saW5rIHRleHQtbm93cmFwIHdpZGUtaWNvbi1saW5rJnF1b3Q7IGhyZWY9JnF1b3Q7bWFpbHRvOmpvaGFubmVzLnBldHJvbmVAc2tiLnNlJnF1b3Q7Jmd0O2pvaGFubmVzLnBldHJvbmVAc2tiLnNlJmx0Oy9hJmd0OyZsdDsvZGl2Jmd0OyYjMTA7Jmx0Oy9kaXYmZ3Q7JiMxMDsiPlBldHJvbmUsIEpvaGFubmVzPC9hPjsgPGEgY2xhc3M9InBvcG92ZXItbGluayBsaW5rLXVuc3R5bGVkIiBocmVmPSIjIiBkYXRhLXRpdGxlPSImbHQ7c3BhbiZndDt2YW4gQXMsIERpcmsmbHQ7YSBjbGFzcz0mcXVvdDtzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoJnF1b3Q7IHRhcmdldD0mcXVvdDtfYmxhbmsmcXVvdDsgcmVsPSZxdW90O25vZm9sbG93JnF1b3Q7IHRpdGxlPSZxdW90O1NlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICd2YW4gQXMsIERpcmsnLi4uJnF1b3Q7IGFyaWEtbGFiZWw9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ3ZhbiBBcywgRGlyaycmcXVvdDsgaHJlZj0mcXVvdDsvL3d3dy5wYW5nYWVhLmRlLz9xPWF1dGhvciUzQW9yY2lkJTNBMDAwMC0wMDAyLTY1NTMtODk4MiZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7b3JjaWQtbGluayB0ZXh0LW5vd3JhcCB3aWRlLWljb24tbGluayZxdW90OyB0YXJnZXQ9JnF1b3Q7X2JsYW5rJnF1b3Q7IGhyZWY9JnF1b3Q7aHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAyLTY1NTMtODk4MiZxdW90OyZndDtodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyJmx0Oy9hJmd0OyZsdDsvZGl2Jmd0OyYjMTA7Jmx0Oy9kaXYmZ3Q7JiMxMDsiPnZhbiBBcywgRGlyazwvYT47IEd1c3RhZnNzb24sIExhcnMtR8O2cmFuOyBOw6RzbHVuZCwgSmVucy1PdmU7IDxhIGNsYXNzPSJwb3BvdmVyLWxpbmsgbGluay11bnN0eWxlZCIgaHJlZj0iIyIgZGF0YS10aXRsZT0iJmx0O3NwYW4mZ3Q7TGF1ZG9uLCBIamFsbWFyJmx0O2EgY2xhc3M9JnF1b3Q7c2VhcmNobGluayBnbHlwaGljb24gZ2x5cGhpY29uLXNlYXJjaCZxdW90OyB0YXJnZXQ9JnF1b3Q7X2JsYW5rJnF1b3Q7IHJlbD0mcXVvdDtub2ZvbGxvdyZxdW90OyB0aXRsZT0mcXVvdDtTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnTGF1ZG9uLCBIamFsbWFyJy4uLiZxdW90OyBhcmlhLWxhYmVsPSZxdW90O1NlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdMYXVkb24sIEhqYWxtYXInJnF1b3Q7IGhyZWY9JnF1b3Q7Ly93d3cucGFuZ2FlYS5kZS8/cT1hdXRob3IlM0FvcmNpZCUzQTAwMDAtMDAwMS02MDU4LTE0NjYmcXVvdDsmZ3Q7Jmx0Oy9hJmd0OyZsdDsvc3BhbiZndDsiIGRhdGEtY29udGVudD0iJmx0O2RpdiZndDsmbHQ7ZGl2Jmd0OyZsdDthIGNsYXNzPSZxdW90O29yY2lkLWxpbmsgdGV4dC1ub3dyYXAgd2lkZS1pY29uLWxpbmsmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyBocmVmPSZxdW90O2h0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYmcXVvdDsmZ3Q7aHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAxLTYwNTgtMTQ2NiZsdDsvYSZndDsmbHQ7L2RpdiZndDsmIzEwOyZsdDsvZGl2Jmd0OyYjMTA7Ij5MYXVkb24sIEhqYWxtYXI8L2E+ICgyMDE0KTo8L3N0cm9uZz4gSHlkcm9sb2dpY2FsIGFuZCBtZXRlb3JvbG9naWNhbCBpbnZlc3RpZ2F0aW9ucyBpbiBhIGxha2UgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZC4gPGVtPlBBTkdBRUE8L2VtPiwgPGEgcmVsPSJub2ZvbGxvdyBib29rbWFyayIgaHJlZj0iaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgiIGRhdGEtdGl0bGU9IiZsdDtzcGFuJmd0O1BlcnNpc3RlbnQgRE9JIE5hbWUmbHQ7L3NwYW4mZ3Q7IiBkYXRhLWNvbnRlbnQ9IiZsdDtkaXYgY2xhc3M9JnF1b3Q7bGluay1kZXNjcmlwdGlvbiZxdW90OyZndDsmbHQ7cCZndDtBICZsdDthIGhyZWY9JnF1b3Q7aHR0cHM6Ly9kb2kub3JnLyZxdW90OyBjbGFzcz0mcXVvdDtkb2ktbGluayZxdW90OyB0YXJnZXQ9JnF1b3Q7X2JsYW5rJnF1b3Q7Jmd0O0RPSSBuYW1lJmx0Oy9hJmd0OyBzaGFsbCBiZSB1c2VkIHRvIGNpdGUgYW5kIGxpbmsgUEFOR0FFQSBkYXRhc2V0cy4mbHQ7L3AmZ3Q7JiMxMDsmbHQ7cCZndDtBICZsdDtiJmd0O0RPSSBuYW1lJmx0Oy9iJmd0OyBpcyBndWFyYW50ZWVkIHRvIG5ldmVyIGNoYW5nZSwgc28geW91IGNhbiB1c2UgaXQgdG8gbGluayBwZXJtYW5lbnRseSB0byBkYXRhc2V0cyBvciBkb2N1bWVudHMuICZsdDtiJmd0O0lmIHlvdSB3YW50IHRvIGNpdGUgdGhpcyBkYXRhc2V0LCB1c2UgdGhlIGZ1bGwgY2l0YXRpb24gYW5kIGFkZCB0aGlzIGxpbmsgYXMgYSBwZXJzaXN0ZW50IHJlZmVyZW5jZS4mbHQ7L2ImZ3Q7Jmx0Oy9wJmd0OyYjMTA7Jmx0O2RpdiZndDtZb3UgbWF5IHVzZSB5b3VyIGJyb3dzZXIncyAmbHQ7Y29kZSZndDtjb3B5IGxpbmsgbG9jYXRpb24mbHQ7L2NvZGUmZ3Q7IGZ1bmN0aW9uYWxpdHkgdG8gcmV0cmlldmUgdGhlIGxpbmshIFlvdSBjYW4gYWxzbyBkb3dubG9hZCB0aGUgY2l0YXRpb24gaW4gc2V2ZXJhbCBmb3JtYXRzIG9uIHRoaXMgcGFnZS4mbHQ7L2RpdiZndDsmIzEwOyZsdDsvZGl2Jmd0OyYjMTA7IiBjbGFzcz0idGV4dC1saW5rd3JhcCBwb3BvdmVyLWxpbmsgZG9pLWxpbmsiPmh0dHBzOi8vZG9pLm9yZy8xMC4xNTk0L1BBTkdBRUEuODM2MTc4PC9hPiw8aHIgY2xhc3M9InNwYWNlciIgYXJpYS1oaWRkZW49InRydWUiIC8+CjxlbT5TdXBwbGVtZW50IHRvOjwvZW0+IEpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTUpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldC4gPGVtPkVhcnRoIFN5c3RlbSBTY2llbmNlIERhdGE8L2VtPiwgPHN0cm9uZz43KDEpPC9zdHJvbmc+LCA5My0xMDgsIDxhIGNsYXNzPSJ0ZXh0LWxpbmt3cmFwIGRvaS1saW5rIiBocmVmPSJodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIgdGFyZ2V0PSJfYmxhbmsiPmh0dHBzOi8vZG9pLm9yZy8xMC41MTk0L2Vzc2QtNy05My0yMDE1PC9hPjwvaDE+CjxwIGNsYXNzPSJob3d0b2NpdGUiPjxzbWFsbD48c3BhbiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1idWxsaG9ybiI+PC9zcGFuPiA8c3Ryb25nPkFsd2F5cyBxdW90ZSBhYm92ZSBjaXRhdGlvbiB3aGVuIHVzaW5nIGRhdGEhPC9zdHJvbmc+IFlvdSBjYW4gZG93bmxvYWQgdGhlIGNpdGF0aW9uIGluIHNldmVyYWwgZm9ybWF0cyBiZWxvdy48L3NtYWxsPjwvcD4KPHAgY2xhc3M9ImRhdGEtYnV0dG9ucyI+PGEgcmVsPSJub2ZvbGxvdyBkZXNjcmliZWRieSIgdGl0bGU9IkV4cG9ydCBjaXRhdGlvbiB0byBSZWZlcmVuY2UgTWFuYWdlciwgRW5kTm90ZSwgUHJvQ2l0ZSIgaHJlZj0iP2Zvcm1hdD1jaXRhdGlvbl9yaXMiIGNsYXNzPSJhY3Rpb25idXR0b25saW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj5SSVMgQ2l0YXRpb248L3NwYW4+PC9hPjxhIHJlbD0ibm9mb2xsb3cgZGVzY3JpYmVkYnkiIHRpdGxlPSJFeHBvcnQgY2l0YXRpb24gdG8gQmliVGVYIiBocmVmPSI/Zm9ybWF0PWNpdGF0aW9uX2JpYnRleCIgY2xhc3M9ImFjdGlvbmJ1dHRvbmxpbmsiPjxzcGFuIGNsYXNzPSJhY3Rpb25idXR0b24iPjxzcGFuIHN0eWxlPSJmb250LXZhcmlhbnQ6c21hbGwtY2FwczsiPkJpYlRlWDwvc3Bhbj4gQ2l0YXRpb248L3NwYW4+PC9hPjxhIHJlbD0ibm9mb2xsb3ciIHRpdGxlPSJFeHBvcnQgY2l0YXRpb24gYXMgcGxhaW4gdGV4dCIgaHJlZj0iP2Zvcm1hdD1jaXRhdGlvbl90ZXh0IiB0YXJnZXQ9Il9ibGFuayIgY2xhc3M9ImFjdGlvbmJ1dHRvbmxpbmsgc2hhcmUtbGluayI+PHNwYW4gY2xhc3M9ImFjdGlvbmJ1dHRvbiI+VGV4dCBDaXRhdGlvbjwvc3Bhbj48L2E+PHNwYW4gY2xhc3M9InNlcGFyYXRvciI+PC9zcGFuPjxhIHJlbD0ibm9mb2xsb3ciIGNsYXNzPSJzZWxmLXJlZmVyZXItbGluayBzaGFyZS1saW5rIGFjdGlvbmJ1dHRvbmxpbmsiIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvbm9qcy5waHAiIGRhdGEtdGVtcGxhdGU9Imh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9zaGFyZXIucGhwP3U9I3UjJmFtcDt0PSN0IyIgdGl0bGU9IlNoYXJlIGRhdGFzZXQgb24gRmFjZWJvb2siIHRhcmdldD0iX2JsYW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj48c3BhbiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1zaGFyZSI+PC9zcGFuPiBGYWNlYm9vazwvc3Bhbj48L2E+PGEgcmVsPSJub2ZvbGxvdyIgY2xhc3M9InNlbGYtcmVmZXJlci1saW5rIHNoYXJlLWxpbmsgYWN0aW9uYnV0dG9ubGluayIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9ub2pzLnBocCIgZGF0YS10ZW1wbGF0ZT0iaHR0cHM6Ly90d2l0dGVyLmNvbS9pbnRlbnQvdHdlZXQ/dXJsPSN1IyZhbXA7dGV4dD0jdCMiIHRpdGxlPSJTaGFyZSBkYXRhc2V0IG9uIFR3aXR0ZXIiIHRhcmdldD0iX2JsYW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj48c3BhbiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1zaGFyZSI+PC9zcGFuPiBUd2l0dGVyPC9zcGFuPjwvYT48YSByZWw9Im5vZm9sbG93IiBjbGFzcz0ic2VsZi1yZWZlcmVyLWxpbmsgc2hhcmUtbGluayBhY3Rpb25idXR0b25saW5rIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL25vanMucGhwIiBkYXRhLXRlbXBsYXRlPSJodHRwczovL3BsdXMuZ29vZ2xlLmNvbS9zaGFyZT91cmw9I3UjJmFtcDtobD1lbiIgdGl0bGU9IlNoYXJlIGRhdGFzZXQgb24gR29vZ2xlKyIgdGFyZ2V0PSJfYmxhbmsiPjxzcGFuIGNsYXNzPSJhY3Rpb25idXR0b24iPjxzcGFuIGNsYXNzPSJnbHlwaGljb24gZ2x5cGhpY29uLXNoYXJlIj48L3NwYW4+IEdvb2dsZSs8L3NwYW4+PC9hPjxzcGFuIGNsYXNzPSJzZXBhcmF0b3IiPjwvc3Bhbj48YSByZWw9Im5vZm9sbG93IiB0YXJnZXQ9Il9ibGFuayIgdGl0bGU9IkRpc3BsYXkgZXZlbnRzIGluIG1hcCIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9hZHZhbmNlZC9nbWFwLWRhdGFzZXQucGhwP2lkPTgzNjE3OCZhbXA7dmlld3BvcnRCQk9YPS01MC4xODAzNyw2Ny4xMjU5NCwtNTAuMTgwMzcsNjcuMTI1OTQiIGNsYXNzPSJhY3Rpb25idXR0b25saW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj5TaG93IE1hcDwvc3Bhbj48L2E+PGEgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IkRpc3BsYXkgZXZlbnRzIGluIEdvb2dsZSBFYXJ0aCIgaHJlZj0iP2Zvcm1hdD1ldmVudHNfa21sIiBjbGFzcz0iYWN0aW9uYnV0dG9ubGluayI+PHNwYW4gY2xhc3M9ImFjdGlvbmJ1dHRvbiI+R29vZ2xlIEVhcnRoPC9zcGFuPjwvYT48c3BhbiBjbGFzcz0ic2VwYXJhdG9yIj48L3NwYW4+PHNwYW4gZGF0YS1iYWRnZS10eXBlPSIxIiBkYXRhLWRvaT0iMTAuMTU5NC9QQU5HQUVBLjgzNjE3OCIgZGF0YS1iYWRnZS1wb3BvdmVyPSJyaWdodCIgZGF0YS1oaWRlLW5vLW1lbnRpb25zPSJ0cnVlIiBjbGFzcz0iYWx0bWV0cmljLWVtYmVkIj48L3NwYW4+PC9wPgo8ZGl2IGNsYXNzPSJjbGVhcmZpeCI+PC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9InJvdyI+PGRpdiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0idGl0bGUiPkFic3RyYWN0OjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0iY29sLWxnLTIxIGNvbC1tZC0yMCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJkZXNjciI+PGRpdiBjbGFzcz0iYWJzdHJhY3QiPkZldyBoeWRyb2xvZ2ljYWwgc3R1ZGllcyBoYXZlIGJlZW4gbWFkZSBpbiBHcmVlbmxhbmQsIG90aGVyIHRoYW4gb24gZ2xhY2lhbCBoeWRyb2xvZ3kgYXNzb2NpYXRlZCB3aXRoIHRoZSBpY2Ugc2hlZXQuIFVuZGVyc3RhbmRpbmcgcGVybWFmcm9zdCBoeWRyb2xvZ3kgYW5kIGh5ZHJvY2xpbWF0aWMgY2hhbmdlIGFuZCB2YXJpYWJpbGl0eSwgaG93ZXZlciwgcHJvdmlkZXMga2V5IGluZm9ybWF0aW9uIGZvciB1bmRlcnN0YW5kaW5nIGNsaW1hdGUgY2hhbmdlIGVmZmVjdHMgYW5kIGZlZWRiYWNrcyBpbiB0aGUgQXJjdGljIGxhbmRzY2FwZS4gVGhpcyBwYXBlciBwcmVzZW50cyBhIG5ldyBleHRlbnNpdmUgYW5kIGRldGFpbGVkIGh5ZHJvbG9naWNhbCBhbmQgbWV0ZW9yb2xvZ2ljYWwgb3BlbiBhY2Nlc3MgZGF0YXNldCwgd2l0aCBoaWdoIHRlbXBvcmFsIHJlc29sdXRpb24gZnJvbSBhIDEuNTYga20qKjIgcGVybWFmcm9zdCBjYXRjaG1lbnQgd2l0aCBhIGxha2UgdW5kZXJsYWluIGJ5IGEgdGhyb3VnaCB0YWxpayBjbG9zZSB0byB0aGUgaWNlIHNoZWV0IGluIHRoZSBLYW5nZXJsdXNzdWFxIHJlZ2lvbiwgd2VzdGVybiBHcmVlbmxhbmQuIFRoZSBwYXBlciBkZXNjcmliZXMgdGhlIGh5ZHJvbG9naWNhbCBzaXRlIGludmVzdGlnYXRpb25zIGFuZCB1dGlsaXplZCBlcXVpcG1lbnQsIGFzIHdlbGwgYXMgdGhlIGRhdGEgY29sbGVjdGlvbiBhbmQgcHJvY2Vzc2luZy4gVGhlIGludmVzdGlnYXRpb25zIHdlcmUgcGVyZm9ybWVkIGJldHdlZW4gMjAxMCBhbmQgMjAxMy4gVGhlIGhpZ2ggc3BhdGlhbCByZXNvbHV0aW9uLCB3aXRoaW4gdGhlIGludmVzdGlnYXRlZCBhcmVhLCBvZiB0aGUgZGF0YXNldCBtYWtlcyBpdCBoaWdobHkgc3VpdGFibGUgZm9yIHZhcmlvdXMgZGV0YWlsZWQgaHlkcm9sb2dpY2FsIGFuZCBlY29sb2dpY2FsIHN0dWRpZXMgb24gY2F0Y2htZW50IHNjYWxlLjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJyb3ciPjxkaXYgY2xhc3M9ImNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9InRpdGxlIj5GdXJ0aGVyIGRldGFpbHM6PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIj48ZGl2IGNsYXNzPSJoYW5naW5nIj48YSB0YXJnZXQ9Il9zZWxmIiBocmVmPSJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L3R3b2JvYXRsYWtlX2dyZWVubGFuZC5qcGciPk1hcCBvZiBUd28gQm9hdCBMYWtlIGluIEdyZWVubGFuZCAoanBnIDEzIE1CKSB3aXRoIHBvc2l0aW9uIG9mIHNhbXBsaW5nIHNpdGVzPC9hPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24uLi4iIGFyaWEtbGFiZWw9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24iIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvP3E9JTQwcmVmNjU0NzciPjwvYT48L2Rpdj4KPGRpdiBjbGFzcz0iaGFuZ2luZyI+PGEgdGFyZ2V0PSJfc2VsZiIgaHJlZj0iaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC9UaW1lbGFwc2VfVEJMLnppcCI+VGltZSBsYXBzIHBob3RvcyBvZiBsYWtlIDIwMTItMDktMDUgdG8gMjAxMy0wOC0xNCAobW92IGZpbGUsIHppcHBlZCAyMDUgTUIpPC9hPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24uLi4iIGFyaWEtbGFiZWw9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24iIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvP3E9JTQwcmVmNjU0MDgiPjwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+UHJvamVjdChzKTo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImhhbmdpbmciPjxzdHJvbmc+PGEgdGFyZ2V0PSJfYmxhbmsiIGhyZWY9Imh0dHBzOi8vd3d3LnJlc2VhcmNoZ2F0ZS5uZXQvcHJvamVjdC9HUmVlbmxhbmQtQW5hbG9ndWUtU3VyZmFjZS1Qcm9qZWN0LUdSQVNQIj5HUmVlbmxhbmQgQW5hbG9ndWUgU3VyZmFjZSBQcm9qZWN0PC9hPjwvc3Ryb25nPiAoR1JBU1ApPGEgY2xhc3M9InNlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2giIHRhcmdldD0iX2JsYW5rIiByZWw9Im5vZm9sbG93IiB0aXRsZT0iU2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0dSZWVubGFuZCBBbmFsb2d1ZSBTdXJmYWNlIFByb2plY3QnLi4uIiBhcmlhLWxhYmVsPSJTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnR1JlZW5sYW5kIEFuYWxvZ3VlIFN1cmZhY2UgUHJvamVjdCciIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvP3E9cHJvamVjdCUzQWxhYmVsJTNBR1JBU1AiPjwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+Q292ZXJhZ2U6PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIj48ZGl2IGNsYXNzPSJoYW5naW5nIGdlbyI+PGVtIGNsYXNzPSJ1bmZhcmJlIj5MYXRpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibGF0aXR1ZGUiPjY3LjEyNTk0MDwvc3Bhbj48ZW0gY2xhc3M9InVuZmFyYmUiPiAqIExvbmdpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibG9uZ2l0dWRlIj4tNTAuMTgwMzcwPC9zcGFuPjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJyb3ciPjxkaXYgY2xhc3M9ImNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9InRpdGxlIj5FdmVudChzKTo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImhhbmdpbmcgZ2VvIj48c3Ryb25nPlRCTDwvc3Ryb25nPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdUQkwnLi4uIiBhcmlhLWxhYmVsPSJTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnVEJMJyIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8/cT1ldmVudCUzQWxhYmVsJTNBVEJMIj48L2E+PGVtIGNsYXNzPSJ1bmZhcmJlIj4gKiBMYXRpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibGF0aXR1ZGUiPjY3LjEyNTk0MDwvc3Bhbj48ZW0gY2xhc3M9InVuZmFyYmUiPiAqIExvbmdpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibG9uZ2l0dWRlIj4tNTAuMTgwMzcwPC9zcGFuPjxlbSBjbGFzcz0idW5mYXJiZSI+ICogTG9jYXRpb246IDwvZW0+PHNwYW4+VHdvIEJvYXQgTGFrZSwgS2FuZ2VybHVzc3VhcSwgR3JlZW5sYW5kPC9zcGFuPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdUd28gQm9hdCBMYWtlLCBLYW5nZXJsdXNzdWFxLCBHcmVlbmxhbmQnLi4uIiBhcmlhLWxhYmVsPSJTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnVHdvIEJvYXQgTGFrZSwgS2FuZ2VybHVzc3VhcSwgR3JlZW5sYW5kJyIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8/cT1sb2NhdGlvbiUzQSUyMlR3bytCb2F0K0xha2UlMkMrS2FuZ2VybHVzc3VhcSUyQytHcmVlbmxhbmQlMjIiPjwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+Q29tbWVudDo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImFic3RyYWN0Ij5UaGUgZGF0YXNldCBjb250YWlucyBoeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGRhdGEgZnJvbSBhIGxha2UgY2F0Y2htZW50IGluIHRoZSBLYW5nZXJsdXNzdWFxIHJlZ2lvbiwgV2VzdGVybiBHcmVlbmxhbmQuIFRoZSBpbnZlc3RpZ2F0aW9ucyB3ZXJlIHBlcmZvcm1lZCBkdXJpbmcgMjAxMC0yMDEzIGFuZCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgYXJlIGluY2x1ZGVkOiBTb2lsIG1vaXN0dXJlLCBTb2lsIHRlbXBlcmF0dXJlLCBIeWRyYXVsaWMgcHJvcGVydGllcyBvZiB0aGUgYWN0aXZlIGxheWVyLCBtZXRlb3JvbG9naWNhbCBwYXJhbWV0ZXJzIGZyb20gYSBsb2NhbCB3ZWF0aGVyIHN0YXRpb24gd2l0aGluIHRoZSBjYXRjaG1lbnQsIHdhdGVyIGxldmVscyBhbmQgZGlzY2hhcmdlLCBzdWJsaW1hdGlvbiBhbmQgZXZhcG9ydGF0aW9uIG1lYXN1cm1lbnRzLCBzbm93IGRlcHRoIGFuZCBzbm93IHdhdGVyIGNvbnRlbnQgZGF0YSBhbmQgdGltZSBsYXBzZSBwaG90b3MuPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9InJvdyI+PGRpdiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0idGl0bGUiPkxpY2Vuc2U6PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIj48ZGl2IGNsYXNzPSJoYW5naW5nIj48YSBocmVmPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLyIgcmVsPSJsaWNlbnNlIiB0YXJnZXQ9Il9ibGFuayI+PGltZyBzcmM9Ii8vd3d3LnBhbmdhZWEuZGUvc2hhcmVkL3BpY3MvbGljZW5zZXMvQ0MtQlktMy4wLnBuZyIgc3R5bGU9InZlcnRpY2FsLWFsaWduOmJhc2VsaW5lOyBib3JkZXItd2lkdGg6MDsiIGFsdD0iQ0MtQlktMy4wIiAvPiBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIDMuMCBVbnBvcnRlZDwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+U2l6ZTo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImhhbmdpbmciPjU2NjMuMCBrQnl0ZXM8L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQgY29sLWxnLW9mZnNldC0zIGNvbC1tZC1vZmZzZXQtNCI+PGRpdiBjbGFzcz0idGV4dC1ibG9jayB0b3AtYm9yZGVyIj4KPGgyIGlkPSJkb3dubG9hZCI+RG93bmxvYWQgRGF0YTwvaDI+CjxwPjxhIGhyZWY9Imh0dHA6Ly9zdG9yZS5wYW5nYWVhLmRlL1B1YmxpY2F0aW9ucy9Kb2hhbnNzb25FX2V0X2FsXzIwMTQvam9oYW5zc29uX2V0YWwtMjAxNC56aXAiIHRhcmdldD0iX3NlbGYiPkRvd25sb2FkIGRhdGFzZXQ8L2E+PC9wPgo8L2Rpdj48L2Rpdj48L2Rpdj48ZGl2IGlkPSJyZWNvbW1lbmRhdGlvbnMiPjwvZGl2Pgo8L2Rpdj4NCjwvZGl2Pg0KPC9kaXY+DQo8L2Rpdj4NCjwvZGl2Pg0KPGRpdiBpZD0iZm9vdGVyLXdyYXBwZXIiIGNsYXNzPSJ0b3AtYm9yZGVyIGhpZGRlbi1wcmludCI+DQogIDxkaXYgY2xhc3M9ImNvbnRhaW5lci1mbHVpZCI+DQogICAgPGZvb3RlciBjbGFzcz0icm93Ij48IS0tIHZvbGxlIFNjcmVlbi1CcmVpdGUgLS0+DQogICAgICA8ZGl2IGNsYXNzPSJjb250ZW50LXdyYXBwZXIiPjwhLS0gbWF4LiBCcmVpdGUgLS0+DQogICAgICAgIDxkaXYgY2xhc3M9ImJsaW5kc3BhbHRlIGNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS00IGNvbC14cy00Ij48L2Rpdj4NCiAgICAgICAgPGRpdiBpZD0iZm9vdGVyLWhvc3RlZC1ieS1hcmVhIiBjbGFzcz0iY29sLWxnLTE4IGNvbC1tZC05IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgIDwhLS08ZGl2IGNsYXNzPSJjb2wtbGctMjQgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPi0tPg0KICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbC1sZy0xMiBjb2wtbWQtMjQgY29sLXNtLTI0IGNvbC14cy0yNCI+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJoZWFkbGluZSB1bmRlcmxpbmVkIj4NCiAgICAgICAgICAgICAgUEFOR0FFQSBpcyBob3N0ZWQgYnkNCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgDQogICAgICAgICAgICA8ZGl2Pg0KICAgICAgICAgICAgICA8cD4NCiAgICAgICAgICAgICAgICBBbGZyZWQgV2VnZW5lciBJbnN0aXR1dGUsIEhlbG1ob2x0eiBDZW50ZXIgZm9yIFBvbGFyIGFuZCBNYXJpbmUgUmVzZWFyY2ggKEFXSSk8YnIvPg0KICAgICAgICAgICAgICAgIENlbnRlciBmb3IgTWFyaW5lIEVudmlyb25tZW50YWwgU2NpZW5jZXMsIFVuaXZlcnNpdHkgb2YgQnJlbWVuIChNQVJVTSkNCiAgICAgICAgICAgICAgPC9wPg0KICAgICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImhlYWRsaW5lIHVuZGVybGluZWQiPg0KICAgICAgICAgICAgICBUaGUgU3lzdGVtIGlzIHN1cHBvcnRlZCBieQ0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICANCiAgICAgICAgICAgIDxkaXY+DQogICAgICAgICAgICAgIDxwPg0KICAgICAgICAgICAgICAgIFRoZSBFdXJvcGVhbiBDb21taXNzaW9uLCBSZXNlYXJjaDxici8+DQogICAgICAgICAgICAgICAgRmVkZXJhbCBNaW5pc3RyeSBvZiBFZHVjYXRpb24gYW5kIFJlc2VhcmNoIChCTUJGKTxici8+DQogICAgICAgICAgICAgICAgRGV1dHNjaGUgRm9yc2NodW5nc2dlbWVpbnNjaGFmdCAoREZHKTxici8+DQogICAgICAgICAgICAgICAgSW50ZXJuYXRpb25hbCBPY2VhbiBEaXNjb3ZlcnkgUHJvZ3JhbSAoSU9EUCkNCiAgICAgICAgICAgICAgPC9wPg0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICA8ZGl2IGNsYXNzPSJjb2wtbGctMTIgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaGVhZGxpbmUgdW5kZXJsaW5lZCI+DQogICAgICAgICAgICAgIFBBTkdBRUEgaXMgbWVtYmVyIG9mDQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIA0KICAgICAgICAgICAgPGRpdj4NCiAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cuaWNzdS13ZHMub3JnLyIgdGFyZ2V0PSJfYmxhbmsiIHRpdGxlPSJJQ1NVIFdvcmxkIERhdGEgU3lzdGVtIj4NCiAgICAgICAgICAgICAgICA8aW1nIGNsYXNzPSJjb2wtbGctNiBjb2wtbWQtNiBjb2wtc20tNiBjb2wteHMtNiIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2xvZ29zL2xvZ28td2RzLWJsb2NrLnBuZyIgYWx0PSJJQ1NVIFdvcmxkIERhdGEgU3lzdGVtIj4NCiAgICAgICAgICAgICAgPC9hPg0KICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy53bW8uaW50LyIgdGFyZ2V0PSJfYmxhbmsiIHRpdGxlPSJXb3JsZCBNZXRlb3JvbG9naWNhbCBPcmdhbml6YXRpb24iPg0KICAgICAgICAgICAgICAgIDxpbWcgY2xhc3M9ImNvbC1sZy02IGNvbC1tZC02IGNvbC1zbS02IGNvbC14cy02IiBzcmM9Ii8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3YuODQ3NWExMmQwNTQxMTMxN2YzZDk5MzU5YjAxN2EyNjMvbG9nb3MvbG9nby13bW8tYmxvY2sucG5nIiBhbHQ9IldvcmxkIE1ldGVvcm9sb2dpY2FsIE9yZ2FuaXphdGlvbiI+DQogICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgICAgPGRpdiBpZD0iZm9vdGVyLXNvY2lhbC1hcmVhIiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgIDxkaXYgaWQ9ImZvb3Rlci1zb2NpYWwtYXJlYS13cmFwcGVyIiBjbGFzcz0iY29sLWxnLTI0IGNvbC1tZC0yNCBjb2wtc20tMjQgY29sLXhzLTI0Ij4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImJsaW5kc3BhbHRlIGNvbC1sZy0wIGNvbC1tZC00Ij48L2Rpdj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbC1sZy0yNCBjb2wtbWQtNSBjb2wtbWQtNSBjb2wteHMtMTAiPg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ1bmRlcmxpbmVkIj5TaGFyZSBvbi4uLjwvZGl2Pg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJzb2NpYWwtaWNvbnMiPg0KICAgICAgICAgICAgICAgIDxhIHJlbD0ibm9mb2xsb3ciIGNsYXNzPSJzZWxmLXJlZmVyZXItbGluayBzaGFyZS1saW5rIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL25vanMucGhwIiBkYXRhLXRlbXBsYXRlPSJodHRwczovL3d3dy5mYWNlYm9vay5jb20vc2hhcmVyLnBocD91PSN1IyZhbXA7dD0jdCMiIHRpdGxlPSJTaGFyZSBvbiBGYWNlYm9vayIgdGFyZ2V0PSJfYmxhbmsiPg0KICAgICAgICAgICAgICAgICAgPGltZyBpZD0iZmFjZWJvb2staWNvbiIgY2xhc3M9ImNvbC1sZy04IGNvbC1tZC04IGNvbC1zbS04IGNvbC14cy04IiBzcmM9Ii8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3YuODQ3NWExMmQwNTQxMTMxN2YzZDk5MzU5YjAxN2EyNjMvc29jaWFsLWljb25zL2ZhY2Vib29rLWljb24ucG5nIiBhbHQ9IkZhY2Vib29rIEljb24iPg0KICAgICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgICAgICA8YSByZWw9Im5vZm9sbG93IiBjbGFzcz0ic2VsZi1yZWZlcmVyLWxpbmsgc2hhcmUtbGluayIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9ub2pzLnBocCIgZGF0YS10ZW1wbGF0ZT0iaHR0cHM6Ly90d2l0dGVyLmNvbS9pbnRlbnQvdHdlZXQ/dXJsPSN1IyZhbXA7dGV4dD0jdCMiIHRpdGxlPSJTaGFyZSBvbiBUd2l0dGVyIiB0YXJnZXQ9Il9ibGFuayI+DQogICAgICAgICAgICAgICAgICA8aW1nIGlkPSJ0d2l0dGVyLWljb24iIGNsYXNzPSJjb2wtbGctOCBjb2wtbWQtOCBjb2wtc20tOCBjb2wteHMtOCIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL3NvY2lhbC1pY29ucy90d2l0dGVyLWljb24ucG5nIiBhbHQ9IlR3aXR0ZXIgSWNvbiI+DQogICAgICAgICAgICAgICAgPC9hPg0KICAgICAgICAgICAgICAgIDxhIHJlbD0ibm9mb2xsb3ciIGNsYXNzPSJzZWxmLXJlZmVyZXItbGluayBzaGFyZS1saW5rIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL25vanMucGhwIiBkYXRhLXRlbXBsYXRlPSJodHRwczovL3BsdXMuZ29vZ2xlLmNvbS9zaGFyZT91cmw9I3UjJmFtcDtobD1lbiIgdGl0bGU9IlNoYXJlIG9uIEdvb2dsZSsiIHRhcmdldD0iX2JsYW5rIj4NCiAgICAgICAgICAgICAgICAgIDxpbWcgaWQ9ImdwbHVzLWljb24iIGNsYXNzPSJjb2wtbGctOCBjb2wtbWQtOCBjb2wtc20tOCBjb2wteHMtOCIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL3NvY2lhbC1pY29ucy9ncGx1cy1pY29uLnBuZyIgYWx0PSJHb29nbGUrIEljb24iPg0KICAgICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImJsaW5kc3BhbHRlIGNvbG8tbGctMCBjb2wtbWQtMTgiPjwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgICAgICANCiAgICAgICAgPGRpdiBpZD0iZm9vdGVyLW1lbnUtYXJlYSIgY2xhc3M9ImNvbC1sZy0yNCBjb2wtbWQtMjQgY29sLXNtLTI0IGNvbC14cy0yNCI+DQogICAgICAgICAgPGRpdiBjbGFzcz0iYmxpbmRzcGFsdGUgY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTQgY29sLXhzLTQiPjwvZGl2Pg0KICAgICAgICAgIDxkaXYgaWQ9ImZvb3Rlci1tZW51LXdyYXBwZXIiIGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgICAgPG5hdiBpZD0iZm9vdGVyLW5hdiI+DQogICAgICAgICAgICAgIDx1bD4NCiAgICAgICAgICAgICAgICA8bGkgaWQ9ImFib3V0LWxlZ2FsLW5vdGljZSI+DQogICAgICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fib3V0L2xlZ2FsLnBocCI+TGVnYWwgbm90aWNlPC9hPg0KICAgICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICAgICAgPGxpIGlkPSJhYm91dC1wcml2YWN5LXBvbGljeSI+DQogICAgICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fib3V0L3ByaXZhY3lwb2xpY3kucGhwIj5Qcml2YWN5IHBvbGljeTwvYT4NCiAgICAgICAgICAgICAgICA8L2xpPg0KICAgICAgICAgICAgICAgIDxsaSBpZD0iYWJvdXQtY29va2llcyI+DQogICAgICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fib3V0L2Nvb2tpZXMucGhwIj5Db29raWVzPC9hPg0KICAgICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICAgICAgPGxpIGlkPSJhYm91dC1jb250YWN0Ij4NCiAgICAgICAgICAgICAgICAgIDxhIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvY29udGFjdC8iPkNvbnRhY3Q8L2E+DQogICAgICAgICAgICAgICAgPC9saT4NCiAgICAgICAgICAgICAgPC91bD4NCiAgICAgICAgICAgIDwvbmF2Pg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2xlYXJmaXgiPjwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvZm9vdGVyPg0KICA8L2Rpdj4NCjwvZGl2Pg0KPC9ib2R5Pgo8L2h0bWw+Cg== + http_version: + recorded_at: Thu, 29 Nov 2018 12:17:36 GMT +- request: + method: get + uri: https://doi.pangaea.de/10.1594/PANGAEA.836178 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Server: + - PANGAEA/1.0 + Date: + - Thu, 29 Nov 2018 12:17:36 GMT + Vary: + - Accept + Link: + - ;rel="cite-as", ;rel="describedby";type="application/ld+json", + ;rel="describedby";type="application/x-research-info-systems", + ;rel="describedby";type="application/x-bibtex", + ;rel="item";type="application/zip", + ;rel="author", ;rel="author" + Content-Type: + - text/html;charset=UTF-8 + X-Ua-Compatible: + - IE=Edge + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + body: + encoding: ASCII-8BIT + string: !binary |- + PCFET0NUWVBFIGh0bWw+DQo8aHRtbCBsYW5nPSJlbiI+DQo8aGVhZD4KPG1ldGEgY2hhcnNldD0iVVRGLTgiPg0KPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLCBtaW5pbXVtLXNjYWxlPTEsIG1heGltdW0tc2NhbGU9MSwgdXNlci1zY2FsYWJsZT1ubyI+DQo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzP2ZhbWlseT1PcGVuK1NhbnM6NDAwLDYwMCw0MDBpdGFsaWMsNzAwLDcwMGl0YWxpYyw2MDBpdGFsaWMsMzAwLDMwMGl0YWxpYyw4MDAsODAwaXRhbGljIj4KPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2Jvb3RzdHJhcC0yNGNvbC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiPgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3YuODQ3NWExMmQwNTQxMTMxN2YzZDk5MzU5YjAxN2EyNjMvY3NzL3BhbmdhZWEuY3NzIj4KPCEtLVtpZiBsdGUgSUUgOV0+DQo8c3R5bGU+I3RvcGljcy1wdWxsZG93bi13cmFwcGVyIGxhYmVsOmFmdGVyIHsgZGlzcGxheTpub25lOyB9PC9zdHlsZT4NCjwhW2VuZGlmXS0tPg0KPGxpbmsgcmVsPSJzaG9ydGN1dCBpY29uIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2Zhdmljb24uaWNvIj4NCjxsaW5rIHJlbD0iaWNvbiIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9hc3NldHMvdi44NDc1YTEyZDA1NDExMzE3ZjNkOTkzNTliMDE3YTI2My9mYXZpY29uLmljbyIgdHlwZT0iaW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uIj4NCjxsaW5rIHJlbD0iaW1hZ2Vfc3JjIiB0eXBlPSJpbWFnZS9wbmciIGhyZWY9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3NvY2lhbC1pY29ucy9wYW5nYWVhLXNoYXJlLnBuZyI+DQo8bWV0YSBwcm9wZXJ0eT0ib2c6aW1hZ2UiIGNvbnRlbnQ9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3NvY2lhbC1pY29ucy9wYW5nYWVhLXNoYXJlLnBuZyI+DQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9qcXVlcnkvMS4xMi40L2pxdWVyeS5taW4uanMiPjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9qcXVlcnkubWF0Y2hIZWlnaHQvMC43LjAvanF1ZXJ5Lm1hdGNoSGVpZ2h0LW1pbi5qcyI+PC9zY3JpcHQ+CjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9Ii8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2pxdWVyeS5hcHBlYXIvMC40LjEvanF1ZXJ5LmFwcGVhci5taW4uanMiPjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2Jvb3RzdHJhcC0yNGNvbC9qcy9ib290c3RyYXAubWluLmpzIj48L3NjcmlwdD4KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0iLy93d3cucGFuZ2FlYS5kZS9hc3NldHMvdi44NDc1YTEyZDA1NDExMzE3ZjNkOTkzNTliMDE3YTI2My9qcy9kYXRhY29tYm8tbWluLmpzIj48L3NjcmlwdD4KPHRpdGxlPkpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTQpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgbGFrZSBuZWFyIEthbmdlcmx1c3N1YXEsIHdlc3QgR3JlZW5sYW5kPC90aXRsZT4KPG1ldGEgbmFtZT0idGl0bGUiIGNvbnRlbnQ9Ikh5ZHJvbG9naWNhbCBhbmQgbWV0ZW9yb2xvZ2ljYWwgaW52ZXN0aWdhdGlvbnMgaW4gYSBsYWtlIG5lYXIgS2FuZ2VybHVzc3VhcSwgd2VzdCBHcmVlbmxhbmQiIC8+CjxtZXRhIG5hbWU9ImF1dGhvciIgY29udGVudD0iSm9oYW5zc29uLCBFbW1hOyBCZXJnbHVuZCwgU3RlbjsgTGluZGJvcmcsIFRvYmlhczsgUGV0cm9uZSwgSm9oYW5uZXM7IHZhbiBBcywgRGlyazsgR3VzdGFmc3NvbiwgTGFycy1Hw7ZyYW47IE7DpHNsdW5kLCBKZW5zLU92ZTsgTGF1ZG9uLCBIamFsbWFyIiAvPgo8bWV0YSBuYW1lPSJkYXRlIiBjb250ZW50PSIyMDE0LTA5LTI1IiAvPgo8bWV0YSBuYW1lPSJkZXNjcmlwdGlvbiIgY29udGVudD0iSm9oYW5zc29uLCBFbW1hOyBCZXJnbHVuZCwgU3RlbjsgTGluZGJvcmcsIFRvYmlhczsgUGV0cm9uZSwgSm9oYW5uZXM7IHZhbiBBcywgRGlyazsgR3VzdGFmc3NvbiwgTGFycy1Hw7ZyYW47IE7DpHNsdW5kLCBKZW5zLU92ZTsgTGF1ZG9uLCBIamFsbWFyICgyMDE0KTogSHlkcm9sb2dpY2FsIGFuZCBtZXRlb3JvbG9naWNhbCBpbnZlc3RpZ2F0aW9ucyBpbiBhIGxha2UgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZC4gUEFOR0FFQSwgaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgsIFN1cHBsZW1lbnQgdG86IEpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTUpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldC4gRWFydGggU3lzdGVtIFNjaWVuY2UgRGF0YSwgNygxKSwgOTMtMTA4LCBodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIgLz4KPG1ldGEgbmFtZT0iZ2VvLnBvc2l0aW9uIiBjb250ZW50PSI2Ny4xMjU5NDA7LTUwLjE4MDM3MCIgLz4KPG1ldGEgbmFtZT0iSUNCTSIgY29udGVudD0iNjcuMTI1OTQwLCAtNTAuMTgwMzcwIiAvPgo8IS0tQkVHSU46IER1YmxpbiBDb3JlIGRlc2NyaXB0aW9uLS0+CjxsaW5rIHJlbD0ic2NoZW1hLkRDIiBocmVmPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgLz4KPGxpbmsgcmVsPSJzY2hlbWEuRENURVJNUyIgaHJlZj0iaHR0cDovL3B1cmwub3JnL2RjL3Rlcm1zLyIgLz4KPG1ldGEgbmFtZT0iREMudGl0bGUiIGNvbnRlbnQ9Ikh5ZHJvbG9naWNhbCBhbmQgbWV0ZW9yb2xvZ2ljYWwgaW52ZXN0aWdhdGlvbnMgaW4gYSBsYWtlIG5lYXIgS2FuZ2VybHVzc3VhcSwgd2VzdCBHcmVlbmxhbmQiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IkpvaGFuc3NvbiwgRW1tYSIgLz4KPG1ldGEgbmFtZT0iREMuY3JlYXRvciIgY29udGVudD0iQmVyZ2x1bmQsIFN0ZW4iIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IkxpbmRib3JnLCBUb2JpYXMiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IlBldHJvbmUsIEpvaGFubmVzIiAvPgo8bWV0YSBuYW1lPSJEQy5jcmVhdG9yIiBjb250ZW50PSJ2YW4gQXMsIERpcmsiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9Ikd1c3RhZnNzb24sIExhcnMtR8O2cmFuIiAvPgo8bWV0YSBuYW1lPSJEQy5jcmVhdG9yIiBjb250ZW50PSJOw6RzbHVuZCwgSmVucy1PdmUiIC8+CjxtZXRhIG5hbWU9IkRDLmNyZWF0b3IiIGNvbnRlbnQ9IkxhdWRvbiwgSGphbG1hciIgLz4KPG1ldGEgbmFtZT0iREMucHVibGlzaGVyIiBjb250ZW50PSJQQU5HQUVBIiAvPgo8bWV0YSBuYW1lPSJEQy5zb3VyY2UiIGNvbnRlbnQ9IlN1cHBsZW1lbnQgdG86IEpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTUpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldC4gRWFydGggU3lzdGVtIFNjaWVuY2UgRGF0YSwgNygxKSwgOTMtMTA4LCBodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIgLz4KPG1ldGEgbmFtZT0iREMuZGF0ZSIgY29udGVudD0iMjAxNC0wOS0yNSIgc2NoZW1lPSJEQ1RFUk1TLlczQ0RURiIgLz4KPG1ldGEgbmFtZT0iREMudHlwZSIgY29udGVudD0iRGF0YXNldCIgLz4KPG1ldGEgbmFtZT0iREMubGFuZ3VhZ2UiIGNvbnRlbnQ9ImVuIiBzY2hlbWU9IkRDVEVSTVMuUkZDMzA2NiIgLz4KPG1ldGEgbmFtZT0iRENURVJNUy5saWNlbnNlIiBzY2hlbWU9IkRDVEVSTVMuVVJJIiBjb250ZW50PSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLyIgLz4KPG1ldGEgbmFtZT0iREMuaWRlbnRpZmllciIgY29udGVudD0iaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgiIHNjaGVtZT0iRENURVJNUy5VUkkiIC8+CjxtZXRhIG5hbWU9IkRDLmZvcm1hdCIgY29udGVudD0iYXBwbGljYXRpb24vemlwLCA1NjYzLjAga0J5dGVzIiAvPgo8bWV0YSBuYW1lPSJEQy5yZWxhdGlvbiIgY29udGVudD0iTWFwIG9mIFR3byBCb2F0IExha2UgaW4gR3JlZW5sYW5kIChqcGcgMTMgTUIpIHdpdGggcG9zaXRpb24gb2Ygc2FtcGxpbmcgc2l0ZXMgKFVSSTogaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC90d29ib2F0bGFrZV9ncmVlbmxhbmQuanBnKSIgLz4KPG1ldGEgbmFtZT0iREMucmVsYXRpb24iIGNvbnRlbnQ9IlRpbWUgbGFwcyBwaG90b3Mgb2YgbGFrZSAyMDEyLTA5LTA1IHRvIDIwMTMtMDgtMTQgKG1vdiBmaWxlLCB6aXBwZWQgMjA1IE1CKSAoVVJJOiBodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L1RpbWVsYXBzZV9UQkwuemlwKSIgLz4KPCEtLUVORDogRHVibGluIENvcmUgZGVzY3JpcHRpb24tLT4KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0iLy9tYXBzLmdvb2dsZWFwaXMuY29tL21hcHMvYXBpL2pzP3Y9MyZhbXA7bGFuZ3VhZ2U9ZW4mYW1wO2tleT1BSXphU3lEU2lWalBTNVl2YW5ac0VINFJ2SzBnRXI0NlVvLTFyQ1EiPjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+Lyo8IVtDREFUQVsqL2pRdWVyeShmdW5jdGlvbigkKSB7IHJldHVybiBpbml0aWFsaXplU21hbGxEYXRhc2V0R01hcCg4MzYxNzgsJ2hhc2g9YzY2NjkzY2JiZjZjNDkyYjEwYmU4M2Q0NDliOWY0NzUnLG5ldyBnb29nbGUubWFwcy5MYXRMbmdCb3VuZHMobmV3IGdvb2dsZS5tYXBzLkxhdExuZyg2Ny4xMjU5NCwtNTAuMTgwMzcpLG5ldyBnb29nbGUubWFwcy5MYXRMbmcoNjcuMTI1OTQsLTUwLjE4MDM3KSksdW5kZWZpbmVkKTsgfSk7LypdXT4qLzwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSIvL2QxYnhoOHVhczFtbnc3LmNsb3VkZnJvbnQubmV0L2Fzc2V0cy9lbWJlZC5qcyI+PC9zY3JpcHQ+CjxsaW5rIHJlbD0iY2l0ZS1hcyIgaHJlZj0iaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgiPgo8bGluayByZWw9ImRlc2NyaWJlZGJ5IiBocmVmPSJodHRwczovL2RvaS5wYW5nYWVhLmRlLzEwLjE1OTQvUEFOR0FFQS44MzYxNzg/Zm9ybWF0PW1ldGFkYXRhX2pzb25sZCIgdHlwZT0iYXBwbGljYXRpb24vbGQranNvbiI+CjxsaW5rIHJlbD0iZGVzY3JpYmVkYnkiIGhyZWY9Imh0dHBzOi8vZG9pLnBhbmdhZWEuZGUvMTAuMTU5NC9QQU5HQUVBLjgzNjE3OD9mb3JtYXQ9Y2l0YXRpb25fcmlzIiB0eXBlPSJhcHBsaWNhdGlvbi94LXJlc2VhcmNoLWluZm8tc3lzdGVtcyI+CjxsaW5rIHJlbD0iZGVzY3JpYmVkYnkiIGhyZWY9Imh0dHBzOi8vZG9pLnBhbmdhZWEuZGUvMTAuMTU5NC9QQU5HQUVBLjgzNjE3OD9mb3JtYXQ9Y2l0YXRpb25fYmlidGV4IiB0eXBlPSJhcHBsaWNhdGlvbi94LWJpYnRleCI+CjxsaW5rIHJlbD0iaXRlbSIgaHJlZj0iaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC9qb2hhbnNzb25fZXRhbC0yMDE0LnppcCIgdHlwZT0iYXBwbGljYXRpb24vemlwIj4KPGxpbmsgcmVsPSJhdXRob3IiIGhyZWY9Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMi02NTUzLTg5ODIiPgo8bGluayByZWw9ImF1dGhvciIgaHJlZj0iaHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAxLTYwNTgtMTQ2NiI+CjxzY3JpcHQgdHlwZT0iYXBwbGljYXRpb24vbGQranNvbiI+eyJAY29udGV4dCI6Imh0dHA6Ly9zY2hlbWEub3JnLyIsIkBpZCI6Imh0dHBzOi8vZG9pLm9yZy8xMC4xNTk0L1BBTkdBRUEuODM2MTc4IiwiQHR5cGUiOiJEYXRhc2V0IiwiaWRlbnRpZmllciI6Imh0dHBzOi8vZG9pLm9yZy8xMC4xNTk0L1BBTkdBRUEuODM2MTc4IiwidXJsIjoiaHR0cHM6Ly9kb2kucGFuZ2FlYS5kZS8xMC4xNTk0L1BBTkdBRUEuODM2MTc4IiwiY3JlYXRvciI6W3siQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoiSm9oYW5zc29uIiwiZ2l2ZW5OYW1lIjoiRW1tYSIsImVtYWlsIjoiZW1tYS5qb2hhbnNzb25Ac2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJCZXJnbHVuZCIsImdpdmVuTmFtZSI6IlN0ZW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6IkxpbmRib3JnIiwiZ2l2ZW5OYW1lIjoiVG9iaWFzIiwiZW1haWwiOiJ0b2JpYXMubGluZGJvcmdAc2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJQZXRyb25lIiwiZ2l2ZW5OYW1lIjoiSm9oYW5uZXMiLCJlbWFpbCI6ImpvaGFubmVzLnBldHJvbmVAc2tiLnNlIn0seyJAaWQiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIiwiQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoidmFuIEFzIiwiZ2l2ZW5OYW1lIjoiRGlyayIsImlkZW50aWZpZXIiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJHdXN0YWZzc29uIiwiZ2l2ZW5OYW1lIjoiTGFycy1Hw7ZyYW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6Ik7DpHNsdW5kIiwiZ2l2ZW5OYW1lIjoiSmVucy1PdmUifSx7IkBpZCI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYiLCJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJMYXVkb24iLCJnaXZlbk5hbWUiOiJIamFsbWFyIiwiaWRlbnRpZmllciI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYifV0sIm5hbWUiOiJIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgbGFrZSBuZWFyIEthbmdlcmx1c3N1YXEsIHdlc3QgR3JlZW5sYW5kIiwicHVibGlzaGVyIjp7IkB0eXBlIjoiT3JnYW5pemF0aW9uIiwibmFtZSI6IlBBTkdBRUEiLCJkaXNhbWJpZ3VhdGluZ0Rlc2NyaXB0aW9uIjoiRGF0YSBQdWJsaXNoZXIgZm9yIEVhcnRoICYgRW52aXJvbm1lbnRhbCBTY2llbmNlIiwidXJsIjoiaHR0cHM6Ly93d3cucGFuZ2FlYS5kZS8ifSwiaW5jbHVkZWRJbkRhdGFDYXRhbG9nIjp7IkB0eXBlIjoiRGF0YUNhdGFsb2ciLCJuYW1lIjoiUEFOR0FFQSIsImRpc2FtYmlndWF0aW5nRGVzY3JpcHRpb24iOiJEYXRhIFB1Ymxpc2hlciBmb3IgRWFydGggJiBFbnZpcm9ubWVudGFsIFNjaWVuY2UiLCJ1cmwiOiJodHRwczovL3d3dy5wYW5nYWVhLmRlLyJ9LCJkYXRlUHVibGlzaGVkIjoiMjAxNC0wOS0yNSIsImNpdGF0aW9uIjpbeyJAaWQiOiJodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIsIkB0eXBlIjoiUHVibGljYXRpb25Jc3N1ZSIsImlkZW50aWZpZXIiOiJodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIsInVybCI6Imh0dHBzOi8vZG9pLm9yZy8xMC41MTk0L2Vzc2QtNy05My0yMDE1IiwiY3JlYXRvciI6W3siQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoiSm9oYW5zc29uIiwiZ2l2ZW5OYW1lIjoiRW1tYSIsImVtYWlsIjoiZW1tYS5qb2hhbnNzb25Ac2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJCZXJnbHVuZCIsImdpdmVuTmFtZSI6IlN0ZW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6IkxpbmRib3JnIiwiZ2l2ZW5OYW1lIjoiVG9iaWFzIiwiZW1haWwiOiJ0b2JpYXMubGluZGJvcmdAc2tiLnNlIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJQZXRyb25lIiwiZ2l2ZW5OYW1lIjoiSm9oYW5uZXMiLCJlbWFpbCI6ImpvaGFubmVzLnBldHJvbmVAc2tiLnNlIn0seyJAaWQiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIiwiQHR5cGUiOiJQZXJzb24iLCJmYW1pbHlOYW1lIjoidmFuIEFzIiwiZ2l2ZW5OYW1lIjoiRGlyayIsImlkZW50aWZpZXIiOiJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyIn0seyJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJHdXN0YWZzc29uIiwiZ2l2ZW5OYW1lIjoiTGFycy1Hw7ZyYW4ifSx7IkB0eXBlIjoiUGVyc29uIiwiZmFtaWx5TmFtZSI6Ik7DpHNsdW5kIiwiZ2l2ZW5OYW1lIjoiSmVucy1PdmUifSx7IkBpZCI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYiLCJAdHlwZSI6IlBlcnNvbiIsImZhbWlseU5hbWUiOiJMYXVkb24iLCJnaXZlbk5hbWUiOiJIamFsbWFyIiwiaWRlbnRpZmllciI6Imh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYifV0sIm5hbWUiOiJIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldCIsImRhdGVQdWJsaXNoZWQiOiIyMDE1IiwiaXNzdWVOdW1iZXIiOiI3KDEpIiwicGFnaW5hdGlvbiI6IjkzLTEwOCIsImlzUGFydE9mIjp7IkB0eXBlIjoiQ3JlYXRpdmVXb3JrU2VyaWVzIiwibmFtZSI6IkVhcnRoIFN5c3RlbSBTY2llbmNlIERhdGEifX0seyJAaWQiOiJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L3R3b2JvYXRsYWtlX2dyZWVubGFuZC5qcGciLCJAdHlwZSI6IldlYlBhZ2UiLCJpZGVudGlmaWVyIjoiaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC90d29ib2F0bGFrZV9ncmVlbmxhbmQuanBnIiwidXJsIjoiaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC90d29ib2F0bGFrZV9ncmVlbmxhbmQuanBnIiwibmFtZSI6Ik1hcCBvZiBUd28gQm9hdCBMYWtlIGluIEdyZWVubGFuZCAoanBnIDEzIE1CKSB3aXRoIHBvc2l0aW9uIG9mIHNhbXBsaW5nIHNpdGVzIn0seyJAaWQiOiJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L1RpbWVsYXBzZV9UQkwuemlwIiwiQHR5cGUiOiJXZWJQYWdlIiwiaWRlbnRpZmllciI6Imh0dHA6Ly9zdG9yZS5wYW5nYWVhLmRlL1B1YmxpY2F0aW9ucy9Kb2hhbnNzb25FX2V0X2FsXzIwMTQvVGltZWxhcHNlX1RCTC56aXAiLCJ1cmwiOiJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L1RpbWVsYXBzZV9UQkwuemlwIiwibmFtZSI6IlRpbWUgbGFwcyBwaG90b3Mgb2YgbGFrZSAyMDEyLTA5LTA1IHRvIDIwMTMtMDgtMTQgKG1vdiBmaWxlLCB6aXBwZWQgMjA1IE1CKSJ9XSwiZGVzY3JpcHRpb24iOiJGZXcgaHlkcm9sb2dpY2FsIHN0dWRpZXMgaGF2ZSBiZWVuIG1hZGUgaW4gR3JlZW5sYW5kLCBvdGhlciB0aGFuIG9uIGdsYWNpYWwgaHlkcm9sb2d5IGFzc29jaWF0ZWQgd2l0aCB0aGUgaWNlIHNoZWV0LiBVbmRlcnN0YW5kaW5nIHBlcm1hZnJvc3QgaHlkcm9sb2d5IGFuZCBoeWRyb2NsaW1hdGljIGNoYW5nZSBhbmQgdmFyaWFiaWxpdHksIGhvd2V2ZXIsIHByb3ZpZGVzIGtleSBpbmZvcm1hdGlvbiBmb3IgdW5kZXJzdGFuZGluZyBjbGltYXRlIGNoYW5nZSBlZmZlY3RzIGFuZCBmZWVkYmFja3MgaW4gdGhlIEFyY3RpYyBsYW5kc2NhcGUuIFRoaXMgcGFwZXIgcHJlc2VudHMgYSBuZXcgZXh0ZW5zaXZlIGFuZCBkZXRhaWxlZCBoeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIG9wZW4gYWNjZXNzIGRhdGFzZXQsIHdpdGggaGlnaCB0ZW1wb3JhbCByZXNvbHV0aW9uIGZyb20gYSAxLjU2IGttKioyIHBlcm1hZnJvc3QgY2F0Y2htZW50IHdpdGggYSBsYWtlIHVuZGVybGFpbiBieSBhIHRocm91Z2ggdGFsaWsgY2xvc2UgdG8gdGhlIGljZSBzaGVldCBpbiB0aGUgS2FuZ2VybHVzc3VhcSByZWdpb24sIHdlc3Rlcm4gR3JlZW5sYW5kLiBUaGUgcGFwZXIgZGVzY3JpYmVzIHRoZSBoeWRyb2xvZ2ljYWwgc2l0ZSBpbnZlc3RpZ2F0aW9ucyBhbmQgdXRpbGl6ZWQgZXF1aXBtZW50LCBhcyB3ZWxsIGFzIHRoZSBkYXRhIGNvbGxlY3Rpb24gYW5kIHByb2Nlc3NpbmcuIFRoZSBpbnZlc3RpZ2F0aW9ucyB3ZXJlIHBlcmZvcm1lZCBiZXR3ZWVuIDIwMTAgYW5kIDIwMTMuIFRoZSBoaWdoIHNwYXRpYWwgcmVzb2x1dGlvbiwgd2l0aGluIHRoZSBpbnZlc3RpZ2F0ZWQgYXJlYSwgb2YgdGhlIGRhdGFzZXQgbWFrZXMgaXQgaGlnaGx5IHN1aXRhYmxlIGZvciB2YXJpb3VzIGRldGFpbGVkIGh5ZHJvbG9naWNhbCBhbmQgZWNvbG9naWNhbCBzdHVkaWVzIG9uIGNhdGNobWVudCBzY2FsZS4iLCJzcGF0aWFsQ292ZXJhZ2UiOnsiQHR5cGUiOiJQbGFjZSIsImdlbyI6eyJAdHlwZSI6Ikdlb0Nvb3JkaW5hdGVzIiwibGF0aXR1ZGUiOjY3LjEyNTk0LCJsb25naXR1ZGUiOi01MC4xODAzN319LCJpbkxhbmd1YWdlIjoiZW4iLCJsaWNlbnNlIjoiaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzMuMC8iLCJkaXN0cmlidXRpb24iOnsiQHR5cGUiOiJEYXRhRG93bmxvYWQiLCJmaWxlRm9ybWF0IjoiYXBwbGljYXRpb24vemlwIiwiY29udGVudFVybCI6Imh0dHA6Ly9zdG9yZS5wYW5nYWVhLmRlL1B1YmxpY2F0aW9ucy9Kb2hhbnNzb25FX2V0X2FsXzIwMTQvam9oYW5zc29uX2V0YWwtMjAxNC56aXAifX08L3NjcmlwdD4KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPi8qPCFbQ0RBVEFbKi8NCihmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCihpW3JdLnE9aVtyXS5xfHxbXSkucHVzaChhcmd1bWVudHMpfSxpW3JdLmw9MSpuZXcgRGF0ZSgpO2E9cy5jcmVhdGVFbGVtZW50KG8pLA0KbT1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKG8pWzBdO2EuYXN5bmM9MTthLnNyYz1nO20ucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYSxtKQ0KfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCcvL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KZ2EoJ2NyZWF0ZScsICdVQS0zMDYyNDE1MC0xJywgJ3BhbmdhZWEuZGUnKTsNCmdhKCdzZXQnLCAnYW5vbnltaXplSXAnLCB0cnVlKTsNCmdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQovKl1dPiovPC9zY3JpcHQ+DQo8L2hlYWQ+DQo8Ym9keSBjbGFzcz0iaG9tZXBhZ2UtbGF5b3V0Ij4NCjxkaXYgaWQ9ImhlYWRlci13cmFwcGVyIj4NCiAgPGRpdiBjbGFzcz0iY29udGFpbmVyLWZsdWlkIj4NCiAgICA8aGVhZGVyIGNsYXNzPSJyb3ciPjwhLS0gdm9sbGUgU2NyZWVuLUJyZWl0ZSAtLT4NCiAgICAgIDxkaXYgY2xhc3M9ImNvbnRlbnQtd3JhcHBlciI+PCEtLSBtYXguIEJyZWl0ZSAtLT4NCiAgICAgICAgPGRpdiBpZD0ibG9naW4tYXJlYS13cmFwcGVyIiBjbGFzcz0iaGlkZGVuLXByaW50Ij48ZGl2IGlkPSJsb2dpbi1hcmVhIj48c3BhbiBpZD0idXNlci1uYW1lIj5Ob3QgbG9nZ2VkIGluPC9zcGFuPjxhIGlkPSJzaWdudXAtYnV0dG9uIiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1wbHVzLXNpZ24gc2VsZi1yZWZlcmVyLWxpbmsiIHRpdGxlPSJTaWduIFVwIC8gQ3JlYXRlIEFjY291bnQiIGFyaWEtbGFiZWw9IlNpZ24gdXAiIHRhcmdldD0iX3NlbGYiIHJlbD0ibm9mb2xsb3ciIGhyZWY9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvdXNlci9zaWdudXAucGhwP3JlZmVyZXI9aHR0cHMlM0ElMkYlMkZ3d3cucGFuZ2FlYS5kZSUyRiIgZGF0YS10ZW1wbGF0ZT0iaHR0cHM6Ly93d3cucGFuZ2FlYS5kZS91c2VyL3NpZ251cC5waHA/cmVmZXJlcj0jdSMiPjwvYT48YSBpZD0ibG9naW4tYnV0dG9uIiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1sb2ctaW4gc2VsZi1yZWZlcmVyLWxpbmsiIHRpdGxlPSJMb2cgSW4iIGFyaWEtbGFiZWw9IkxvZyBpbiIgdGFyZ2V0PSJfc2VsZiIgcmVsPSJub2ZvbGxvdyIgaHJlZj0iaHR0cHM6Ly93d3cucGFuZ2FlYS5kZS91c2VyL2xvZ2luLnBocD9yZWZlcmVyPWh0dHBzJTNBJTJGJTJGd3d3LnBhbmdhZWEuZGUlMkYiIGRhdGEtdGVtcGxhdGU9Imh0dHBzOi8vd3d3LnBhbmdhZWEuZGUvdXNlci9sb2dpbi5waHA/cmVmZXJlcj0jdSMiPjwvYT48L2Rpdj48L2Rpdj4NCiAgICAgICAgPGRpdiBjbGFzcz0iYmxpbmRzcGFsdGUgaGVhZGVyLWJsb2NrIGNvbC1sZy0zIGNvbC1tZC00Ij48L2Rpdj4NCiAgICAgICAgDQogICAgICAgIDxkaXYgaWQ9ImhlYWRlci1sb2dvLWJsb2NrIiBjbGFzcz0iaGVhZGVyLWJsb2NrIGNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS00IGNvbC14cy04Ij4NCiAgICAgICAgICA8ZGl2IGlkPSJwYW5nYWVhLWxvZ28iPg0KICAgICAgICAgICAgPGEgdGl0bGU9IlBBTkdBRUEgaG9tZSIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8iIGNsYXNzPSJob21lLWxpbmsiPjxpbWcgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2xheW91dC1pbWFnZXMvcGFuZ2FlYS1sb2dvLnBuZyIgYWx0PSJQQU5HQUVBIGhvbWUiPjwvYT4NCiAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgPC9kaXY+DQogICAgICAgIA0KICAgICAgICA8ZGl2IGlkPSJoZWFkZXItbWlkLWJsb2NrIiBjbGFzcz0iaGVhZGVyLWJsb2NrIGNvbC1sZy0xMiBjb2wtbWQtOSBjb2wtc20tMjAgY29sLXhzLTE2Ij4NCiAgICAgICAgICA8ZGl2IGlkPSJwYW5nYWVhLWxvZ28taGVhZGxpbmUiPg0KICAgICAgICAgICAgUEFOR0FFQTxzcGFuIGNsYXNzPSJwdW5rdCI+Ljwvc3Bhbj4NCiAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICA8ZGl2IGlkPSJwYW5nYWVhLWxvZ28tc2xvZ2FuIj4NCiAgICAgICAgICAgIDxzcGFuPkRhdGEgUHVibGlzaGVyIGZvciBFYXJ0aCAmYW1wOyA8L3NwYW4+PHNwYW4gY2xhc3M9Im5vd3JhcCI+RW52aXJvbm1lbnRhbCBTY2llbmNlPC9zcGFuPg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDxkaXYgaWQ9InNlYXJjaC1hcmVhLWhlYWRlciIgY2xhc3M9InJvdyI+PC9kaXY+DQogICAgICAgIDwvZGl2Pg0KICAgICAgICANCiAgICAgICAgPGRpdiBpZD0iaGVhZGVyLW1haW4tbWVudS1ibG9jayIgY2xhc3M9ImhlYWRlci1ibG9jayBoaWRkZW4tcHJpbnQgY29sLWxnLTYgY29sLW1kLTcgY29sLXNtLTI0IGNvbC14cy0yNCI+DQogICAgICAgICAgPG5hdiBpZD0ibWFpbi1uYXYiPg0KICAgICAgICAgICAgPHVsPg0KICAgICAgICAgICAgICA8bGkgaWQ9Im1lbnUtc2VhcmNoIj4NCiAgICAgICAgICAgICAgICA8IS0tIGNsYXNzIG9uIGxpbmsgaXMgaW1wb3J0YW50LCBkb24ndCBjaGFuZ2UhISEgLS0+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8iIGNsYXNzPSJob21lLWxpbmsiPlNlYXJjaDwvYT4NCiAgICAgICAgICAgICAgPC9saT4NCiAgICAgICAgICAgICAgPGxpIGlkPSJtZW51LXN1Ym1pdCI+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9zdWJtaXQvIj5TdWJtaXQ8L2E+DQogICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICAgIDxsaSBpZD0ibWVudS1hYm91dCI+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9hYm91dC8iPkFib3V0PC9hPg0KICAgICAgICAgICAgICA8L2xpPg0KICAgICAgICAgICAgICA8bGkgaWQ9Im1lbnUtY29udGFjdCI+DQogICAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9jb250YWN0LyI+Q29udGFjdDwvYT4NCiAgICAgICAgICAgICAgPC9saT4NCiAgICAgICAgICAgIDwvdWw+DQogICAgICAgICAgPC9uYXY+DQogICAgICAgICAgPGRpdiBjbGFzcz0iY2xlYXJmaXgiPjwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvaGVhZGVyPg0KICA8L2Rpdj4NCjwvZGl2Pg0KPGRpdiBpZD0iZmxleC13cmFwcGVyIj4NCjxkaXYgaWQ9Im1haW4tY29udGFpbmVyIiBjbGFzcz0iY29udGFpbmVyLWZsdWlkIj4NCjxkaXYgaWQ9Im1haW4tcm93IiBjbGFzcz0icm93IG1haW4tcm93Ij4NCjxkaXYgaWQ9Im1haW4iIGNsYXNzPSJjb2wtbGctMjQgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KPGRpdiBpZD0iZGF0YXNldCI+CjxkaXYgY2xhc3M9InJvdyI+PGRpdiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTI0IGNvbC14cy0yNCBoaWRkZW4teHMgaGlkZGVuLXNtIj48ZGl2IGNsYXNzPSJ0aXRsZSBjaXRhdGlvbiBpbnZpc2libGUtdG9wLWJvcmRlciI+Q2l0YXRpb246PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIHRvcC1ib3JkZXIiPjxkaXYgaWQ9ImdtYXAtZGF0YXNldC13cmFwcGVyIiBjbGFzcz0iZ21hcC13cmFwcGVyIGhpZGRlbi1wcmludCBoaWRkZW4teHMgaGlkZGVuLXNtIGNvbC1sZy04IGNvbC1tZC04IGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImVtYmVkLXJlc3BvbnNpdmUgZW1iZWQtcmVzcG9uc2l2ZS00YnkzIj48ZGl2IGlkPSJnbWFwLWRhdGFzZXQiIGNsYXNzPSJlbWJlZC1yZXNwb25zaXZlLWl0ZW0iPjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjxoMSBjbGFzcz0iaGFuZ2luZyBjaXRhdGlvbiI+PHN0cm9uZz48YSBjbGFzcz0icG9wb3Zlci1saW5rIGxpbmstdW5zdHlsZWQiIGhyZWY9IiMiIGRhdGEtdGl0bGU9IiZsdDtzcGFuJmd0O0pvaGFuc3NvbiwgRW1tYSZsdDthIGNsYXNzPSZxdW90O3NlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2gmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyByZWw9JnF1b3Q7bm9mb2xsb3cmcXVvdDsgdGl0bGU9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0pvaGFuc3NvbiwgRW1tYScuLi4mcXVvdDsgYXJpYS1sYWJlbD0mcXVvdDtTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnSm9oYW5zc29uLCBFbW1hJyZxdW90OyBocmVmPSZxdW90Oy8vd3d3LnBhbmdhZWEuZGUvP3E9YXV0aG9yJTNBZW1haWwlM0FlbW1hLmpvaGFuc3NvbiU0MHNrYi5zZSZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7bWFpbC1saW5rIHRleHQtbm93cmFwIHdpZGUtaWNvbi1saW5rJnF1b3Q7IGhyZWY9JnF1b3Q7bWFpbHRvOmVtbWEuam9oYW5zc29uQHNrYi5zZSZxdW90OyZndDtlbW1hLmpvaGFuc3NvbkBza2Iuc2UmbHQ7L2EmZ3Q7Jmx0Oy9kaXYmZ3Q7JiMxMDsmbHQ7L2RpdiZndDsmIzEwOyI+Sm9oYW5zc29uLCBFbW1hPC9hPjsgQmVyZ2x1bmQsIFN0ZW47IDxhIGNsYXNzPSJwb3BvdmVyLWxpbmsgbGluay11bnN0eWxlZCIgaHJlZj0iIyIgZGF0YS10aXRsZT0iJmx0O3NwYW4mZ3Q7TGluZGJvcmcsIFRvYmlhcyZsdDthIGNsYXNzPSZxdW90O3NlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2gmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyByZWw9JnF1b3Q7bm9mb2xsb3cmcXVvdDsgdGl0bGU9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0xpbmRib3JnLCBUb2JpYXMnLi4uJnF1b3Q7IGFyaWEtbGFiZWw9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0xpbmRib3JnLCBUb2JpYXMnJnF1b3Q7IGhyZWY9JnF1b3Q7Ly93d3cucGFuZ2FlYS5kZS8/cT1hdXRob3IlM0FlbWFpbCUzQXRvYmlhcy5saW5kYm9yZyU0MHNrYi5zZSZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7bWFpbC1saW5rIHRleHQtbm93cmFwIHdpZGUtaWNvbi1saW5rJnF1b3Q7IGhyZWY9JnF1b3Q7bWFpbHRvOnRvYmlhcy5saW5kYm9yZ0Bza2Iuc2UmcXVvdDsmZ3Q7dG9iaWFzLmxpbmRib3JnQHNrYi5zZSZsdDsvYSZndDsmbHQ7L2RpdiZndDsmIzEwOyZsdDsvZGl2Jmd0OyYjMTA7Ij5MaW5kYm9yZywgVG9iaWFzPC9hPjsgPGEgY2xhc3M9InBvcG92ZXItbGluayBsaW5rLXVuc3R5bGVkIiBocmVmPSIjIiBkYXRhLXRpdGxlPSImbHQ7c3BhbiZndDtQZXRyb25lLCBKb2hhbm5lcyZsdDthIGNsYXNzPSZxdW90O3NlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2gmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyByZWw9JnF1b3Q7bm9mb2xsb3cmcXVvdDsgdGl0bGU9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ1BldHJvbmUsIEpvaGFubmVzJy4uLiZxdW90OyBhcmlhLWxhYmVsPSZxdW90O1NlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdQZXRyb25lLCBKb2hhbm5lcycmcXVvdDsgaHJlZj0mcXVvdDsvL3d3dy5wYW5nYWVhLmRlLz9xPWF1dGhvciUzQWVtYWlsJTNBam9oYW5uZXMucGV0cm9uZSU0MHNrYi5zZSZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7bWFpbC1saW5rIHRleHQtbm93cmFwIHdpZGUtaWNvbi1saW5rJnF1b3Q7IGhyZWY9JnF1b3Q7bWFpbHRvOmpvaGFubmVzLnBldHJvbmVAc2tiLnNlJnF1b3Q7Jmd0O2pvaGFubmVzLnBldHJvbmVAc2tiLnNlJmx0Oy9hJmd0OyZsdDsvZGl2Jmd0OyYjMTA7Jmx0Oy9kaXYmZ3Q7JiMxMDsiPlBldHJvbmUsIEpvaGFubmVzPC9hPjsgPGEgY2xhc3M9InBvcG92ZXItbGluayBsaW5rLXVuc3R5bGVkIiBocmVmPSIjIiBkYXRhLXRpdGxlPSImbHQ7c3BhbiZndDt2YW4gQXMsIERpcmsmbHQ7YSBjbGFzcz0mcXVvdDtzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoJnF1b3Q7IHRhcmdldD0mcXVvdDtfYmxhbmsmcXVvdDsgcmVsPSZxdW90O25vZm9sbG93JnF1b3Q7IHRpdGxlPSZxdW90O1NlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICd2YW4gQXMsIERpcmsnLi4uJnF1b3Q7IGFyaWEtbGFiZWw9JnF1b3Q7U2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ3ZhbiBBcywgRGlyaycmcXVvdDsgaHJlZj0mcXVvdDsvL3d3dy5wYW5nYWVhLmRlLz9xPWF1dGhvciUzQW9yY2lkJTNBMDAwMC0wMDAyLTY1NTMtODk4MiZxdW90OyZndDsmbHQ7L2EmZ3Q7Jmx0Oy9zcGFuJmd0OyIgZGF0YS1jb250ZW50PSImbHQ7ZGl2Jmd0OyZsdDtkaXYmZ3Q7Jmx0O2EgY2xhc3M9JnF1b3Q7b3JjaWQtbGluayB0ZXh0LW5vd3JhcCB3aWRlLWljb24tbGluayZxdW90OyB0YXJnZXQ9JnF1b3Q7X2JsYW5rJnF1b3Q7IGhyZWY9JnF1b3Q7aHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAyLTY1NTMtODk4MiZxdW90OyZndDtodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItNjU1My04OTgyJmx0Oy9hJmd0OyZsdDsvZGl2Jmd0OyYjMTA7Jmx0Oy9kaXYmZ3Q7JiMxMDsiPnZhbiBBcywgRGlyazwvYT47IEd1c3RhZnNzb24sIExhcnMtR8O2cmFuOyBOw6RzbHVuZCwgSmVucy1PdmU7IDxhIGNsYXNzPSJwb3BvdmVyLWxpbmsgbGluay11bnN0eWxlZCIgaHJlZj0iIyIgZGF0YS10aXRsZT0iJmx0O3NwYW4mZ3Q7TGF1ZG9uLCBIamFsbWFyJmx0O2EgY2xhc3M9JnF1b3Q7c2VhcmNobGluayBnbHlwaGljb24gZ2x5cGhpY29uLXNlYXJjaCZxdW90OyB0YXJnZXQ9JnF1b3Q7X2JsYW5rJnF1b3Q7IHJlbD0mcXVvdDtub2ZvbGxvdyZxdW90OyB0aXRsZT0mcXVvdDtTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnTGF1ZG9uLCBIamFsbWFyJy4uLiZxdW90OyBhcmlhLWxhYmVsPSZxdW90O1NlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdMYXVkb24sIEhqYWxtYXInJnF1b3Q7IGhyZWY9JnF1b3Q7Ly93d3cucGFuZ2FlYS5kZS8/cT1hdXRob3IlM0FvcmNpZCUzQTAwMDAtMDAwMS02MDU4LTE0NjYmcXVvdDsmZ3Q7Jmx0Oy9hJmd0OyZsdDsvc3BhbiZndDsiIGRhdGEtY29udGVudD0iJmx0O2RpdiZndDsmbHQ7ZGl2Jmd0OyZsdDthIGNsYXNzPSZxdW90O29yY2lkLWxpbmsgdGV4dC1ub3dyYXAgd2lkZS1pY29uLWxpbmsmcXVvdDsgdGFyZ2V0PSZxdW90O19ibGFuayZxdW90OyBocmVmPSZxdW90O2h0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMS02MDU4LTE0NjYmcXVvdDsmZ3Q7aHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAxLTYwNTgtMTQ2NiZsdDsvYSZndDsmbHQ7L2RpdiZndDsmIzEwOyZsdDsvZGl2Jmd0OyYjMTA7Ij5MYXVkb24sIEhqYWxtYXI8L2E+ICgyMDE0KTo8L3N0cm9uZz4gSHlkcm9sb2dpY2FsIGFuZCBtZXRlb3JvbG9naWNhbCBpbnZlc3RpZ2F0aW9ucyBpbiBhIGxha2UgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZC4gPGVtPlBBTkdBRUE8L2VtPiwgPGEgcmVsPSJub2ZvbGxvdyBib29rbWFyayIgaHJlZj0iaHR0cHM6Ly9kb2kub3JnLzEwLjE1OTQvUEFOR0FFQS44MzYxNzgiIGRhdGEtdGl0bGU9IiZsdDtzcGFuJmd0O1BlcnNpc3RlbnQgRE9JIE5hbWUmbHQ7L3NwYW4mZ3Q7IiBkYXRhLWNvbnRlbnQ9IiZsdDtkaXYgY2xhc3M9JnF1b3Q7bGluay1kZXNjcmlwdGlvbiZxdW90OyZndDsmbHQ7cCZndDtBICZsdDthIGhyZWY9JnF1b3Q7aHR0cHM6Ly9kb2kub3JnLyZxdW90OyBjbGFzcz0mcXVvdDtkb2ktbGluayZxdW90OyB0YXJnZXQ9JnF1b3Q7X2JsYW5rJnF1b3Q7Jmd0O0RPSSBuYW1lJmx0Oy9hJmd0OyBzaGFsbCBiZSB1c2VkIHRvIGNpdGUgYW5kIGxpbmsgUEFOR0FFQSBkYXRhc2V0cy4mbHQ7L3AmZ3Q7JiMxMDsmbHQ7cCZndDtBICZsdDtiJmd0O0RPSSBuYW1lJmx0Oy9iJmd0OyBpcyBndWFyYW50ZWVkIHRvIG5ldmVyIGNoYW5nZSwgc28geW91IGNhbiB1c2UgaXQgdG8gbGluayBwZXJtYW5lbnRseSB0byBkYXRhc2V0cyBvciBkb2N1bWVudHMuICZsdDtiJmd0O0lmIHlvdSB3YW50IHRvIGNpdGUgdGhpcyBkYXRhc2V0LCB1c2UgdGhlIGZ1bGwgY2l0YXRpb24gYW5kIGFkZCB0aGlzIGxpbmsgYXMgYSBwZXJzaXN0ZW50IHJlZmVyZW5jZS4mbHQ7L2ImZ3Q7Jmx0Oy9wJmd0OyYjMTA7Jmx0O2RpdiZndDtZb3UgbWF5IHVzZSB5b3VyIGJyb3dzZXIncyAmbHQ7Y29kZSZndDtjb3B5IGxpbmsgbG9jYXRpb24mbHQ7L2NvZGUmZ3Q7IGZ1bmN0aW9uYWxpdHkgdG8gcmV0cmlldmUgdGhlIGxpbmshIFlvdSBjYW4gYWxzbyBkb3dubG9hZCB0aGUgY2l0YXRpb24gaW4gc2V2ZXJhbCBmb3JtYXRzIG9uIHRoaXMgcGFnZS4mbHQ7L2RpdiZndDsmIzEwOyZsdDsvZGl2Jmd0OyYjMTA7IiBjbGFzcz0idGV4dC1saW5rd3JhcCBwb3BvdmVyLWxpbmsgZG9pLWxpbmsiPmh0dHBzOi8vZG9pLm9yZy8xMC4xNTk0L1BBTkdBRUEuODM2MTc4PC9hPiw8aHIgY2xhc3M9InNwYWNlciIgYXJpYS1oaWRkZW49InRydWUiIC8+CjxlbT5TdXBwbGVtZW50IHRvOjwvZW0+IEpvaGFuc3NvbiwgRSBldCBhbC4gKDIwMTUpOiBIeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGludmVzdGlnYXRpb25zIGluIGEgcGVyaWdsYWNpYWwgbGFrZSBjYXRjaG1lbnQgbmVhciBLYW5nZXJsdXNzdWFxLCB3ZXN0IEdyZWVubGFuZCDigJMgcHJlc2VudGF0aW9uIG9mIGEgbmV3IG11bHRpLXBhcmFtZXRlciBkYXRhIHNldC4gPGVtPkVhcnRoIFN5c3RlbSBTY2llbmNlIERhdGE8L2VtPiwgPHN0cm9uZz43KDEpPC9zdHJvbmc+LCA5My0xMDgsIDxhIGNsYXNzPSJ0ZXh0LWxpbmt3cmFwIGRvaS1saW5rIiBocmVmPSJodHRwczovL2RvaS5vcmcvMTAuNTE5NC9lc3NkLTctOTMtMjAxNSIgdGFyZ2V0PSJfYmxhbmsiPmh0dHBzOi8vZG9pLm9yZy8xMC41MTk0L2Vzc2QtNy05My0yMDE1PC9hPjwvaDE+CjxwIGNsYXNzPSJob3d0b2NpdGUiPjxzbWFsbD48c3BhbiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1idWxsaG9ybiI+PC9zcGFuPiA8c3Ryb25nPkFsd2F5cyBxdW90ZSBhYm92ZSBjaXRhdGlvbiB3aGVuIHVzaW5nIGRhdGEhPC9zdHJvbmc+IFlvdSBjYW4gZG93bmxvYWQgdGhlIGNpdGF0aW9uIGluIHNldmVyYWwgZm9ybWF0cyBiZWxvdy48L3NtYWxsPjwvcD4KPHAgY2xhc3M9ImRhdGEtYnV0dG9ucyI+PGEgcmVsPSJub2ZvbGxvdyBkZXNjcmliZWRieSIgdGl0bGU9IkV4cG9ydCBjaXRhdGlvbiB0byBSZWZlcmVuY2UgTWFuYWdlciwgRW5kTm90ZSwgUHJvQ2l0ZSIgaHJlZj0iP2Zvcm1hdD1jaXRhdGlvbl9yaXMiIGNsYXNzPSJhY3Rpb25idXR0b25saW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj5SSVMgQ2l0YXRpb248L3NwYW4+PC9hPjxhIHJlbD0ibm9mb2xsb3cgZGVzY3JpYmVkYnkiIHRpdGxlPSJFeHBvcnQgY2l0YXRpb24gdG8gQmliVGVYIiBocmVmPSI/Zm9ybWF0PWNpdGF0aW9uX2JpYnRleCIgY2xhc3M9ImFjdGlvbmJ1dHRvbmxpbmsiPjxzcGFuIGNsYXNzPSJhY3Rpb25idXR0b24iPjxzcGFuIHN0eWxlPSJmb250LXZhcmlhbnQ6c21hbGwtY2FwczsiPkJpYlRlWDwvc3Bhbj4gQ2l0YXRpb248L3NwYW4+PC9hPjxhIHJlbD0ibm9mb2xsb3ciIHRpdGxlPSJFeHBvcnQgY2l0YXRpb24gYXMgcGxhaW4gdGV4dCIgaHJlZj0iP2Zvcm1hdD1jaXRhdGlvbl90ZXh0IiB0YXJnZXQ9Il9ibGFuayIgY2xhc3M9ImFjdGlvbmJ1dHRvbmxpbmsgc2hhcmUtbGluayI+PHNwYW4gY2xhc3M9ImFjdGlvbmJ1dHRvbiI+VGV4dCBDaXRhdGlvbjwvc3Bhbj48L2E+PHNwYW4gY2xhc3M9InNlcGFyYXRvciI+PC9zcGFuPjxhIHJlbD0ibm9mb2xsb3ciIGNsYXNzPSJzZWxmLXJlZmVyZXItbGluayBzaGFyZS1saW5rIGFjdGlvbmJ1dHRvbmxpbmsiIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvbm9qcy5waHAiIGRhdGEtdGVtcGxhdGU9Imh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9zaGFyZXIucGhwP3U9I3UjJmFtcDt0PSN0IyIgdGl0bGU9IlNoYXJlIGRhdGFzZXQgb24gRmFjZWJvb2siIHRhcmdldD0iX2JsYW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj48c3BhbiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1zaGFyZSI+PC9zcGFuPiBGYWNlYm9vazwvc3Bhbj48L2E+PGEgcmVsPSJub2ZvbGxvdyIgY2xhc3M9InNlbGYtcmVmZXJlci1saW5rIHNoYXJlLWxpbmsgYWN0aW9uYnV0dG9ubGluayIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9ub2pzLnBocCIgZGF0YS10ZW1wbGF0ZT0iaHR0cHM6Ly90d2l0dGVyLmNvbS9pbnRlbnQvdHdlZXQ/dXJsPSN1IyZhbXA7dGV4dD0jdCMiIHRpdGxlPSJTaGFyZSBkYXRhc2V0IG9uIFR3aXR0ZXIiIHRhcmdldD0iX2JsYW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj48c3BhbiBjbGFzcz0iZ2x5cGhpY29uIGdseXBoaWNvbi1zaGFyZSI+PC9zcGFuPiBUd2l0dGVyPC9zcGFuPjwvYT48YSByZWw9Im5vZm9sbG93IiBjbGFzcz0ic2VsZi1yZWZlcmVyLWxpbmsgc2hhcmUtbGluayBhY3Rpb25idXR0b25saW5rIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL25vanMucGhwIiBkYXRhLXRlbXBsYXRlPSJodHRwczovL3BsdXMuZ29vZ2xlLmNvbS9zaGFyZT91cmw9I3UjJmFtcDtobD1lbiIgdGl0bGU9IlNoYXJlIGRhdGFzZXQgb24gR29vZ2xlKyIgdGFyZ2V0PSJfYmxhbmsiPjxzcGFuIGNsYXNzPSJhY3Rpb25idXR0b24iPjxzcGFuIGNsYXNzPSJnbHlwaGljb24gZ2x5cGhpY29uLXNoYXJlIj48L3NwYW4+IEdvb2dsZSs8L3NwYW4+PC9hPjxzcGFuIGNsYXNzPSJzZXBhcmF0b3IiPjwvc3Bhbj48YSByZWw9Im5vZm9sbG93IiB0YXJnZXQ9Il9ibGFuayIgdGl0bGU9IkRpc3BsYXkgZXZlbnRzIGluIG1hcCIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9hZHZhbmNlZC9nbWFwLWRhdGFzZXQucGhwP2lkPTgzNjE3OCZhbXA7dmlld3BvcnRCQk9YPS01MC4xODAzNyw2Ny4xMjU5NCwtNTAuMTgwMzcsNjcuMTI1OTQiIGNsYXNzPSJhY3Rpb25idXR0b25saW5rIj48c3BhbiBjbGFzcz0iYWN0aW9uYnV0dG9uIj5TaG93IE1hcDwvc3Bhbj48L2E+PGEgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IkRpc3BsYXkgZXZlbnRzIGluIEdvb2dsZSBFYXJ0aCIgaHJlZj0iP2Zvcm1hdD1ldmVudHNfa21sIiBjbGFzcz0iYWN0aW9uYnV0dG9ubGluayI+PHNwYW4gY2xhc3M9ImFjdGlvbmJ1dHRvbiI+R29vZ2xlIEVhcnRoPC9zcGFuPjwvYT48c3BhbiBjbGFzcz0ic2VwYXJhdG9yIj48L3NwYW4+PHNwYW4gZGF0YS1iYWRnZS10eXBlPSIxIiBkYXRhLWRvaT0iMTAuMTU5NC9QQU5HQUVBLjgzNjE3OCIgZGF0YS1iYWRnZS1wb3BvdmVyPSJyaWdodCIgZGF0YS1oaWRlLW5vLW1lbnRpb25zPSJ0cnVlIiBjbGFzcz0iYWx0bWV0cmljLWVtYmVkIj48L3NwYW4+PC9wPgo8ZGl2IGNsYXNzPSJjbGVhcmZpeCI+PC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9InJvdyI+PGRpdiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0idGl0bGUiPkFic3RyYWN0OjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0iY29sLWxnLTIxIGNvbC1tZC0yMCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJkZXNjciI+PGRpdiBjbGFzcz0iYWJzdHJhY3QiPkZldyBoeWRyb2xvZ2ljYWwgc3R1ZGllcyBoYXZlIGJlZW4gbWFkZSBpbiBHcmVlbmxhbmQsIG90aGVyIHRoYW4gb24gZ2xhY2lhbCBoeWRyb2xvZ3kgYXNzb2NpYXRlZCB3aXRoIHRoZSBpY2Ugc2hlZXQuIFVuZGVyc3RhbmRpbmcgcGVybWFmcm9zdCBoeWRyb2xvZ3kgYW5kIGh5ZHJvY2xpbWF0aWMgY2hhbmdlIGFuZCB2YXJpYWJpbGl0eSwgaG93ZXZlciwgcHJvdmlkZXMga2V5IGluZm9ybWF0aW9uIGZvciB1bmRlcnN0YW5kaW5nIGNsaW1hdGUgY2hhbmdlIGVmZmVjdHMgYW5kIGZlZWRiYWNrcyBpbiB0aGUgQXJjdGljIGxhbmRzY2FwZS4gVGhpcyBwYXBlciBwcmVzZW50cyBhIG5ldyBleHRlbnNpdmUgYW5kIGRldGFpbGVkIGh5ZHJvbG9naWNhbCBhbmQgbWV0ZW9yb2xvZ2ljYWwgb3BlbiBhY2Nlc3MgZGF0YXNldCwgd2l0aCBoaWdoIHRlbXBvcmFsIHJlc29sdXRpb24gZnJvbSBhIDEuNTYga20qKjIgcGVybWFmcm9zdCBjYXRjaG1lbnQgd2l0aCBhIGxha2UgdW5kZXJsYWluIGJ5IGEgdGhyb3VnaCB0YWxpayBjbG9zZSB0byB0aGUgaWNlIHNoZWV0IGluIHRoZSBLYW5nZXJsdXNzdWFxIHJlZ2lvbiwgd2VzdGVybiBHcmVlbmxhbmQuIFRoZSBwYXBlciBkZXNjcmliZXMgdGhlIGh5ZHJvbG9naWNhbCBzaXRlIGludmVzdGlnYXRpb25zIGFuZCB1dGlsaXplZCBlcXVpcG1lbnQsIGFzIHdlbGwgYXMgdGhlIGRhdGEgY29sbGVjdGlvbiBhbmQgcHJvY2Vzc2luZy4gVGhlIGludmVzdGlnYXRpb25zIHdlcmUgcGVyZm9ybWVkIGJldHdlZW4gMjAxMCBhbmQgMjAxMy4gVGhlIGhpZ2ggc3BhdGlhbCByZXNvbHV0aW9uLCB3aXRoaW4gdGhlIGludmVzdGlnYXRlZCBhcmVhLCBvZiB0aGUgZGF0YXNldCBtYWtlcyBpdCBoaWdobHkgc3VpdGFibGUgZm9yIHZhcmlvdXMgZGV0YWlsZWQgaHlkcm9sb2dpY2FsIGFuZCBlY29sb2dpY2FsIHN0dWRpZXMgb24gY2F0Y2htZW50IHNjYWxlLjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJyb3ciPjxkaXYgY2xhc3M9ImNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9InRpdGxlIj5GdXJ0aGVyIGRldGFpbHM6PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIj48ZGl2IGNsYXNzPSJoYW5naW5nIj48YSB0YXJnZXQ9Il9zZWxmIiBocmVmPSJodHRwOi8vc3RvcmUucGFuZ2FlYS5kZS9QdWJsaWNhdGlvbnMvSm9oYW5zc29uRV9ldF9hbF8yMDE0L3R3b2JvYXRsYWtlX2dyZWVubGFuZC5qcGciPk1hcCBvZiBUd28gQm9hdCBMYWtlIGluIEdyZWVubGFuZCAoanBnIDEzIE1CKSB3aXRoIHBvc2l0aW9uIG9mIHNhbXBsaW5nIHNpdGVzPC9hPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24uLi4iIGFyaWEtbGFiZWw9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24iIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvP3E9JTQwcmVmNjU0NzciPjwvYT48L2Rpdj4KPGRpdiBjbGFzcz0iaGFuZ2luZyI+PGEgdGFyZ2V0PSJfc2VsZiIgaHJlZj0iaHR0cDovL3N0b3JlLnBhbmdhZWEuZGUvUHVibGljYXRpb25zL0pvaGFuc3NvbkVfZXRfYWxfMjAxNC9UaW1lbGFwc2VfVEJMLnppcCI+VGltZSBsYXBzIHBob3RvcyBvZiBsYWtlIDIwMTItMDktMDUgdG8gMjAxMy0wOC0xNCAobW92IGZpbGUsIHppcHBlZCAyMDUgTUIpPC9hPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24uLi4iIGFyaWEtbGFiZWw9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvIHRoaXMgcHVibGljYXRpb24iIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvP3E9JTQwcmVmNjU0MDgiPjwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+UHJvamVjdChzKTo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImhhbmdpbmciPjxzdHJvbmc+PGEgdGFyZ2V0PSJfYmxhbmsiIGhyZWY9Imh0dHBzOi8vd3d3LnJlc2VhcmNoZ2F0ZS5uZXQvcHJvamVjdC9HUmVlbmxhbmQtQW5hbG9ndWUtU3VyZmFjZS1Qcm9qZWN0LUdSQVNQIj5HUmVlbmxhbmQgQW5hbG9ndWUgU3VyZmFjZSBQcm9qZWN0PC9hPjwvc3Ryb25nPiAoR1JBU1ApPGEgY2xhc3M9InNlYXJjaGxpbmsgZ2x5cGhpY29uIGdseXBoaWNvbi1zZWFyY2giIHRhcmdldD0iX2JsYW5rIiByZWw9Im5vZm9sbG93IiB0aXRsZT0iU2VhcmNoIFBBTkdBRUEgZm9yIG90aGVyIGRhdGFzZXRzIHJlbGF0ZWQgdG8gJ0dSZWVubGFuZCBBbmFsb2d1ZSBTdXJmYWNlIFByb2plY3QnLi4uIiBhcmlhLWxhYmVsPSJTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnR1JlZW5sYW5kIEFuYWxvZ3VlIFN1cmZhY2UgUHJvamVjdCciIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvP3E9cHJvamVjdCUzQWxhYmVsJTNBR1JBU1AiPjwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+Q292ZXJhZ2U6PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIj48ZGl2IGNsYXNzPSJoYW5naW5nIGdlbyI+PGVtIGNsYXNzPSJ1bmZhcmJlIj5MYXRpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibGF0aXR1ZGUiPjY3LjEyNTk0MDwvc3Bhbj48ZW0gY2xhc3M9InVuZmFyYmUiPiAqIExvbmdpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibG9uZ2l0dWRlIj4tNTAuMTgwMzcwPC9zcGFuPjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJyb3ciPjxkaXYgY2xhc3M9ImNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9InRpdGxlIj5FdmVudChzKTo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImhhbmdpbmcgZ2VvIj48c3Ryb25nPlRCTDwvc3Ryb25nPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdUQkwnLi4uIiBhcmlhLWxhYmVsPSJTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnVEJMJyIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8/cT1ldmVudCUzQWxhYmVsJTNBVEJMIj48L2E+PGVtIGNsYXNzPSJ1bmZhcmJlIj4gKiBMYXRpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibGF0aXR1ZGUiPjY3LjEyNTk0MDwvc3Bhbj48ZW0gY2xhc3M9InVuZmFyYmUiPiAqIExvbmdpdHVkZTogPC9lbT48c3BhbiBjbGFzcz0ibG9uZ2l0dWRlIj4tNTAuMTgwMzcwPC9zcGFuPjxlbSBjbGFzcz0idW5mYXJiZSI+ICogTG9jYXRpb246IDwvZW0+PHNwYW4+VHdvIEJvYXQgTGFrZSwgS2FuZ2VybHVzc3VhcSwgR3JlZW5sYW5kPC9zcGFuPjxhIGNsYXNzPSJzZWFyY2hsaW5rIGdseXBoaWNvbiBnbHlwaGljb24tc2VhcmNoIiB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub2ZvbGxvdyIgdGl0bGU9IlNlYXJjaCBQQU5HQUVBIGZvciBvdGhlciBkYXRhc2V0cyByZWxhdGVkIHRvICdUd28gQm9hdCBMYWtlLCBLYW5nZXJsdXNzdWFxLCBHcmVlbmxhbmQnLi4uIiBhcmlhLWxhYmVsPSJTZWFyY2ggUEFOR0FFQSBmb3Igb3RoZXIgZGF0YXNldHMgcmVsYXRlZCB0byAnVHdvIEJvYXQgTGFrZSwgS2FuZ2VybHVzc3VhcSwgR3JlZW5sYW5kJyIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS8/cT1sb2NhdGlvbiUzQSUyMlR3bytCb2F0K0xha2UlMkMrS2FuZ2VybHVzc3VhcSUyQytHcmVlbmxhbmQlMjIiPjwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+Q29tbWVudDo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImFic3RyYWN0Ij5UaGUgZGF0YXNldCBjb250YWlucyBoeWRyb2xvZ2ljYWwgYW5kIG1ldGVvcm9sb2dpY2FsIGRhdGEgZnJvbSBhIGxha2UgY2F0Y2htZW50IGluIHRoZSBLYW5nZXJsdXNzdWFxIHJlZ2lvbiwgV2VzdGVybiBHcmVlbmxhbmQuIFRoZSBpbnZlc3RpZ2F0aW9ucyB3ZXJlIHBlcmZvcm1lZCBkdXJpbmcgMjAxMC0yMDEzIGFuZCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgYXJlIGluY2x1ZGVkOiBTb2lsIG1vaXN0dXJlLCBTb2lsIHRlbXBlcmF0dXJlLCBIeWRyYXVsaWMgcHJvcGVydGllcyBvZiB0aGUgYWN0aXZlIGxheWVyLCBtZXRlb3JvbG9naWNhbCBwYXJhbWV0ZXJzIGZyb20gYSBsb2NhbCB3ZWF0aGVyIHN0YXRpb24gd2l0aGluIHRoZSBjYXRjaG1lbnQsIHdhdGVyIGxldmVscyBhbmQgZGlzY2hhcmdlLCBzdWJsaW1hdGlvbiBhbmQgZXZhcG9ydGF0aW9uIG1lYXN1cm1lbnRzLCBzbm93IGRlcHRoIGFuZCBzbm93IHdhdGVyIGNvbnRlbnQgZGF0YSBhbmQgdGltZSBsYXBzZSBwaG90b3MuPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9InJvdyI+PGRpdiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0idGl0bGUiPkxpY2Vuc2U6PC9kaXY+CjwvZGl2Pgo8ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPjxkaXYgY2xhc3M9ImRlc2NyIj48ZGl2IGNsYXNzPSJoYW5naW5nIj48YSBocmVmPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLyIgcmVsPSJsaWNlbnNlIiB0YXJnZXQ9Il9ibGFuayI+PGltZyBzcmM9Ii8vd3d3LnBhbmdhZWEuZGUvc2hhcmVkL3BpY3MvbGljZW5zZXMvQ0MtQlktMy4wLnBuZyIgc3R5bGU9InZlcnRpY2FsLWFsaWduOmJhc2VsaW5lOyBib3JkZXItd2lkdGg6MDsiIGFsdD0iQ0MtQlktMy4wIiAvPiBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIDMuMCBVbnBvcnRlZDwvYT48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMyBjb2wtbWQtNCBjb2wtc20tMjQgY29sLXhzLTI0Ij48ZGl2IGNsYXNzPSJ0aXRsZSI+U2l6ZTo8L2Rpdj4KPC9kaXY+CjxkaXYgY2xhc3M9ImNvbC1sZy0yMSBjb2wtbWQtMjAgY29sLXNtLTI0IGNvbC14cy0yNCI+PGRpdiBjbGFzcz0iZGVzY3IiPjxkaXYgY2xhc3M9ImhhbmdpbmciPjU2NjMuMCBrQnl0ZXM8L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0icm93Ij48ZGl2IGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQgY29sLWxnLW9mZnNldC0zIGNvbC1tZC1vZmZzZXQtNCI+PGRpdiBjbGFzcz0idGV4dC1ibG9jayB0b3AtYm9yZGVyIj4KPGgyIGlkPSJkb3dubG9hZCI+RG93bmxvYWQgRGF0YTwvaDI+CjxwPjxhIGhyZWY9Imh0dHA6Ly9zdG9yZS5wYW5nYWVhLmRlL1B1YmxpY2F0aW9ucy9Kb2hhbnNzb25FX2V0X2FsXzIwMTQvam9oYW5zc29uX2V0YWwtMjAxNC56aXAiIHRhcmdldD0iX3NlbGYiPkRvd25sb2FkIGRhdGFzZXQ8L2E+PC9wPgo8L2Rpdj48L2Rpdj48L2Rpdj48ZGl2IGlkPSJyZWNvbW1lbmRhdGlvbnMiPjwvZGl2Pgo8L2Rpdj4NCjwvZGl2Pg0KPC9kaXY+DQo8L2Rpdj4NCjwvZGl2Pg0KPGRpdiBpZD0iZm9vdGVyLXdyYXBwZXIiIGNsYXNzPSJ0b3AtYm9yZGVyIGhpZGRlbi1wcmludCI+DQogIDxkaXYgY2xhc3M9ImNvbnRhaW5lci1mbHVpZCI+DQogICAgPGZvb3RlciBjbGFzcz0icm93Ij48IS0tIHZvbGxlIFNjcmVlbi1CcmVpdGUgLS0+DQogICAgICA8ZGl2IGNsYXNzPSJjb250ZW50LXdyYXBwZXIiPjwhLS0gbWF4LiBCcmVpdGUgLS0+DQogICAgICAgIDxkaXYgY2xhc3M9ImJsaW5kc3BhbHRlIGNvbC1sZy0zIGNvbC1tZC00IGNvbC1zbS00IGNvbC14cy00Ij48L2Rpdj4NCiAgICAgICAgPGRpdiBpZD0iZm9vdGVyLWhvc3RlZC1ieS1hcmVhIiBjbGFzcz0iY29sLWxnLTE4IGNvbC1tZC05IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgIDwhLS08ZGl2IGNsYXNzPSJjb2wtbGctMjQgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPi0tPg0KICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbC1sZy0xMiBjb2wtbWQtMjQgY29sLXNtLTI0IGNvbC14cy0yNCI+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJoZWFkbGluZSB1bmRlcmxpbmVkIj4NCiAgICAgICAgICAgICAgUEFOR0FFQSBpcyBob3N0ZWQgYnkNCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgDQogICAgICAgICAgICA8ZGl2Pg0KICAgICAgICAgICAgICA8cD4NCiAgICAgICAgICAgICAgICBBbGZyZWQgV2VnZW5lciBJbnN0aXR1dGUsIEhlbG1ob2x0eiBDZW50ZXIgZm9yIFBvbGFyIGFuZCBNYXJpbmUgUmVzZWFyY2ggKEFXSSk8YnIvPg0KICAgICAgICAgICAgICAgIENlbnRlciBmb3IgTWFyaW5lIEVudmlyb25tZW50YWwgU2NpZW5jZXMsIFVuaXZlcnNpdHkgb2YgQnJlbWVuIChNQVJVTSkNCiAgICAgICAgICAgICAgPC9wPg0KICAgICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImhlYWRsaW5lIHVuZGVybGluZWQiPg0KICAgICAgICAgICAgICBUaGUgU3lzdGVtIGlzIHN1cHBvcnRlZCBieQ0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICANCiAgICAgICAgICAgIDxkaXY+DQogICAgICAgICAgICAgIDxwPg0KICAgICAgICAgICAgICAgIFRoZSBFdXJvcGVhbiBDb21taXNzaW9uLCBSZXNlYXJjaDxici8+DQogICAgICAgICAgICAgICAgRmVkZXJhbCBNaW5pc3RyeSBvZiBFZHVjYXRpb24gYW5kIFJlc2VhcmNoIChCTUJGKTxici8+DQogICAgICAgICAgICAgICAgRGV1dHNjaGUgRm9yc2NodW5nc2dlbWVpbnNjaGFmdCAoREZHKTxici8+DQogICAgICAgICAgICAgICAgSW50ZXJuYXRpb25hbCBPY2VhbiBEaXNjb3ZlcnkgUHJvZ3JhbSAoSU9EUCkNCiAgICAgICAgICAgICAgPC9wPg0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICA8ZGl2IGNsYXNzPSJjb2wtbGctMTIgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaGVhZGxpbmUgdW5kZXJsaW5lZCI+DQogICAgICAgICAgICAgIFBBTkdBRUEgaXMgbWVtYmVyIG9mDQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIA0KICAgICAgICAgICAgPGRpdj4NCiAgICAgICAgICAgICAgPGEgaHJlZj0iLy93d3cuaWNzdS13ZHMub3JnLyIgdGFyZ2V0PSJfYmxhbmsiIHRpdGxlPSJJQ1NVIFdvcmxkIERhdGEgU3lzdGVtIj4NCiAgICAgICAgICAgICAgICA8aW1nIGNsYXNzPSJjb2wtbGctNiBjb2wtbWQtNiBjb2wtc20tNiBjb2wteHMtNiIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL2xvZ29zL2xvZ28td2RzLWJsb2NrLnBuZyIgYWx0PSJJQ1NVIFdvcmxkIERhdGEgU3lzdGVtIj4NCiAgICAgICAgICAgICAgPC9hPg0KICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy53bW8uaW50LyIgdGFyZ2V0PSJfYmxhbmsiIHRpdGxlPSJXb3JsZCBNZXRlb3JvbG9naWNhbCBPcmdhbml6YXRpb24iPg0KICAgICAgICAgICAgICAgIDxpbWcgY2xhc3M9ImNvbC1sZy02IGNvbC1tZC02IGNvbC1zbS02IGNvbC14cy02IiBzcmM9Ii8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3YuODQ3NWExMmQwNTQxMTMxN2YzZDk5MzU5YjAxN2EyNjMvbG9nb3MvbG9nby13bW8tYmxvY2sucG5nIiBhbHQ9IldvcmxkIE1ldGVvcm9sb2dpY2FsIE9yZ2FuaXphdGlvbiI+DQogICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgICAgPGRpdiBpZD0iZm9vdGVyLXNvY2lhbC1hcmVhIiBjbGFzcz0iY29sLWxnLTMgY29sLW1kLTI0IGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgIDxkaXYgaWQ9ImZvb3Rlci1zb2NpYWwtYXJlYS13cmFwcGVyIiBjbGFzcz0iY29sLWxnLTI0IGNvbC1tZC0yNCBjb2wtc20tMjQgY29sLXhzLTI0Ij4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImJsaW5kc3BhbHRlIGNvbC1sZy0wIGNvbC1tZC00Ij48L2Rpdj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbC1sZy0yNCBjb2wtbWQtNSBjb2wtbWQtNSBjb2wteHMtMTAiPg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJ1bmRlcmxpbmVkIj5TaGFyZSBvbi4uLjwvZGl2Pg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJzb2NpYWwtaWNvbnMiPg0KICAgICAgICAgICAgICAgIDxhIHJlbD0ibm9mb2xsb3ciIGNsYXNzPSJzZWxmLXJlZmVyZXItbGluayBzaGFyZS1saW5rIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL25vanMucGhwIiBkYXRhLXRlbXBsYXRlPSJodHRwczovL3d3dy5mYWNlYm9vay5jb20vc2hhcmVyLnBocD91PSN1IyZhbXA7dD0jdCMiIHRpdGxlPSJTaGFyZSBvbiBGYWNlYm9vayIgdGFyZ2V0PSJfYmxhbmsiPg0KICAgICAgICAgICAgICAgICAgPGltZyBpZD0iZmFjZWJvb2staWNvbiIgY2xhc3M9ImNvbC1sZy04IGNvbC1tZC04IGNvbC1zbS04IGNvbC14cy04IiBzcmM9Ii8vd3d3LnBhbmdhZWEuZGUvYXNzZXRzL3YuODQ3NWExMmQwNTQxMTMxN2YzZDk5MzU5YjAxN2EyNjMvc29jaWFsLWljb25zL2ZhY2Vib29rLWljb24ucG5nIiBhbHQ9IkZhY2Vib29rIEljb24iPg0KICAgICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgICAgICA8YSByZWw9Im5vZm9sbG93IiBjbGFzcz0ic2VsZi1yZWZlcmVyLWxpbmsgc2hhcmUtbGluayIgaHJlZj0iLy93d3cucGFuZ2FlYS5kZS9ub2pzLnBocCIgZGF0YS10ZW1wbGF0ZT0iaHR0cHM6Ly90d2l0dGVyLmNvbS9pbnRlbnQvdHdlZXQ/dXJsPSN1IyZhbXA7dGV4dD0jdCMiIHRpdGxlPSJTaGFyZSBvbiBUd2l0dGVyIiB0YXJnZXQ9Il9ibGFuayI+DQogICAgICAgICAgICAgICAgICA8aW1nIGlkPSJ0d2l0dGVyLWljb24iIGNsYXNzPSJjb2wtbGctOCBjb2wtbWQtOCBjb2wtc20tOCBjb2wteHMtOCIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL3NvY2lhbC1pY29ucy90d2l0dGVyLWljb24ucG5nIiBhbHQ9IlR3aXR0ZXIgSWNvbiI+DQogICAgICAgICAgICAgICAgPC9hPg0KICAgICAgICAgICAgICAgIDxhIHJlbD0ibm9mb2xsb3ciIGNsYXNzPSJzZWxmLXJlZmVyZXItbGluayBzaGFyZS1saW5rIiBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL25vanMucGhwIiBkYXRhLXRlbXBsYXRlPSJodHRwczovL3BsdXMuZ29vZ2xlLmNvbS9zaGFyZT91cmw9I3UjJmFtcDtobD1lbiIgdGl0bGU9IlNoYXJlIG9uIEdvb2dsZSsiIHRhcmdldD0iX2JsYW5rIj4NCiAgICAgICAgICAgICAgICAgIDxpbWcgaWQ9ImdwbHVzLWljb24iIGNsYXNzPSJjb2wtbGctOCBjb2wtbWQtOCBjb2wtc20tOCBjb2wteHMtOCIgc3JjPSIvL3d3dy5wYW5nYWVhLmRlL2Fzc2V0cy92Ljg0NzVhMTJkMDU0MTEzMTdmM2Q5OTM1OWIwMTdhMjYzL3NvY2lhbC1pY29ucy9ncGx1cy1pY29uLnBuZyIgYWx0PSJHb29nbGUrIEljb24iPg0KICAgICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImJsaW5kc3BhbHRlIGNvbG8tbGctMCBjb2wtbWQtMTgiPjwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgICAgICANCiAgICAgICAgPGRpdiBpZD0iZm9vdGVyLW1lbnUtYXJlYSIgY2xhc3M9ImNvbC1sZy0yNCBjb2wtbWQtMjQgY29sLXNtLTI0IGNvbC14cy0yNCI+DQogICAgICAgICAgPGRpdiBjbGFzcz0iYmxpbmRzcGFsdGUgY29sLWxnLTMgY29sLW1kLTQgY29sLXNtLTQgY29sLXhzLTQiPjwvZGl2Pg0KICAgICAgICAgIDxkaXYgaWQ9ImZvb3Rlci1tZW51LXdyYXBwZXIiIGNsYXNzPSJjb2wtbGctMjEgY29sLW1kLTIwIGNvbC1zbS0yNCBjb2wteHMtMjQiPg0KICAgICAgICAgICAgPG5hdiBpZD0iZm9vdGVyLW5hdiI+DQogICAgICAgICAgICAgIDx1bD4NCiAgICAgICAgICAgICAgICA8bGkgaWQ9ImFib3V0LWxlZ2FsLW5vdGljZSI+DQogICAgICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fib3V0L2xlZ2FsLnBocCI+TGVnYWwgbm90aWNlPC9hPg0KICAgICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICAgICAgPGxpIGlkPSJhYm91dC1wcml2YWN5LXBvbGljeSI+DQogICAgICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fib3V0L3ByaXZhY3lwb2xpY3kucGhwIj5Qcml2YWN5IHBvbGljeTwvYT4NCiAgICAgICAgICAgICAgICA8L2xpPg0KICAgICAgICAgICAgICAgIDxsaSBpZD0iYWJvdXQtY29va2llcyI+DQogICAgICAgICAgICAgICAgICA8YSBocmVmPSIvL3d3dy5wYW5nYWVhLmRlL2Fib3V0L2Nvb2tpZXMucGhwIj5Db29raWVzPC9hPg0KICAgICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICAgICAgPGxpIGlkPSJhYm91dC1jb250YWN0Ij4NCiAgICAgICAgICAgICAgICAgIDxhIGhyZWY9Ii8vd3d3LnBhbmdhZWEuZGUvY29udGFjdC8iPkNvbnRhY3Q8L2E+DQogICAgICAgICAgICAgICAgPC9saT4NCiAgICAgICAgICAgICAgPC91bD4NCiAgICAgICAgICAgIDwvbmF2Pg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2xlYXJmaXgiPjwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvZm9vdGVyPg0KICA8L2Rpdj4NCjwvZGl2Pg0KPC9ib2R5Pgo8L2h0bWw+Cg== + http_version: + recorded_at: Thu, 29 Nov 2018 12:17:36 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_status_code_200.yml b/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_status_code_200.yml index 504510b00..0dcd94ce8 100644 --- a/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_status_code_200.yml +++ b/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_status_code_200.yml @@ -17,7 +17,7 @@ http_interactions: message: OK headers: Date: - - Wed, 26 Sep 2018 09:05:42 GMT + - Mon, 19 Nov 2018 13:19:28 GMT Content-Type: - application/json;charset=UTF-8 Connection: @@ -28,5 +28,5 @@ http_interactions: encoding: ASCII-8BIT string: '{"responseCode":1,"handle":"10.5438/FJ3W-0SHD","values":[{"index":1,"type":"URL","data":{"format":"string","value":"https://blog.datacite.org/data-driven-development/"},"ttl":86400,"timestamp":"2016-12-19T15:06:36Z"}]}' http_version: - recorded_at: Wed, 26 Sep 2018 09:05:42 GMT + recorded_at: Mon, 19 Nov 2018 13:19:28 GMT recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_url.yml b/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_url.yml index 504510b00..88d8baf59 100644 --- a/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_url.yml +++ b/spec/fixtures/vcr_cassettes/dois/GET_/dois/DOI/get-url/returns_url.yml @@ -17,7 +17,7 @@ http_interactions: message: OK headers: Date: - - Wed, 26 Sep 2018 09:05:42 GMT + - Mon, 19 Nov 2018 13:19:27 GMT Content-Type: - application/json;charset=UTF-8 Connection: @@ -28,5 +28,5 @@ http_interactions: encoding: ASCII-8BIT string: '{"responseCode":1,"handle":"10.5438/FJ3W-0SHD","values":[{"index":1,"type":"URL","data":{"format":"string","value":"https://blog.datacite.org/data-driven-development/"},"ttl":86400,"timestamp":"2016-12-19T15:06:36Z"}]}' http_version: - recorded_at: Wed, 26 Sep 2018 09:05:42 GMT + recorded_at: Mon, 19 Nov 2018 13:19:27 GMT recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/returns_status_code_201.yml b/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/returns_status_code_201.yml new file mode 100644 index 000000000..340b5d4ac --- /dev/null +++ b/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/returns_status_code_201.yml @@ -0,0 +1,82 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7554 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 18:37:48 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - 6510584d-f399-4bbe-a1dd-1b4a8f1c07cc + Etag: + - W/"381993dc8a6b0f20960c96a3639c0284" + X-Runtime: + - '0.079075' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7554","type":"prefixes","attributes":{"registration-agency":"Crossref","created":null,"updated":"2016-09-21T21:07:27Z"},"relationships":{"clients":{"data":[]},"providers":{"data":[]}}},"included":[]}' + http_version: + recorded_at: Fri, 07 Dec 2018 18:37:48 GMT +- request: + method: get + uri: http://www.crossref.org/openurl/?format=unixref&id=doi:10.7554/elife.01567&noredirect=true&pid=tech@datacite.org + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/xml + response: + status: + code: 200 + message: OK + headers: + Server: + - Apache-Coyote/1.1 + Crossref-Deployment-Name: + - qs4-1 + Content-Type: + - text/xml;charset=UTF-8 + Content-Language: + - en-US + Date: + - Fri, 07 Dec 2018 18:37:48 GMT + Connection: + - close + body: + encoding: ASCII-8BIT + string: !binary |- + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGRvaV9yZWNvcmRzPg0KICA8ZG9pX3JlY29yZCBvd25lcj0iMTAuNzU1NCIgdGltZXN0YW1wPSIyMDE4LTA4LTIzIDA5OjQxOjQ5Ij4NCiAgICA8Y3Jvc3NyZWY+DQogICAgICA8am91cm5hbD4NCiAgICAgICAgPGpvdXJuYWxfbWV0YWRhdGEgbGFuZ3VhZ2U9ImVuIj4NCiAgICAgICAgICA8ZnVsbF90aXRsZT5lTGlmZTwvZnVsbF90aXRsZT4NCiAgICAgICAgICA8aXNzbiBtZWRpYV90eXBlPSJlbGVjdHJvbmljIj4yMDUwLTA4NFg8L2lzc24+DQogICAgICAgIDwvam91cm5hbF9tZXRhZGF0YT4NCiAgICAgICAgPGpvdXJuYWxfaXNzdWU+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8am91cm5hbF92b2x1bWU+DQogICAgICAgICAgICA8dm9sdW1lPjM8L3ZvbHVtZT4NCiAgICAgICAgICA8L2pvdXJuYWxfdm9sdW1lPg0KICAgICAgICA8L2pvdXJuYWxfaXNzdWU+DQogICAgICAgIDxqb3VybmFsX2FydGljbGUgcHVibGljYXRpb25fdHlwZT0iZnVsbF90ZXh0IiByZWZlcmVuY2VfZGlzdHJpYnV0aW9uX29wdHM9ImFueSI+DQogICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgIDx0aXRsZT5BdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvdGl0bGU+DQogICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgPGNvbnRyaWJ1dG9ycz4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJmaXJzdCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPk1hcnRpYWw8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlNhbmthcjwvc3VybmFtZT4NCiAgICAgICAgICAgICAgPGFmZmlsaWF0aW9uPkRlcGFydG1lbnQgb2YgUGxhbnQgTW9sZWN1bGFyIEJpb2xvZ3ksIFVuaXZlcnNpdHkgb2YgTGF1c2FubmUsIExhdXNhbm5lLCBTd2l0emVybGFuZDwvYWZmaWxpYXRpb24+DQogICAgICAgICAgICA8L3BlcnNvbl9uYW1lPg0KICAgICAgICAgICAgPHBlcnNvbl9uYW1lIGNvbnRyaWJ1dG9yX3JvbGU9ImF1dGhvciIgc2VxdWVuY2U9ImFkZGl0aW9uYWwiPg0KICAgICAgICAgICAgICA8Z2l2ZW5fbmFtZT5LYWlzYTwvZ2l2ZW5fbmFtZT4NCiAgICAgICAgICAgICAgPHN1cm5hbWU+TmllbWluZW48L3N1cm5hbWU+DQogICAgICAgICAgICAgIDxhZmZpbGlhdGlvbj5EZXBhcnRtZW50IG9mIFBsYW50IE1vbGVjdWxhciBCaW9sb2d5LCBVbml2ZXJzaXR5IG9mIExhdXNhbm5lLCBMYXVzYW5uZSwgU3dpdHplcmxhbmQ8L2FmZmlsaWF0aW9uPg0KICAgICAgICAgICAgPC9wZXJzb25fbmFtZT4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJhZGRpdGlvbmFsIj4NCiAgICAgICAgICAgICAgPGdpdmVuX25hbWU+TGF1cmE8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlJhZ25pPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPklvYW5uaXM8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlhlbmFyaW9zPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+Vml0YWwtSVQsIFN3aXNzIEluc3RpdHV0ZSBvZiBCaW9pbmZvcm1hdGljcywgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPkNocmlzdGlhbiBTPC9naXZlbl9uYW1lPg0KICAgICAgICAgICAgICA8c3VybmFtZT5IYXJkdGtlPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgPC9jb250cmlidXRvcnM+DQogICAgICAgICAgPGFic3RyYWN0Pg0KICAgICAgICAgICAgPHA+QW1vbmcgdmFyaW91cyBhZHZhbnRhZ2VzLCB0aGVpciBzbWFsbCBzaXplIG1ha2VzIG1vZGVsIG9yZ2FuaXNtcyBwcmVmZXJyZWQgc3ViamVjdHMgb2YgaW52ZXN0aWdhdGlvbi4gWWV0LCBldmVuIGluIG1vZGVsIHN5c3RlbXMgZGV0YWlsZWQgYW5hbHlzaXMgb2YgbnVtZXJvdXMgZGV2ZWxvcG1lbnRhbCBwcm9jZXNzZXMgYXQgY2VsbHVsYXIgbGV2ZWwgaXMgc2V2ZXJlbHkgaGFtcGVyZWQgYnkgdGhlaXIgc2NhbGUuIEZvciBpbnN0YW5jZSwgc2Vjb25kYXJ5IGdyb3d0aCBvZiBBcmFiaWRvcHNpcyBoeXBvY290eWxzIGNyZWF0ZXMgYSByYWRpYWwgcGF0dGVybiBvZiBoaWdobHkgc3BlY2lhbGl6ZWQgdGlzc3VlcyB0aGF0IGNvbXByaXNlcyBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHN0YXJ0aW5nIGZyb20gYSBmZXcgZG96ZW4uIFRoaXMgZHluYW1pYyBwcm9jZXNzIGlzIGRpZmZpY3VsdCB0byBmb2xsb3cgYmVjYXVzZSBvZiBpdHMgc2NhbGUgYW5kIGJlY2F1c2UgaXQgY2FuIG9ubHkgYmUgaW52ZXN0aWdhdGVkIGludmFzaXZlbHksIHByZWNsdWRpbmcgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBjZWxsIHByb2xpZmVyYXRpb24sIGRpZmZlcmVudGlhdGlvbiwgYW5kIHBhdHRlcm5pbmcgZXZlbnRzIGludm9sdmVkLiBUbyBvdmVyY29tZSBzdWNoIGxpbWl0YXRpb24sIHdlIGVzdGFibGlzaGVkIGFuIGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d5IGFwcHJvYWNoLiBXZSBhY3F1aXJlZCBoeXBvY290eWwgY3Jvc3Mtc2VjdGlvbnMgZnJvbSB0aWxlZCBoaWdoLXJlc29sdXRpb24gaW1hZ2VzIGFuZCBleHRyYWN0ZWQgdGhlaXIgaW5mb3JtYXRpb24gY29udGVudCB1c2luZyBjdXN0b20gaGlnaC10aHJvdWdocHV0IGltYWdlIHByb2Nlc3NpbmcgYW5kIHNlZ21lbnRhdGlvbi4gQ291cGxlZCB3aXRoIGF1dG9tYXRlZCBjZWxsIHR5cGUgcmVjb2duaXRpb24gdGhyb3VnaCBtYWNoaW5lIGxlYXJuaW5nLCB3ZSBjb3VsZCBlc3RhYmxpc2ggYSBjZWxsdWxhciByZXNvbHV0aW9uIGF0bGFzIHRoYXQgcmV2ZWFscyB2YXNjdWxhciBtb3JwaG9keW5hbWljcyBkdXJpbmcgc2Vjb25kYXJ5IGdyb3d0aCwgZm9yIGV4YW1wbGUgZXF1aWRpc3RhbnQgcGhsb2VtIHBvbGUgZm9ybWF0aW9uLjwvcD4NCiAgICAgICAgICA8L2Fic3RyYWN0Pg0KICAgICAgICAgIDxhYnN0cmFjdCBhYnN0cmFjdC10eXBlPSJleGVjdXRpdmUtc3VtbWFyeSI+DQogICAgICAgICAgICA8cD5PdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgbGl2aW5nIHdvcmxkIGhhcyBiZWVuIGFkdmFuY2VkIGdyZWF0bHkgYnkgc3R1ZGllcyBvZiDigJhtb2RlbCBvcmdhbmlzbXPigJksIHN1Y2ggYXMgbWljZSwgemVicmFmaXNoLCBhbmQgZnJ1aXQgZmxpZXMuIFN0dWR5aW5nIHRoZXNlIGNyZWF0dXJlcyBoYXMgYmVlbiBjcnVjaWFsIHRvIHVuY292ZXJpbmcgdGhlIGdlbmVzIHRoYXQgY29udHJvbCBob3cgb3VyIGJvZGllcyBkZXZlbG9wIGFuZCBncm93LCBhbmQgYWxzbyB0byBkaXNjb3ZlciB0aGUgZ2VuZXRpYyBiYXNpcyBvZiBkaXNlYXNlcyBzdWNoIGFzIGNhbmNlci48L3A+DQogICAgICAgICAgICA8cD5UaGFsZSBjcmVzc+KAlG9yIEFyYWJpZG9wc2lzIHRoYWxpYW5hIHRvIGdpdmUgaXRzIGZvcm1hbCBuYW1l4oCUaXMgdGhlIG1vZGVsIG9yZ2FuaXNtIG9mIGNob2ljZSBmb3IgbWFueSBwbGFudCBiaW9sb2dpc3RzLiBUaGlzIHRpbnkgd2VlZCBoYXMgYmVlbiB3aWRlbHkgc3R1ZGllZCBiZWNhdXNlIGl0IGNhbiBjb21wbGV0ZSBpdHMgbGlmZWN5Y2xlLCBmcm9tIHNlZWQgdG8gc2VlZCwgaW4gYWJvdXQgNiB3ZWVrcywgYW5kIGJlY2F1c2UgaXRzIHJlbGF0aXZlbHkgc21hbGwgZ2Vub21lIHNpbXBsaWZpZXMgdGhlIHNlYXJjaCBmb3IgZ2VuZXMgdGhhdCBjb250cm9sIHNwZWNpZmljIHRyYWl0cy4gSG93ZXZlciwgYXMgd2l0aCBvdGhlciBtdWNoLXN0dWRpZWQgbW9kZWwgc3lzdGVtcywgdW5kZXJzdGFuZGluZyB0aGUgY2hhbmdlcyB0aGF0IHVuZGVycGluIHRoZSBkZXZlbG9wbWVudCBvZiBzb21lIG9mIHRoZSBtb3JlIGNvbXBsZXggdGlzc3VlcyBpbiBBcmFiaWRvcHNpcyBoYXMgYmVlbiBzZXZlcmVseSBoYW1wZXJlZCBieSB0aGUgc2hlYXIgbnVtYmVyIG9mIGNlbGxzIGludm9sdmVkLjwvcD4NCiAgICAgICAgICAgIDxwPkFmdGVyIGl0IGhhcyBlbWVyZ2VkIGZyb20gdGhlIHNlZWQsIHRoZSBwbGFudOKAmXMgZmlyc3Qgc3RlbSB3aWxsIGRldmVsb3AgZnJvbSBhIGZldyBkb3plbiBjZWxscyBpbiB3aWR0aCB0byBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHdpdGggaGlnaGx5IHNwZWNpYWxpemVkIHRpc3N1ZXMgYXJyYW5nZWQgaW4gYSBjb21wbGV4IHBhdHRlcm4gb2YgY29uY2VudHJpYyBjaXJjbGVzLiBBbHRob3VnaCB0aGlzIHN0ZW0gdGhpY2tlbmluZyBwcm9jZXNzIHJlcHJlc2VudHMgYSBtYWpvciBkZXZlbG9wbWVudGFsIGNoYW5nZSBpbiBtYW55IHBsYW50c+KAlGZyb20gQXJhYmlkb3BzaXMgdG8gb2FrIHRyZWVz4oCUaXQgaGFzIGJlZW4gdW5kZXItcmVzZWFyY2hlZC4gVGhpcyBpcyBwYXJ0bHkgYmVjYXVzZSBpdCBpbnZvbHZlcyBzbyBtYW55IGRpZmZlcmVudCBjZWxscywgYW5kIGFsc28gYmVjYXVzZSBpdCBjYW4gb25seSBiZSBvYnNlcnZlZCBpbiB0aGluIHNlY3Rpb25zIGN1dCBvdXQgb2YgdGhlIHBsYW504oCZcyBzdGVtLjwvcD4NCiAgICAgICAgICAgIDxwPk5vdyBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gaGF2ZSBkZXZlbG9wZWQgYSBub3ZlbCBhcHByb2FjaCwgdGVybWVkIOKAmGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d54oCZLCB0byBvdmVyY29tZSB0aGVzZSBwcm9ibGVtcy4gVGhpcyBzdHJhdGVneSBpbnZvbHZlcyDigJh0ZWFjaGluZ+KAmSBhIGNvbXB1dGVyIHRvIGF1dG9tYXRpY2FsbHkgcmVjb2duaXplIGRpZmZlcmVudCBwbGFudCBjZWxscyBhbmQgdG8gbWVhc3VyZSB0aGVpciBpbXBvcnRhbnQgZmVhdHVyZXMgaW4gaGlnaC1yZXNvbHV0aW9uIGltYWdlcyBvZiB0aXNzdWUgc2VjdGlvbnMuIFRoZSByZXN1bHRpbmcg4oCYbWFw4oCZIG9mIHRoZSBkZXZlbG9waW5nIHN0ZW3igJR3aGljaCByZXF1aXJlZCBvdmVyIDgwMCBociBvZiBjb21wdXRpbmcgdGltZSB0byBjb21wbGV0ZeKAlHJldmVhbHMgdGhlIGNoYW5nZXMgdG8gY2VsbHMgYW5kIHRpc3N1ZXMgYXMgdGhleSBkZXZlbG9wIHRoYXQgYWxsb3cgdGhlIHRyYW5zcG9ydCBvZiB3YXRlciwgc3VnYXJzIGFuZCBudXRyaWVudHMgYmV0d2VlbiB0aGUgYWJvdmUtIGFuZCBiZWxvdy1ncm91bmQgb3JnYW5zLiBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gc3VnZ2VzdCB0aGF0IHRoZWlyIG5vdmVsIGFwcHJvYWNoIGNvdWxkLCBpbiB0aGUgZnV0dXJlLCBhbHNvIGJlIGFwcGxpZWQgdG8gc3R1ZHkgdGhlIGRldmVsb3BtZW50IG9mIG90aGVyIHRpc3N1ZXMgYW5kIG9yZ2FuaXNtcywgaW5jbHVkaW5nIGFuaW1hbHMuPC9wPg0KICAgICAgICAgIDwvYWJzdHJhY3Q+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8cHVibGlzaGVyX2l0ZW0+DQogICAgICAgICAgICA8aXRlbV9udW1iZXIgaXRlbV9udW1iZXJfdHlwZT0iYXJ0aWNsZV9udW1iZXIiPmUwMTU2NzwvaXRlbV9udW1iZXI+DQogICAgICAgICAgICA8aWRlbnRpZmllciBpZF90eXBlPSJkb2kiPjEwLjc1NTQvZUxpZmUuMDE1Njc8L2lkZW50aWZpZXI+DQogICAgICAgICAgPC9wdWJsaXNoZXJfaXRlbT4NCiAgICAgICAgICA8cHJvZ3JhbSBuYW1lPSJmdW5kcmVmIj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5FTUJPIGxvbmd0ZXJtIHBvc3QtZG9jdG9yYWwgZmVsbG93c2hpcHM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5NYXJpZSBIZWltLVZvZWd0bGluPC9hc3NlcnRpb24+DQogICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+DQogICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX2lkZW50aWZpZXIiIHByb3ZpZGVyPSJjcm9zc3JlZiI+NTAxMTAwMDA2MzkwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxwcm9ncmFtIG5hbWU9IkFjY2Vzc0luZGljYXRvcnMiPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0iYW0iPmh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzMuMC88L2xpY2Vuc2VfcmVmPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InRkbSI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxjcm9zc21hcms+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3ZlcnNpb24+MTwvY3Jvc3NtYXJrX3ZlcnNpb24+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3BvbGljeT5lTGlmZXNjaWVuY2VzPC9jcm9zc21hcmtfcG9saWN5Pg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgICA8Y3Jvc3NtYXJrX2RvbWFpbj4NCiAgICAgICAgICAgICAgICA8ZG9tYWluPnd3dy5lbGlmZXNjaWVuY2VzLm9yZzwvZG9tYWluPg0KICAgICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW4+DQogICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5fZXhjbHVzaXZlPmZhbHNlPC9jcm9zc21hcmtfZG9tYWluX2V4Y2x1c2l2ZT4NCiAgICAgICAgICAgIDxjdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icmVjZWl2ZWQiIGxhYmVsPSJSZWNlaXZlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIwIj4yMDEzLTA5LTIwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iYWNjZXB0ZWQiIGxhYmVsPSJBY2NlcHRlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIxIj4yMDEzLTEyLTI0PC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icHVibGlzaGVkIiBsYWJlbD0iUHVibGlzaGVkIiBncm91cF9uYW1lPSJwdWJsaWNhdGlvbl9oaXN0b3J5IiBncm91cF9sYWJlbD0iUHVibGljYXRpb24gSGlzdG9yeSIgb3JkZXI9IjIiPjIwMTQtMDItMTE8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iZnVuZHJlZiI+DQogICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgRU1CTw0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIj5odHRwOi8vZHguZG9pLm9yZy8xMC4xMzAzOS81MDExMDAwMDMwNDM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX25hbWUiPg0KICAgICAgICAgICAgICAgICAgICBTd2lzcyBOYXRpb25hbCBTY2llbmNlIEZvdW5kYXRpb24NCiAgICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfaWRlbnRpZmllciI+aHR0cDovL2R4LmRvaS5vcmcvMTAuMTMwMzkvNTAxMTAwMDAxNzExPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIiBwcm92aWRlcj0iY3Jvc3NyZWYiPmh0dHA6Ly9keC5kb2kub3JnLzEwLjEzMDM5LzUwMTEwMDAwNjM5MDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iQWNjZXNzSW5kaWNhdG9ycyI+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89ImFtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0idGRtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgICAgPC9jdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgPC9jcm9zc21hcms+DQogICAgICAgICAgPHByb2dyYW0+DQogICAgICAgICAgICA8cmVsYXRlZF9pdGVtPg0KICAgICAgICAgICAgICA8ZGVzY3JpcHRpb24+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvZGVzY3JpcHRpb24+DQogICAgICAgICAgICAgIDxpbnRlcl93b3JrX3JlbGF0aW9uIGlkZW50aWZpZXItdHlwZT0iZG9pIiByZWxhdGlvbnNoaXAtdHlwZT0iaXNTdXBwbGVtZW50ZWRCeSI+MTAuNTA2MS9kcnlhZC5iODM1azwvaW50ZXJfd29ya19yZWxhdGlvbj4NCiAgICAgICAgICAgIDwvcmVsYXRlZF9pdGVtPg0KICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICA8YXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgICA8YXJjaGl2ZSBuYW1lPSJDTE9DS1NTIiAvPg0KICAgICAgICAgIDwvYXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3PC9kb2k+DQogICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NzwvcmVzb3VyY2U+DQogICAgICAgICAgICA8Y29sbGVjdGlvbiBwcm9wZXJ0eT0idGV4dC1taW5pbmciPg0KICAgICAgICAgICAgICA8aXRlbT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2UgbWltZV90eXBlPSJhcHBsaWNhdGlvbi9wZGYiPmh0dHBzOi8vY2RuLmVsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2VsaWZlLTAxNTY3LXYxLnBkZjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvaXRlbT4NCiAgICAgICAgICAgICAgPGl0ZW0+DQogICAgICAgICAgICAgICAgPHJlc291cmNlIG1pbWVfdHlwZT0iYXBwbGljYXRpb24veG1sIj5odHRwczovL2Nkbi5lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9lbGlmZS0wMTU2Ny12MS54bWw8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2l0ZW0+DQogICAgICAgICAgICA8L2NvbGxlY3Rpb24+DQogICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICA8Y2l0YXRpb25fbGlzdD4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmU8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Qm9ua2U8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT40MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTgxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkFQTCByZWd1bGF0ZXMgdmFzY3VsYXIgdGlzc3VlIGlkZW50aXR5IGluIEFyYWJpZG9wc2lzPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmF0dXJlMDIxMDA8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+R2VuZXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+QnJlbm5lcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Mjwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40MTM8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA5PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+SW4gdGhlIGJlZ2lubmluZyB3YXMgdGhlIHdvcm08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTUzNC9nZW5ldGljcy4xMDkuMTA0OTc2PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPlBoeXNpb2xvZ2lhIFBsYW50YXJ1bTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5DaGFmZmV5PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjU5NDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TZWNvbmRhcnkgeHlsZW0gZGV2ZWxvcG1lbnQgaW4gQXJhYmlkb3BzaXM6IGEgbW9kZWwgZm9yIHdvb2QgZm9ybWF0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzQvai4xMzk5LTMwNTQuMjAwMi4xMTQwNDEzLng8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TmV1cmFsIGNvbXB1dGF0aW9uPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkNoYW5nPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjExOTwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDE8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5UcmFpbmluZyBudS1zdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyczogdGhlb3J5IGFuZCBhbGdvcml0aG1zPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExNjIvMDg5OTc2NjAxNzUwMzk5MzM1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk1hY2hpbmUgTGVhcm5pbmc8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Q29ydGVzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjczPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5NTwvY1llYXI+DQogICAgICAgICAgICAgIDxkb2kgcHJvdmlkZXI9ImNyb3NzcmVmIj4xMC4xMDA3L0JGMDA5OTQwMTg8L2RvaT4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+U3VwcG9ydC12ZWN0b3IgTmV0d29ya3M8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNiI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkRldmVsb3BtZW50PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkRvbGFuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE5PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjcxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5MzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNlbGx1bGFyIG9yZ2FuaXNhdGlvbiBvZiB0aGUgQXJhYmlkb3BzaXMgdGhhbGlhbmEgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI3Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+U2VtaW5hcnMgaW4gQ2VsbCAmYW1wOyBEZXZlbG9wbWVudGFsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RWxvPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTA5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TdGVtIGNlbGwgZnVuY3Rpb24gZHVyaW5nIHBsYW50IHZhc2N1bGFyIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMTYvai5zZW1jZGIuMjAwOS4wOS4wMDk8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI4Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+RGV2ZWxvcG1lbnQ8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RXRjaGVsbHM8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4xNDA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjIyNDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTM8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5XT1g0IGFuZCBXT1gxNCBhY3QgZG93bnN0cmVhbSBvZiB0aGUgUFhZIHJlY2VwdG9yIGtpbmFzZSB0byByZWd1bGF0ZSBwbGFudCB2YXNjdWxhciBwcm9saWZlcmF0aW9uIGluZGVwZW5kZW50bHkgb2YgYW55IHJvbGUgaW4gdmFzY3VsYXIgb3JnYW5pc2F0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEyNDIvZGV2LjA5MTMxNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QTE9TIEdlbmV0aWNzPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkV0Y2hlbGxzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+ODwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT5lMTAwMjk5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudCB2YXNjdWxhciBjZWxsIGRpdmlzaW9uIGlzIG1haW50YWluZWQgYnkgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBQWFkgYW5kIGV0aHlsZW5lIHNpZ25hbGxpbmc8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTM3MS9qb3VybmFsLnBnZW4uMTAwMjk5NzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TW9sZWN1bGFyIFN5c3RlbXMgQmlvbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5GdWNoczwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MzcwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNsdXN0ZXJpbmcgcGhlbm90eXBlIHBvcHVsYXRpb25zIGJ5IGdlbm9tZS13aWRlIFJOQWkgYW5kIG11bHRpcGFyYW1ldHJpYyBpbWFnaW5nPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbXNiLjIwMTAuMjU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxMSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkJpbyBTeXN0ZW1zPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkdyYW5xdmlzdDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjExMDwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42MDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5CYVNBUi1BIHRvb2wgaW4gUiBmb3IgZnJlcXVlbmN5IGRldGVjdGlvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouYmlvc3lzdGVtcy4yMDEyLjA3LjAwNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+Q3VycmVudCBPcGluaW9uIGluIFBsYW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+R3Jvb3ZlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjk8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NTU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGV2ZWxvcG1lbnRhbCBtZWNoYW5pc21zIHJlZ3VsYXRpbmcgc2Vjb25kYXJ5IGdyb3d0aCBpbiB3b29keSBwbGFudHM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLnBiaS4yMDA1LjExLjAxMzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEzIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIyPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MTg8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+VERJRiBwZXB0aWRlIHNpZ25hbGluZyByZWd1bGF0ZXMgdmFzY3VsYXIgc3RlbSBjZWxsIHByb2xpZmVyYXRpb24gdmlhIHRoZSBXT1g0IGhvbWVvYm94IGdlbmUgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNS90cGMuMTEwLjA3NjA4MzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjE0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMgb2YgdGhlIFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjEwNTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT4xNTIwODwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDg8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5Ob24tY2VsbC1hdXRvbm9tb3VzIGNvbnRyb2wgb2YgdmFzY3VsYXIgc3RlbSBjZWxsIGZhdGUgYnkgYSBDTEUgcGVwdGlkZS9yZWNlcHRvciBzeXN0ZW08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjA4MDg0NDQxMDU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkNlbGw8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+TWV5ZXJvd2l0ejwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjU2PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BcmFiaWRvcHNpcywgYSB1c2VmdWwgd2VlZDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2LzAwOTItODY3NCg4OSk5MDkwMC04PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5TY2llbmNlPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk1leWVyb3dpdHo8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yOTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTQ4MjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudHMgY29tcGFyZWQgdG8gYW5pbWFsczogdGhlIGJyb2FkZXN0IGNvbXBhcmF0aXZlIHN0dWR5IG9mIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExMjYvc2NpZW5jZS4xMDY2NjA5PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTciPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QbGFudCBQaHlzaW9sPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk5pZW1pbmVuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM1PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjY1MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDQ8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BIHdlZWQgZm9yIHdvb2Q/IEFyYWJpZG9wc2lzIGFzIGEgZ2VuZXRpYyBtb2RlbCBmb3IgeHlsZW0gZGV2ZWxvcG1lbnQ8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNC9wcC4xMDQuMDQwMjEyPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTgiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmUgQmlvdGVjaG5vbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5Ob2JsZTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjI0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjE1NjU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+V2hhdCBpcyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmU/PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmJ0MTIwNi0xNTY1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5Qcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcyBvZiB0aGUgVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk9sc29uPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+Nzc8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTUxNjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODA8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5DbGFzc2lmaWNhdGlvbiBvZiBjdWx0dXJlZCBtYW1tYWxpYW4gY2VsbHMgYnkgc2hhcGUgYW5hbHlzaXMgYW5kIHBhdHRlcm4gcmVjb2duaXRpb248L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjc3LjMuMTUxNjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+QmlvaW5mb3JtYXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+UGF1PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+OTc5PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkVCSW1hZ2XigJNhbiBSIHBhY2thZ2UgZm9yIGltYWdlIHByb2Nlc3Npbmcgd2l0aCBhcHBsaWNhdGlvbnMgdG8gY2VsbHVsYXIgcGhlbm90eXBlczwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDkzL2Jpb2luZm9ybWF0aWNzL2J0cTA0NjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIxIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5SYWduaTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIzPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjEzMjI8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDExPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TW9iaWxlIGdpYmJlcmVsbGluIGRpcmVjdGx5IHN0aW11bGF0ZXMgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHh5bGVtIGV4cGFuc2lvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTA1L3RwYy4xMTEuMDg0MDIwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjIiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5EcnlhZCBEaWdpdGFsIFJlcG9zaXRvcnk8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2Fua2FyPC9hdXRob3I+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDE0PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC41MDYxL2RyeWFkLmI4MzVrPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjMiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DdXJyZW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2lib3V0PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTg8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NDU4PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwODwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkZsb3dlcmluZyBhcyBhIGNvbmRpdGlvbiBmb3IgeHlsZW0gZXhwYW5zaW9uIGluIEFyYWJpZG9wc2lzIGh5cG9jb3R5bCBhbmQgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouY3ViLjIwMDguMDIuMDcwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjQiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5UaGUgTmV3IFBoeXRvbG9naXN0PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlNwaWNlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Njwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT41Nzc8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RXZvbHV0aW9uIG9mIGRldmVsb3BtZW50IG9mIHZhc2N1bGFyIGNhbWJpYSBhbmQgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTExL2ouMTQ2OS04MTM3LjIwMTAuMDMyMzYueDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjI1Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TWFjaGluZSBWaXNpb24gYW5kIEFwcGxpY2F0aW9uczwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5UaGVyaWF1bHQ8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yMzwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42NTk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+Q2VsbCBtb3JwaG9sb2d5IGNsYXNzaWZpY2F0aW9uIGFuZCBjbHV0dGVyIG1pdGlnYXRpb24gaW4gcGhhc2UtY29udHJhc3QgbWljcm9zY29weSBpbWFnZXMgdXNpbmcgbWFjaGluZSBsZWFybmluZzwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDA3L3MwMDEzOC0wMTEtMDM0NS05PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DZWxsPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlV5dHRld2FhbDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE0OTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40Mzk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TWVjaGFuaWNhbCBzdHJlc3MgYWN0cyB2aWEga2F0YW5pbiB0byBhbXBsaWZ5IGRpZmZlcmVuY2VzIGluIGdyb3d0aCByYXRlIGJldHdlZW4gYWRqYWNlbnQgY2VsbHMgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLmNlbGwuMjAxMi4wMi4wNDg8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyNyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk5hdHVyZSBDZWxsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+WWluPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+ODYwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkEgc2NyZWVuIGZvciBtb3JwaG9sb2dpY2FsIGNvbXBsZXhpdHkgaWRlbnRpZmllcyByZWd1bGF0b3JzIG9mIHN3aXRjaC1saWtlIHRyYW5zaXRpb25zIGJldHdlZW4gZGlzY3JldGUgY2VsbCBzaGFwZXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAzOC9uY2IyNzY0PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgIDwvY2l0YXRpb25fbGlzdD4NCiAgICAgICAgICA8Y29tcG9uZW50X2xpc3Q+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5BYnN0cmFjdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDE8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNhYnN0cmFjdDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPmVMaWZlIGRpZ2VzdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDI8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNkaWdlc3Q8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgMS4gQ2VsbHVsYXIgbGV2ZWwgYW5hbHlzaXMgb2YgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGguPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEpIExpZ2h0IG1pY3Jvc2NvcHkgb2YgY3Jvc3Mgc2VjdGlvbnMgb2J0YWluZWQgZnJvbSBBcmFiaWRvcHNpcyBoeXBvY290eWxzIChvcmdhbiBwb3NpdGlvbiBpbGx1c3RyYXRlZCBmb3IgYSA5LWRheS1vbGQgc2VlZGxpbmcsIGxvd2VyIGxlZnQpIGF0IDkgZGFnICh1cHBlciBsZWZ0KSBhbmQgMzUgZGFnIChyaWdodCkuIFNpemUgYmFycyBhcmUgMTAwIM68bS4gQmx1ZSBHVVMgc3RhaW5pbmcgZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiBhbiBBUEw6OkdVUyByZXBvcnRlciBnZW5lIGluIHRoaXMgQ29sLTAgYmFja2dyb3VuZCBsaW5lIG1hcmtzIHBobG9lbSBidW5kbGVzLiAoQikgT3ZlcnZpZXcgb2YgdGhlIGRldmVsb3BtZW50YWwgc2VyaWVzICh0aW1lIHBvaW50cyBhbmQgZGlzdGluY3Qgc2FtcGxlcyBwZXIgZ2Vub3R5cGUpIGFuYWx5emVkIGluIHRoaXMgc3R1ZHkuIChDKSBFeGFtcGxlIG9mIGEgaGlnaC1yZXNvbHV0aW9uIGh5cG9jb3R5bCBzZWN0aW9uIGltYWdlIGFzc2VtYmxlZCBmcm9tIDExIMOXIDExIHRpbGVzLiAoRCkgVGhlIHNhbWUgaW1hZ2UgYWZ0ZXIgcHJlLXByb2Nlc3NpbmcgYW5kIGJpbmFyaXphdGlvbiwgYW5kIChFKSBzdWJzZXF1ZW50IHNlZ21lbnRhdGlvbiB1c2luZyBhIHdhdGVyc2hlZCBhbGdvcml0aG0uIChGKSBOdW1iZXIgb2YgbWlzLXNlZ21lbnRlZCBjZWxscyBhcyBkZXRlcm1pbmVkIGJ5IGNhcmVmdWwgdmlzdWFsIGluc3BlY3Rpb24gaW4gMTIgc2VjdGlvbnMsIHBsb3R0ZWQgYWdhaW5zdCB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIHBlciBzZWN0aW9uIChsb2cgc2NhbGUpLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDM8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWcxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDIuIFRoZSDigJhRdWFudGl0YXRpdmUgSGlzdG9sb2d54oCZIGFwcHJvYWNoLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBPdmVydmlldyBvZiB0aGUgY29tcHV0YXRpb25hbCBwaXBlbGluZSBmcm9tIGltYWdlIGFjcXVpc2l0aW9uIHRvIGFuYWx5c2lzLiAoQikg4oCYUGhlbm9wcmludHPigJkgZm9yIHRoZSBkaWZmZXJlbnQgZ2Vub3R5cGVzIGFuZCBkZXZlbG9wbWVudGFsIHN0YWdlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA0PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSAy4oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gZXhhbXBsZSBvZiBjbGFzc2lmaWVyIHNlbGVjdGlvbiB0aHJvdWdoIFYtZm9sZCBjcm9zcyB2YWxpZGF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBncmVlbiBhcnJvdyBwb2ludHMgb3V0IHRoZSBzZWxlY3RlZCBmZWF0dXJlIGNvbWJpbmF0aW9uIGFjY29yZGluZyB0byB0aGUgY3JpdGVyaWEgb2YgbWluaW11bSBudW1iZXIgb2YgZmVhdHVyZXMgd2l0aCB0aGUgaGlnaGVzdCBwZXJmb3JtYW5jZSBhbmQgdGhlIGxvd2VzdCB2YXJpYXRpb24gKHRoZSByYWRpdXNWIGZlYXR1cmUgd2FzIGV4Y2x1ZGVkIGR1ZSB0byBpdHMgcHV0YXRpdmUgdmFyaWF0aW9uIGluIHRpc3N1ZSBsb2NhdGlvbikuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnMnMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDMuIFByb2dyZXNzaW9uIG9mIHRpc3N1ZSBwcm9saWZlcmF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIG9mIHRoZSBwaGVub3ByaW50cyBzaG93biBpbiBGaWd1cmUgMkIsIHBlcmZvcm1lZCB3aXRoIG5vcm1hbGl6ZWQgdmFsdWVzIChTdXBwbGVtZW50YXJ5IGZpbGUgNCkuIFRoZSBpbmxheSBzY3JlZXBsb3QgZGlzcGxheXMgdGhlIHByb3BvcnRpb24gb2YgdG90YWwgdmFyaWF0aW9uIGV4cGxhaW5lZCBieSBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQuIChC4oCTRSkgQ29tcGFyYXRpdmUgcGxvdHMgb2YgcGFyYW1ldGVyIHByb2dyZXNzaW9uIGluIHRoZSB0d28gZ2Vub3R5cGVzLiBJbiAoRCksIHh5bGVtIHJlcHJlc2VudHMgY29tYmluZWQgdmVzc2VsLCBwYXJlbmNoeW1hLCBhbmQgZmliZXIgY2VsbHMsIHBobG9lbSByZXByZXNlbnRzIGNvbWJpbmVkIHBobG9lbSBwYXJlbmNoeW1hIGFuZCBidW5kbGUgY2VsbHMuIEVycm9yIGJhcnMgaW5kaWNhdGUgc3RhbmRhcmQgZXJyb3IuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNjwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzM8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgNC4gQmltb2RhbCBkaXN0cmlidXRpb24gb2YgaW5jbGluZSBhbmdsZSBhY2NvcmRpbmcgdG8gcG9zaXRpb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEgYW5kIEIpIFNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGNlbGwgaW5jbGluZSBhbmdsZSBpbGx1c3RyYXRlcyB0aGUgdmFzY3VsYXIgb3JnYW5pemF0aW9uIGluIExlciAoQikgYXMgY29tcGFyZWQgdG8gQ29sLTAgKEEpIGF0IGxhdGVyIHN0YWdlcyBvZiBkZXZlbG9wbWVudCwgZm9yIGV4YW1wbGUgMzAgZGFnLiBUaGUgc2l6ZSBvZiB0aGUgZGlzYyBpbmNyZWFzZXMgd2l0aCB0aGUgYXJlYSBvZiB0aGUgY2VsbC4gQmx1ZSBjb2xvciBpbmRpY2F0ZXMgcmFkaWFsIGNlbGwgb3JpZW50YXRpb24sIHJlZCBvcnRob3JhZGlhbC4gKEMgYW5kIEQpIFZpb2xpbiBwbG90cyBvZiBpbmNsaW5lIGFuZ2xlIGRpc3RyaWJ1dGlvbiwgaWxsdXN0cmF0aW5nIGluY3JlYXNpbmdseSBiaW1vZGFsIGRpc3RyaWJ1dGlvbiBjb2luY2lkZW50IHdpdGggcmVmaW5lZCB2YXNjdWxhciBvcmdhbml6YXRpb24gYW5kIGRpZmZlcmVudCBkeW5hbWljcyBvZiB0aGUgcHJvY2VzcyBpbiB0aGUgdHdvIGdlbm90eXBlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA3PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnNDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA04oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gaWxsdXN0cmF0aW9uIG9mIHRoZSBpbmNsaW5lIGFuZ2xlLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBpbmNsaW5lIGlzIHRoZSBhbmdsZSBiZXR3ZWVuIHRoZSBzZWN0aW9uIHJhZGl1cyB0aHJvdWdoIHRoZSBjZW50ZXIgb2YgYW4gZWxsaXBzZSBmaXQgdG8gYSBjZWxsIGFuZCB0aGUgbWFqb3IgYXhpcyBvZiB0aGF0IGVsbGlwc2UgZXh0ZW5kZWQgdG93YXJkcyB0aGUgeCBheGlzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDg8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9maWd1cmVzI2ZpZzRzMTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA1LiBEaXN0aW5jdCBsb2NhbCBvcmdhbml6YXRpb24gb2YgaW5jbGluZSBhbmdsZSBkdXJpbmcgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGggcHJvZ3Jlc3Npb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEHigJNKKSBEZW5zaXR5IHBsb3RzIG9mIGNlbGwgaW5jbGluZSBhbmdsZSB2cyByYWRpYWwgcG9zaXRpb24gZm9yIHRoZSB0d28gZ2Vub3R5cGVzIGF0IHRoZSBpbmRpY2F0ZWQgZGV2ZWxvcG1lbnRhbCBzdGFnZXMsIHJlcHJlc2VudGluZyBhbGwgY2VsbHMgYWNyb3NzIGFsbCBzZWN0aW9ucyBmb3IgYSBnaXZlbiB0aW1lIHBvaW50LiBUaGUgcmVkIGxpbmVzIHJlcHJlc2VudCB0aGUgZml0IG9mIHRoZXNlIGNsb3VkIGRpc3RyaWJ1dGlvbnMgd2l0aCBsb2NhbGx5IHdlaWdodGVkIGxpbmVhciByZWdyZXNzaW9uIChpLmUuLCBsb3dlc3MpLCByZXZlYWxpbmcgdGhlIGVzc2VudGlhbCBkYXRhIHRyZW5kcy4gQWxsIHNlY3Rpb25zIHdlcmUgbm9ybWFsaXplZCBmcm9tIDAuMCAodGhlIG1hbnVhbGx5IGRlZmluZWQgY2VudGVyKSB0byAxLjAgKHRoZSBhdmVyYWdlIHJhZGl1cyBpbiBhIHNldCBvZiBzZWN0aW9ucyBhcyBkZXRlcm1pbmVkIGJ5IHRoZSBhdmVyYWdlIGRpc3RhbmNlIG9mIHRoZSBvdXRlcm1vc3QgY2VsbHMgZnJvbSB0aGUgY2VudGVyIGZvciBpbmRpdmlkdWFsIHNlY3Rpb25zKS4gQm94IHBsb3RzIGluZGljYXRlIHRoZSBxdWFydGlsZXMgb2YgdGhlIHJhZGlhbiBkaXN0cmlidXRpb24gZm9yIGVhY2ggY2VsbC10eXBlIGNsYXNzIGFuZCBhcmUgcGxhY2VkIGF0IHRoZSBhdmVyYWdlIHBvc2l0aW9uIG9mIHRoZSBjZWxsIHR5cGUgd2l0aCByZXNwZWN0IHRvIHRoZSB5IGF4aXMuIE91dGxpZXJzIGFyZSBzaG93biBhcyBjaXJjbGVzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDk8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWc1PC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDXigJRmaWd1cmUgc3VwcGxlbWVudCAxLiBBbmFseXNpcyBvZiBjZWxsIG51bWJlciBpbiBkZWZpbmVkIHh5bGVtIHJlZ2lvbnMgb2YgZGlmZmVyZW50IHNpemUuPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+Q2VsbCBudW1iZXIgaW4gYSBjaXJjbGUgb2YgMjAw4oCTNTAwIHBpeGVscyBhcm91bmQgdGhlIHNlY3Rpb24gY2VudGVycyBmb3IgQ29sLTAuIENlbGwgY291bnQgaW4gYSBjb25zdGFudCBhcmVhIG9mIHh5bGVtIG92ZXIgdGltZSBhY3Jvc3MgYWxsIGF2ZXJhZ2VkIGFjcm9zcyBhbGwgc2VjdGlvbnMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnNXMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDYuIE1hcHBpbmcgb2YgcGhsb2VtIHBvbGUgcGF0dGVybmluZy48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgRXhhbXBsZSBvZiBHYXVzc2lhbiBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSBvZiB0aGUgbG9jYXRpb24gb2YgcHJlZGljdGVkIHBobG9lbSBidW5kbGVzIGNlbGxzIGluIGEgMzAgZGFnIENvbC0wIHNlY3Rpb24uIEhpZ2ggZGVuc2l0eSByZXByZXNlbnRzIHBobG9lbSBwb2xlcy4gKEIpIEV4YW1wbGUgb2YgYW4gYW5hbHlzaXMgb2YgZW1lcmdpbmcgcGhsb2VtIHBvbGUgcG9zaXRpb24gaW4gYSAzMCBkYWcgQ29sLTAgc2VjdGlvbi4gVGhlIHBsb3QgcmVwcmVzZW50cyBhIHBpeGVsIGludGVuc2l0eSBtYXAgYWZ0ZXIgbm9pc2UgcmVkdWN0aW9uIGFsb25nIGEgY2lyY3VsYXIgcmVnaW9uIG9mIGludGVyZXN0IGFjcm9zcyB0aGUgZW1lcmdpbmcgcGhsb2VtIHBvbGVzLiBJbnRlbnNpdHkgcGVha3MgYXJlIGR1ZSB0byBHVVMgc3RhaW5pbmcgY29uZmVycmVkIHRvIHBobG9lbSBidW5kbGVzIGJ5IGFuIEFQTDo6R1VTIHJlcG9ydGVyIGNvbnN0cnVjdC4gKEMpIFByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gb2YgdGhlIGRhdGEgc2hvd24gaW4gKEIpIG9idGFpbmVkIGZyb20gYW4gYXV0b21hdGVkIEJheWVzaWFuIG1vZGVsLiBUaGUgZG9taW5hbnQgc2luZ2xlIHBlYWsgaW5kaWNhdGVzIGEgY29uc3RhbnQgYXJjIGRpc3RhbmNlIG9mIGNhLiA2MiBwaXhlbCBiZXR3ZWVuIHRoZSBwaGxvZW0gcG9sZXMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzY8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgMS48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgQW4gZXhwbGFuYXRpb24gb2YgdGhlIGV4dHJhY3RlZCBwYXJhbWV0ZXJzIHRoYXQgZGVzY3JpYmUgdGhlIGNlbGx1bGFyIGZlYXR1cmVzLiAoQikgU3VtbWFyeSBpbmZvcm1hdGlvbiBvZiB0aGUgaGFuZC1sYWJlbGVkIHRyYWluaW5nIHNldCBmb3Igc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nLiAoQykgRGVmaW5pdGlvbiBvZiB0aGUgY2xhc3NpZmllcnMgc2VsZWN0ZWQgZm9yIGFuYWx5c2lzLiAoRCkgU3VtbWFyeSBvZiB0aGUgY2xhc3NpZmllciBwYXJhbWV0ZXJzIGZvciBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcuIChFKSBPdmVydmlldyBvZiB0aGUgY2VsbCB0eXBlIGNsYXNzZXMgcmVjb2duaXplZCBieSB0aGUgc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoIGFuZCB0aGVpciBhc3NpZ25tZW50IGNvZGVzIHVzZWQgaW4gRGF0YSBGaWxlcyAzIGFuZCA0Ljwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEyPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDEtZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAyLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIENvbC0wIHNlY3Rpb25zLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEzPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDItZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAzLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIExlciBzZWN0aW9ucy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0QzLWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgNC48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT5UaGUgbm9ybWFsaXplZCB2YWx1ZXMgb2YgdGhlIHBoZW5vcHJpbnRzIChGaWd1cmUgMkIpIHVzZWQgZm9yIFBDQS48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0Q0LWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5EZWNpc2lvbiBsZXR0ZXI8L3RpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9InRleHQvcGxhaW4iIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDE2PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjU0ExPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+QXV0aG9yIHJlc3BvbnNlPC90aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJ0ZXh0L3BsYWluIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNzwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I1NBMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICA8L2NvbXBvbmVudF9saXN0Pg0KICAgICAgICA8L2pvdXJuYWxfYXJ0aWNsZT4NCiAgICAgIDwvam91cm5hbD4NCiAgICA8L2Nyb3NzcmVmPg0KICA8L2RvaV9yZWNvcmQ+DQo8L2RvaV9yZWNvcmRzPg== + http_version: + recorded_at: Fri, 07 Dec 2018 18:37:48 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/sets_state_to_findable.yml b/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/sets_state_to_findable.yml new file mode 100644 index 000000000..cc5a91cf4 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/sets_state_to_findable.yml @@ -0,0 +1,82 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7554 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 18:37:49 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - dc53754b-5309-4e73-a1d0-35bfde3b29dc + Etag: + - W/"381993dc8a6b0f20960c96a3639c0284" + X-Runtime: + - '0.076738' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7554","type":"prefixes","attributes":{"registration-agency":"Crossref","created":null,"updated":"2016-09-21T21:07:27Z"},"relationships":{"clients":{"data":[]},"providers":{"data":[]}}},"included":[]}' + http_version: + recorded_at: Fri, 07 Dec 2018 18:37:49 GMT +- request: + method: get + uri: http://www.crossref.org/openurl/?format=unixref&id=doi:10.7554/elife.01567&noredirect=true&pid=tech@datacite.org + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/xml + response: + status: + code: 200 + message: OK + headers: + Server: + - Apache-Coyote/1.1 + Crossref-Deployment-Name: + - qs4-1 + Content-Type: + - text/xml;charset=UTF-8 + Content-Language: + - en-US + Date: + - Fri, 07 Dec 2018 18:37:49 GMT + Connection: + - close + body: + encoding: ASCII-8BIT + string: !binary |- + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGRvaV9yZWNvcmRzPg0KICA8ZG9pX3JlY29yZCBvd25lcj0iMTAuNzU1NCIgdGltZXN0YW1wPSIyMDE4LTA4LTIzIDA5OjQxOjQ5Ij4NCiAgICA8Y3Jvc3NyZWY+DQogICAgICA8am91cm5hbD4NCiAgICAgICAgPGpvdXJuYWxfbWV0YWRhdGEgbGFuZ3VhZ2U9ImVuIj4NCiAgICAgICAgICA8ZnVsbF90aXRsZT5lTGlmZTwvZnVsbF90aXRsZT4NCiAgICAgICAgICA8aXNzbiBtZWRpYV90eXBlPSJlbGVjdHJvbmljIj4yMDUwLTA4NFg8L2lzc24+DQogICAgICAgIDwvam91cm5hbF9tZXRhZGF0YT4NCiAgICAgICAgPGpvdXJuYWxfaXNzdWU+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8am91cm5hbF92b2x1bWU+DQogICAgICAgICAgICA8dm9sdW1lPjM8L3ZvbHVtZT4NCiAgICAgICAgICA8L2pvdXJuYWxfdm9sdW1lPg0KICAgICAgICA8L2pvdXJuYWxfaXNzdWU+DQogICAgICAgIDxqb3VybmFsX2FydGljbGUgcHVibGljYXRpb25fdHlwZT0iZnVsbF90ZXh0IiByZWZlcmVuY2VfZGlzdHJpYnV0aW9uX29wdHM9ImFueSI+DQogICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgIDx0aXRsZT5BdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvdGl0bGU+DQogICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgPGNvbnRyaWJ1dG9ycz4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJmaXJzdCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPk1hcnRpYWw8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlNhbmthcjwvc3VybmFtZT4NCiAgICAgICAgICAgICAgPGFmZmlsaWF0aW9uPkRlcGFydG1lbnQgb2YgUGxhbnQgTW9sZWN1bGFyIEJpb2xvZ3ksIFVuaXZlcnNpdHkgb2YgTGF1c2FubmUsIExhdXNhbm5lLCBTd2l0emVybGFuZDwvYWZmaWxpYXRpb24+DQogICAgICAgICAgICA8L3BlcnNvbl9uYW1lPg0KICAgICAgICAgICAgPHBlcnNvbl9uYW1lIGNvbnRyaWJ1dG9yX3JvbGU9ImF1dGhvciIgc2VxdWVuY2U9ImFkZGl0aW9uYWwiPg0KICAgICAgICAgICAgICA8Z2l2ZW5fbmFtZT5LYWlzYTwvZ2l2ZW5fbmFtZT4NCiAgICAgICAgICAgICAgPHN1cm5hbWU+TmllbWluZW48L3N1cm5hbWU+DQogICAgICAgICAgICAgIDxhZmZpbGlhdGlvbj5EZXBhcnRtZW50IG9mIFBsYW50IE1vbGVjdWxhciBCaW9sb2d5LCBVbml2ZXJzaXR5IG9mIExhdXNhbm5lLCBMYXVzYW5uZSwgU3dpdHplcmxhbmQ8L2FmZmlsaWF0aW9uPg0KICAgICAgICAgICAgPC9wZXJzb25fbmFtZT4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJhZGRpdGlvbmFsIj4NCiAgICAgICAgICAgICAgPGdpdmVuX25hbWU+TGF1cmE8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlJhZ25pPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPklvYW5uaXM8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlhlbmFyaW9zPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+Vml0YWwtSVQsIFN3aXNzIEluc3RpdHV0ZSBvZiBCaW9pbmZvcm1hdGljcywgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPkNocmlzdGlhbiBTPC9naXZlbl9uYW1lPg0KICAgICAgICAgICAgICA8c3VybmFtZT5IYXJkdGtlPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgPC9jb250cmlidXRvcnM+DQogICAgICAgICAgPGFic3RyYWN0Pg0KICAgICAgICAgICAgPHA+QW1vbmcgdmFyaW91cyBhZHZhbnRhZ2VzLCB0aGVpciBzbWFsbCBzaXplIG1ha2VzIG1vZGVsIG9yZ2FuaXNtcyBwcmVmZXJyZWQgc3ViamVjdHMgb2YgaW52ZXN0aWdhdGlvbi4gWWV0LCBldmVuIGluIG1vZGVsIHN5c3RlbXMgZGV0YWlsZWQgYW5hbHlzaXMgb2YgbnVtZXJvdXMgZGV2ZWxvcG1lbnRhbCBwcm9jZXNzZXMgYXQgY2VsbHVsYXIgbGV2ZWwgaXMgc2V2ZXJlbHkgaGFtcGVyZWQgYnkgdGhlaXIgc2NhbGUuIEZvciBpbnN0YW5jZSwgc2Vjb25kYXJ5IGdyb3d0aCBvZiBBcmFiaWRvcHNpcyBoeXBvY290eWxzIGNyZWF0ZXMgYSByYWRpYWwgcGF0dGVybiBvZiBoaWdobHkgc3BlY2lhbGl6ZWQgdGlzc3VlcyB0aGF0IGNvbXByaXNlcyBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHN0YXJ0aW5nIGZyb20gYSBmZXcgZG96ZW4uIFRoaXMgZHluYW1pYyBwcm9jZXNzIGlzIGRpZmZpY3VsdCB0byBmb2xsb3cgYmVjYXVzZSBvZiBpdHMgc2NhbGUgYW5kIGJlY2F1c2UgaXQgY2FuIG9ubHkgYmUgaW52ZXN0aWdhdGVkIGludmFzaXZlbHksIHByZWNsdWRpbmcgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBjZWxsIHByb2xpZmVyYXRpb24sIGRpZmZlcmVudGlhdGlvbiwgYW5kIHBhdHRlcm5pbmcgZXZlbnRzIGludm9sdmVkLiBUbyBvdmVyY29tZSBzdWNoIGxpbWl0YXRpb24sIHdlIGVzdGFibGlzaGVkIGFuIGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d5IGFwcHJvYWNoLiBXZSBhY3F1aXJlZCBoeXBvY290eWwgY3Jvc3Mtc2VjdGlvbnMgZnJvbSB0aWxlZCBoaWdoLXJlc29sdXRpb24gaW1hZ2VzIGFuZCBleHRyYWN0ZWQgdGhlaXIgaW5mb3JtYXRpb24gY29udGVudCB1c2luZyBjdXN0b20gaGlnaC10aHJvdWdocHV0IGltYWdlIHByb2Nlc3NpbmcgYW5kIHNlZ21lbnRhdGlvbi4gQ291cGxlZCB3aXRoIGF1dG9tYXRlZCBjZWxsIHR5cGUgcmVjb2duaXRpb24gdGhyb3VnaCBtYWNoaW5lIGxlYXJuaW5nLCB3ZSBjb3VsZCBlc3RhYmxpc2ggYSBjZWxsdWxhciByZXNvbHV0aW9uIGF0bGFzIHRoYXQgcmV2ZWFscyB2YXNjdWxhciBtb3JwaG9keW5hbWljcyBkdXJpbmcgc2Vjb25kYXJ5IGdyb3d0aCwgZm9yIGV4YW1wbGUgZXF1aWRpc3RhbnQgcGhsb2VtIHBvbGUgZm9ybWF0aW9uLjwvcD4NCiAgICAgICAgICA8L2Fic3RyYWN0Pg0KICAgICAgICAgIDxhYnN0cmFjdCBhYnN0cmFjdC10eXBlPSJleGVjdXRpdmUtc3VtbWFyeSI+DQogICAgICAgICAgICA8cD5PdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgbGl2aW5nIHdvcmxkIGhhcyBiZWVuIGFkdmFuY2VkIGdyZWF0bHkgYnkgc3R1ZGllcyBvZiDigJhtb2RlbCBvcmdhbmlzbXPigJksIHN1Y2ggYXMgbWljZSwgemVicmFmaXNoLCBhbmQgZnJ1aXQgZmxpZXMuIFN0dWR5aW5nIHRoZXNlIGNyZWF0dXJlcyBoYXMgYmVlbiBjcnVjaWFsIHRvIHVuY292ZXJpbmcgdGhlIGdlbmVzIHRoYXQgY29udHJvbCBob3cgb3VyIGJvZGllcyBkZXZlbG9wIGFuZCBncm93LCBhbmQgYWxzbyB0byBkaXNjb3ZlciB0aGUgZ2VuZXRpYyBiYXNpcyBvZiBkaXNlYXNlcyBzdWNoIGFzIGNhbmNlci48L3A+DQogICAgICAgICAgICA8cD5UaGFsZSBjcmVzc+KAlG9yIEFyYWJpZG9wc2lzIHRoYWxpYW5hIHRvIGdpdmUgaXRzIGZvcm1hbCBuYW1l4oCUaXMgdGhlIG1vZGVsIG9yZ2FuaXNtIG9mIGNob2ljZSBmb3IgbWFueSBwbGFudCBiaW9sb2dpc3RzLiBUaGlzIHRpbnkgd2VlZCBoYXMgYmVlbiB3aWRlbHkgc3R1ZGllZCBiZWNhdXNlIGl0IGNhbiBjb21wbGV0ZSBpdHMgbGlmZWN5Y2xlLCBmcm9tIHNlZWQgdG8gc2VlZCwgaW4gYWJvdXQgNiB3ZWVrcywgYW5kIGJlY2F1c2UgaXRzIHJlbGF0aXZlbHkgc21hbGwgZ2Vub21lIHNpbXBsaWZpZXMgdGhlIHNlYXJjaCBmb3IgZ2VuZXMgdGhhdCBjb250cm9sIHNwZWNpZmljIHRyYWl0cy4gSG93ZXZlciwgYXMgd2l0aCBvdGhlciBtdWNoLXN0dWRpZWQgbW9kZWwgc3lzdGVtcywgdW5kZXJzdGFuZGluZyB0aGUgY2hhbmdlcyB0aGF0IHVuZGVycGluIHRoZSBkZXZlbG9wbWVudCBvZiBzb21lIG9mIHRoZSBtb3JlIGNvbXBsZXggdGlzc3VlcyBpbiBBcmFiaWRvcHNpcyBoYXMgYmVlbiBzZXZlcmVseSBoYW1wZXJlZCBieSB0aGUgc2hlYXIgbnVtYmVyIG9mIGNlbGxzIGludm9sdmVkLjwvcD4NCiAgICAgICAgICAgIDxwPkFmdGVyIGl0IGhhcyBlbWVyZ2VkIGZyb20gdGhlIHNlZWQsIHRoZSBwbGFudOKAmXMgZmlyc3Qgc3RlbSB3aWxsIGRldmVsb3AgZnJvbSBhIGZldyBkb3plbiBjZWxscyBpbiB3aWR0aCB0byBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHdpdGggaGlnaGx5IHNwZWNpYWxpemVkIHRpc3N1ZXMgYXJyYW5nZWQgaW4gYSBjb21wbGV4IHBhdHRlcm4gb2YgY29uY2VudHJpYyBjaXJjbGVzLiBBbHRob3VnaCB0aGlzIHN0ZW0gdGhpY2tlbmluZyBwcm9jZXNzIHJlcHJlc2VudHMgYSBtYWpvciBkZXZlbG9wbWVudGFsIGNoYW5nZSBpbiBtYW55IHBsYW50c+KAlGZyb20gQXJhYmlkb3BzaXMgdG8gb2FrIHRyZWVz4oCUaXQgaGFzIGJlZW4gdW5kZXItcmVzZWFyY2hlZC4gVGhpcyBpcyBwYXJ0bHkgYmVjYXVzZSBpdCBpbnZvbHZlcyBzbyBtYW55IGRpZmZlcmVudCBjZWxscywgYW5kIGFsc28gYmVjYXVzZSBpdCBjYW4gb25seSBiZSBvYnNlcnZlZCBpbiB0aGluIHNlY3Rpb25zIGN1dCBvdXQgb2YgdGhlIHBsYW504oCZcyBzdGVtLjwvcD4NCiAgICAgICAgICAgIDxwPk5vdyBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gaGF2ZSBkZXZlbG9wZWQgYSBub3ZlbCBhcHByb2FjaCwgdGVybWVkIOKAmGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d54oCZLCB0byBvdmVyY29tZSB0aGVzZSBwcm9ibGVtcy4gVGhpcyBzdHJhdGVneSBpbnZvbHZlcyDigJh0ZWFjaGluZ+KAmSBhIGNvbXB1dGVyIHRvIGF1dG9tYXRpY2FsbHkgcmVjb2duaXplIGRpZmZlcmVudCBwbGFudCBjZWxscyBhbmQgdG8gbWVhc3VyZSB0aGVpciBpbXBvcnRhbnQgZmVhdHVyZXMgaW4gaGlnaC1yZXNvbHV0aW9uIGltYWdlcyBvZiB0aXNzdWUgc2VjdGlvbnMuIFRoZSByZXN1bHRpbmcg4oCYbWFw4oCZIG9mIHRoZSBkZXZlbG9waW5nIHN0ZW3igJR3aGljaCByZXF1aXJlZCBvdmVyIDgwMCBociBvZiBjb21wdXRpbmcgdGltZSB0byBjb21wbGV0ZeKAlHJldmVhbHMgdGhlIGNoYW5nZXMgdG8gY2VsbHMgYW5kIHRpc3N1ZXMgYXMgdGhleSBkZXZlbG9wIHRoYXQgYWxsb3cgdGhlIHRyYW5zcG9ydCBvZiB3YXRlciwgc3VnYXJzIGFuZCBudXRyaWVudHMgYmV0d2VlbiB0aGUgYWJvdmUtIGFuZCBiZWxvdy1ncm91bmQgb3JnYW5zLiBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gc3VnZ2VzdCB0aGF0IHRoZWlyIG5vdmVsIGFwcHJvYWNoIGNvdWxkLCBpbiB0aGUgZnV0dXJlLCBhbHNvIGJlIGFwcGxpZWQgdG8gc3R1ZHkgdGhlIGRldmVsb3BtZW50IG9mIG90aGVyIHRpc3N1ZXMgYW5kIG9yZ2FuaXNtcywgaW5jbHVkaW5nIGFuaW1hbHMuPC9wPg0KICAgICAgICAgIDwvYWJzdHJhY3Q+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8cHVibGlzaGVyX2l0ZW0+DQogICAgICAgICAgICA8aXRlbV9udW1iZXIgaXRlbV9udW1iZXJfdHlwZT0iYXJ0aWNsZV9udW1iZXIiPmUwMTU2NzwvaXRlbV9udW1iZXI+DQogICAgICAgICAgICA8aWRlbnRpZmllciBpZF90eXBlPSJkb2kiPjEwLjc1NTQvZUxpZmUuMDE1Njc8L2lkZW50aWZpZXI+DQogICAgICAgICAgPC9wdWJsaXNoZXJfaXRlbT4NCiAgICAgICAgICA8cHJvZ3JhbSBuYW1lPSJmdW5kcmVmIj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5FTUJPIGxvbmd0ZXJtIHBvc3QtZG9jdG9yYWwgZmVsbG93c2hpcHM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5NYXJpZSBIZWltLVZvZWd0bGluPC9hc3NlcnRpb24+DQogICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+DQogICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX2lkZW50aWZpZXIiIHByb3ZpZGVyPSJjcm9zc3JlZiI+NTAxMTAwMDA2MzkwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxwcm9ncmFtIG5hbWU9IkFjY2Vzc0luZGljYXRvcnMiPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0iYW0iPmh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzMuMC88L2xpY2Vuc2VfcmVmPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InRkbSI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxjcm9zc21hcms+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3ZlcnNpb24+MTwvY3Jvc3NtYXJrX3ZlcnNpb24+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3BvbGljeT5lTGlmZXNjaWVuY2VzPC9jcm9zc21hcmtfcG9saWN5Pg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgICA8Y3Jvc3NtYXJrX2RvbWFpbj4NCiAgICAgICAgICAgICAgICA8ZG9tYWluPnd3dy5lbGlmZXNjaWVuY2VzLm9yZzwvZG9tYWluPg0KICAgICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW4+DQogICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5fZXhjbHVzaXZlPmZhbHNlPC9jcm9zc21hcmtfZG9tYWluX2V4Y2x1c2l2ZT4NCiAgICAgICAgICAgIDxjdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icmVjZWl2ZWQiIGxhYmVsPSJSZWNlaXZlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIwIj4yMDEzLTA5LTIwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iYWNjZXB0ZWQiIGxhYmVsPSJBY2NlcHRlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIxIj4yMDEzLTEyLTI0PC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icHVibGlzaGVkIiBsYWJlbD0iUHVibGlzaGVkIiBncm91cF9uYW1lPSJwdWJsaWNhdGlvbl9oaXN0b3J5IiBncm91cF9sYWJlbD0iUHVibGljYXRpb24gSGlzdG9yeSIgb3JkZXI9IjIiPjIwMTQtMDItMTE8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iZnVuZHJlZiI+DQogICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgRU1CTw0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIj5odHRwOi8vZHguZG9pLm9yZy8xMC4xMzAzOS81MDExMDAwMDMwNDM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX25hbWUiPg0KICAgICAgICAgICAgICAgICAgICBTd2lzcyBOYXRpb25hbCBTY2llbmNlIEZvdW5kYXRpb24NCiAgICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfaWRlbnRpZmllciI+aHR0cDovL2R4LmRvaS5vcmcvMTAuMTMwMzkvNTAxMTAwMDAxNzExPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIiBwcm92aWRlcj0iY3Jvc3NyZWYiPmh0dHA6Ly9keC5kb2kub3JnLzEwLjEzMDM5LzUwMTEwMDAwNjM5MDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iQWNjZXNzSW5kaWNhdG9ycyI+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89ImFtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0idGRtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgICAgPC9jdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgPC9jcm9zc21hcms+DQogICAgICAgICAgPHByb2dyYW0+DQogICAgICAgICAgICA8cmVsYXRlZF9pdGVtPg0KICAgICAgICAgICAgICA8ZGVzY3JpcHRpb24+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvZGVzY3JpcHRpb24+DQogICAgICAgICAgICAgIDxpbnRlcl93b3JrX3JlbGF0aW9uIGlkZW50aWZpZXItdHlwZT0iZG9pIiByZWxhdGlvbnNoaXAtdHlwZT0iaXNTdXBwbGVtZW50ZWRCeSI+MTAuNTA2MS9kcnlhZC5iODM1azwvaW50ZXJfd29ya19yZWxhdGlvbj4NCiAgICAgICAgICAgIDwvcmVsYXRlZF9pdGVtPg0KICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICA8YXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgICA8YXJjaGl2ZSBuYW1lPSJDTE9DS1NTIiAvPg0KICAgICAgICAgIDwvYXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3PC9kb2k+DQogICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NzwvcmVzb3VyY2U+DQogICAgICAgICAgICA8Y29sbGVjdGlvbiBwcm9wZXJ0eT0idGV4dC1taW5pbmciPg0KICAgICAgICAgICAgICA8aXRlbT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2UgbWltZV90eXBlPSJhcHBsaWNhdGlvbi9wZGYiPmh0dHBzOi8vY2RuLmVsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2VsaWZlLTAxNTY3LXYxLnBkZjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvaXRlbT4NCiAgICAgICAgICAgICAgPGl0ZW0+DQogICAgICAgICAgICAgICAgPHJlc291cmNlIG1pbWVfdHlwZT0iYXBwbGljYXRpb24veG1sIj5odHRwczovL2Nkbi5lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9lbGlmZS0wMTU2Ny12MS54bWw8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2l0ZW0+DQogICAgICAgICAgICA8L2NvbGxlY3Rpb24+DQogICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICA8Y2l0YXRpb25fbGlzdD4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmU8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Qm9ua2U8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT40MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTgxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkFQTCByZWd1bGF0ZXMgdmFzY3VsYXIgdGlzc3VlIGlkZW50aXR5IGluIEFyYWJpZG9wc2lzPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmF0dXJlMDIxMDA8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+R2VuZXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+QnJlbm5lcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Mjwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40MTM8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA5PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+SW4gdGhlIGJlZ2lubmluZyB3YXMgdGhlIHdvcm08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTUzNC9nZW5ldGljcy4xMDkuMTA0OTc2PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPlBoeXNpb2xvZ2lhIFBsYW50YXJ1bTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5DaGFmZmV5PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjU5NDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TZWNvbmRhcnkgeHlsZW0gZGV2ZWxvcG1lbnQgaW4gQXJhYmlkb3BzaXM6IGEgbW9kZWwgZm9yIHdvb2QgZm9ybWF0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzQvai4xMzk5LTMwNTQuMjAwMi4xMTQwNDEzLng8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TmV1cmFsIGNvbXB1dGF0aW9uPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkNoYW5nPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjExOTwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDE8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5UcmFpbmluZyBudS1zdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyczogdGhlb3J5IGFuZCBhbGdvcml0aG1zPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExNjIvMDg5OTc2NjAxNzUwMzk5MzM1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk1hY2hpbmUgTGVhcm5pbmc8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Q29ydGVzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjczPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5NTwvY1llYXI+DQogICAgICAgICAgICAgIDxkb2kgcHJvdmlkZXI9ImNyb3NzcmVmIj4xMC4xMDA3L0JGMDA5OTQwMTg8L2RvaT4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+U3VwcG9ydC12ZWN0b3IgTmV0d29ya3M8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNiI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkRldmVsb3BtZW50PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkRvbGFuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE5PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjcxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5MzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNlbGx1bGFyIG9yZ2FuaXNhdGlvbiBvZiB0aGUgQXJhYmlkb3BzaXMgdGhhbGlhbmEgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI3Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+U2VtaW5hcnMgaW4gQ2VsbCAmYW1wOyBEZXZlbG9wbWVudGFsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RWxvPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTA5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TdGVtIGNlbGwgZnVuY3Rpb24gZHVyaW5nIHBsYW50IHZhc2N1bGFyIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMTYvai5zZW1jZGIuMjAwOS4wOS4wMDk8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI4Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+RGV2ZWxvcG1lbnQ8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RXRjaGVsbHM8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4xNDA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjIyNDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTM8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5XT1g0IGFuZCBXT1gxNCBhY3QgZG93bnN0cmVhbSBvZiB0aGUgUFhZIHJlY2VwdG9yIGtpbmFzZSB0byByZWd1bGF0ZSBwbGFudCB2YXNjdWxhciBwcm9saWZlcmF0aW9uIGluZGVwZW5kZW50bHkgb2YgYW55IHJvbGUgaW4gdmFzY3VsYXIgb3JnYW5pc2F0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEyNDIvZGV2LjA5MTMxNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QTE9TIEdlbmV0aWNzPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkV0Y2hlbGxzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+ODwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT5lMTAwMjk5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudCB2YXNjdWxhciBjZWxsIGRpdmlzaW9uIGlzIG1haW50YWluZWQgYnkgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBQWFkgYW5kIGV0aHlsZW5lIHNpZ25hbGxpbmc8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTM3MS9qb3VybmFsLnBnZW4uMTAwMjk5NzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TW9sZWN1bGFyIFN5c3RlbXMgQmlvbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5GdWNoczwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MzcwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNsdXN0ZXJpbmcgcGhlbm90eXBlIHBvcHVsYXRpb25zIGJ5IGdlbm9tZS13aWRlIFJOQWkgYW5kIG11bHRpcGFyYW1ldHJpYyBpbWFnaW5nPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbXNiLjIwMTAuMjU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxMSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkJpbyBTeXN0ZW1zPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkdyYW5xdmlzdDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjExMDwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42MDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5CYVNBUi1BIHRvb2wgaW4gUiBmb3IgZnJlcXVlbmN5IGRldGVjdGlvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouYmlvc3lzdGVtcy4yMDEyLjA3LjAwNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+Q3VycmVudCBPcGluaW9uIGluIFBsYW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+R3Jvb3ZlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjk8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NTU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGV2ZWxvcG1lbnRhbCBtZWNoYW5pc21zIHJlZ3VsYXRpbmcgc2Vjb25kYXJ5IGdyb3d0aCBpbiB3b29keSBwbGFudHM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLnBiaS4yMDA1LjExLjAxMzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEzIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIyPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MTg8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+VERJRiBwZXB0aWRlIHNpZ25hbGluZyByZWd1bGF0ZXMgdmFzY3VsYXIgc3RlbSBjZWxsIHByb2xpZmVyYXRpb24gdmlhIHRoZSBXT1g0IGhvbWVvYm94IGdlbmUgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNS90cGMuMTEwLjA3NjA4MzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjE0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMgb2YgdGhlIFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjEwNTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT4xNTIwODwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDg8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5Ob24tY2VsbC1hdXRvbm9tb3VzIGNvbnRyb2wgb2YgdmFzY3VsYXIgc3RlbSBjZWxsIGZhdGUgYnkgYSBDTEUgcGVwdGlkZS9yZWNlcHRvciBzeXN0ZW08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjA4MDg0NDQxMDU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkNlbGw8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+TWV5ZXJvd2l0ejwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjU2PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BcmFiaWRvcHNpcywgYSB1c2VmdWwgd2VlZDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2LzAwOTItODY3NCg4OSk5MDkwMC04PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5TY2llbmNlPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk1leWVyb3dpdHo8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yOTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTQ4MjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudHMgY29tcGFyZWQgdG8gYW5pbWFsczogdGhlIGJyb2FkZXN0IGNvbXBhcmF0aXZlIHN0dWR5IG9mIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExMjYvc2NpZW5jZS4xMDY2NjA5PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTciPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QbGFudCBQaHlzaW9sPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk5pZW1pbmVuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM1PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjY1MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDQ8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BIHdlZWQgZm9yIHdvb2Q/IEFyYWJpZG9wc2lzIGFzIGEgZ2VuZXRpYyBtb2RlbCBmb3IgeHlsZW0gZGV2ZWxvcG1lbnQ8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNC9wcC4xMDQuMDQwMjEyPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTgiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmUgQmlvdGVjaG5vbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5Ob2JsZTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjI0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjE1NjU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+V2hhdCBpcyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmU/PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmJ0MTIwNi0xNTY1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5Qcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcyBvZiB0aGUgVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk9sc29uPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+Nzc8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTUxNjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODA8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5DbGFzc2lmaWNhdGlvbiBvZiBjdWx0dXJlZCBtYW1tYWxpYW4gY2VsbHMgYnkgc2hhcGUgYW5hbHlzaXMgYW5kIHBhdHRlcm4gcmVjb2duaXRpb248L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjc3LjMuMTUxNjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+QmlvaW5mb3JtYXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+UGF1PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+OTc5PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkVCSW1hZ2XigJNhbiBSIHBhY2thZ2UgZm9yIGltYWdlIHByb2Nlc3Npbmcgd2l0aCBhcHBsaWNhdGlvbnMgdG8gY2VsbHVsYXIgcGhlbm90eXBlczwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDkzL2Jpb2luZm9ybWF0aWNzL2J0cTA0NjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIxIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5SYWduaTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIzPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjEzMjI8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDExPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TW9iaWxlIGdpYmJlcmVsbGluIGRpcmVjdGx5IHN0aW11bGF0ZXMgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHh5bGVtIGV4cGFuc2lvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTA1L3RwYy4xMTEuMDg0MDIwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjIiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5EcnlhZCBEaWdpdGFsIFJlcG9zaXRvcnk8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2Fua2FyPC9hdXRob3I+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDE0PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC41MDYxL2RyeWFkLmI4MzVrPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjMiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DdXJyZW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2lib3V0PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTg8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NDU4PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwODwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkZsb3dlcmluZyBhcyBhIGNvbmRpdGlvbiBmb3IgeHlsZW0gZXhwYW5zaW9uIGluIEFyYWJpZG9wc2lzIGh5cG9jb3R5bCBhbmQgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouY3ViLjIwMDguMDIuMDcwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjQiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5UaGUgTmV3IFBoeXRvbG9naXN0PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlNwaWNlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Njwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT41Nzc8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RXZvbHV0aW9uIG9mIGRldmVsb3BtZW50IG9mIHZhc2N1bGFyIGNhbWJpYSBhbmQgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTExL2ouMTQ2OS04MTM3LjIwMTAuMDMyMzYueDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjI1Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TWFjaGluZSBWaXNpb24gYW5kIEFwcGxpY2F0aW9uczwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5UaGVyaWF1bHQ8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yMzwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42NTk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+Q2VsbCBtb3JwaG9sb2d5IGNsYXNzaWZpY2F0aW9uIGFuZCBjbHV0dGVyIG1pdGlnYXRpb24gaW4gcGhhc2UtY29udHJhc3QgbWljcm9zY29weSBpbWFnZXMgdXNpbmcgbWFjaGluZSBsZWFybmluZzwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDA3L3MwMDEzOC0wMTEtMDM0NS05PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DZWxsPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlV5dHRld2FhbDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE0OTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40Mzk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TWVjaGFuaWNhbCBzdHJlc3MgYWN0cyB2aWEga2F0YW5pbiB0byBhbXBsaWZ5IGRpZmZlcmVuY2VzIGluIGdyb3d0aCByYXRlIGJldHdlZW4gYWRqYWNlbnQgY2VsbHMgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLmNlbGwuMjAxMi4wMi4wNDg8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyNyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk5hdHVyZSBDZWxsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+WWluPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+ODYwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkEgc2NyZWVuIGZvciBtb3JwaG9sb2dpY2FsIGNvbXBsZXhpdHkgaWRlbnRpZmllcyByZWd1bGF0b3JzIG9mIHN3aXRjaC1saWtlIHRyYW5zaXRpb25zIGJldHdlZW4gZGlzY3JldGUgY2VsbCBzaGFwZXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAzOC9uY2IyNzY0PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgIDwvY2l0YXRpb25fbGlzdD4NCiAgICAgICAgICA8Y29tcG9uZW50X2xpc3Q+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5BYnN0cmFjdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDE8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNhYnN0cmFjdDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPmVMaWZlIGRpZ2VzdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDI8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNkaWdlc3Q8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgMS4gQ2VsbHVsYXIgbGV2ZWwgYW5hbHlzaXMgb2YgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGguPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEpIExpZ2h0IG1pY3Jvc2NvcHkgb2YgY3Jvc3Mgc2VjdGlvbnMgb2J0YWluZWQgZnJvbSBBcmFiaWRvcHNpcyBoeXBvY290eWxzIChvcmdhbiBwb3NpdGlvbiBpbGx1c3RyYXRlZCBmb3IgYSA5LWRheS1vbGQgc2VlZGxpbmcsIGxvd2VyIGxlZnQpIGF0IDkgZGFnICh1cHBlciBsZWZ0KSBhbmQgMzUgZGFnIChyaWdodCkuIFNpemUgYmFycyBhcmUgMTAwIM68bS4gQmx1ZSBHVVMgc3RhaW5pbmcgZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiBhbiBBUEw6OkdVUyByZXBvcnRlciBnZW5lIGluIHRoaXMgQ29sLTAgYmFja2dyb3VuZCBsaW5lIG1hcmtzIHBobG9lbSBidW5kbGVzLiAoQikgT3ZlcnZpZXcgb2YgdGhlIGRldmVsb3BtZW50YWwgc2VyaWVzICh0aW1lIHBvaW50cyBhbmQgZGlzdGluY3Qgc2FtcGxlcyBwZXIgZ2Vub3R5cGUpIGFuYWx5emVkIGluIHRoaXMgc3R1ZHkuIChDKSBFeGFtcGxlIG9mIGEgaGlnaC1yZXNvbHV0aW9uIGh5cG9jb3R5bCBzZWN0aW9uIGltYWdlIGFzc2VtYmxlZCBmcm9tIDExIMOXIDExIHRpbGVzLiAoRCkgVGhlIHNhbWUgaW1hZ2UgYWZ0ZXIgcHJlLXByb2Nlc3NpbmcgYW5kIGJpbmFyaXphdGlvbiwgYW5kIChFKSBzdWJzZXF1ZW50IHNlZ21lbnRhdGlvbiB1c2luZyBhIHdhdGVyc2hlZCBhbGdvcml0aG0uIChGKSBOdW1iZXIgb2YgbWlzLXNlZ21lbnRlZCBjZWxscyBhcyBkZXRlcm1pbmVkIGJ5IGNhcmVmdWwgdmlzdWFsIGluc3BlY3Rpb24gaW4gMTIgc2VjdGlvbnMsIHBsb3R0ZWQgYWdhaW5zdCB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIHBlciBzZWN0aW9uIChsb2cgc2NhbGUpLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDM8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWcxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDIuIFRoZSDigJhRdWFudGl0YXRpdmUgSGlzdG9sb2d54oCZIGFwcHJvYWNoLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBPdmVydmlldyBvZiB0aGUgY29tcHV0YXRpb25hbCBwaXBlbGluZSBmcm9tIGltYWdlIGFjcXVpc2l0aW9uIHRvIGFuYWx5c2lzLiAoQikg4oCYUGhlbm9wcmludHPigJkgZm9yIHRoZSBkaWZmZXJlbnQgZ2Vub3R5cGVzIGFuZCBkZXZlbG9wbWVudGFsIHN0YWdlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA0PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSAy4oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gZXhhbXBsZSBvZiBjbGFzc2lmaWVyIHNlbGVjdGlvbiB0aHJvdWdoIFYtZm9sZCBjcm9zcyB2YWxpZGF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBncmVlbiBhcnJvdyBwb2ludHMgb3V0IHRoZSBzZWxlY3RlZCBmZWF0dXJlIGNvbWJpbmF0aW9uIGFjY29yZGluZyB0byB0aGUgY3JpdGVyaWEgb2YgbWluaW11bSBudW1iZXIgb2YgZmVhdHVyZXMgd2l0aCB0aGUgaGlnaGVzdCBwZXJmb3JtYW5jZSBhbmQgdGhlIGxvd2VzdCB2YXJpYXRpb24gKHRoZSByYWRpdXNWIGZlYXR1cmUgd2FzIGV4Y2x1ZGVkIGR1ZSB0byBpdHMgcHV0YXRpdmUgdmFyaWF0aW9uIGluIHRpc3N1ZSBsb2NhdGlvbikuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnMnMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDMuIFByb2dyZXNzaW9uIG9mIHRpc3N1ZSBwcm9saWZlcmF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIG9mIHRoZSBwaGVub3ByaW50cyBzaG93biBpbiBGaWd1cmUgMkIsIHBlcmZvcm1lZCB3aXRoIG5vcm1hbGl6ZWQgdmFsdWVzIChTdXBwbGVtZW50YXJ5IGZpbGUgNCkuIFRoZSBpbmxheSBzY3JlZXBsb3QgZGlzcGxheXMgdGhlIHByb3BvcnRpb24gb2YgdG90YWwgdmFyaWF0aW9uIGV4cGxhaW5lZCBieSBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQuIChC4oCTRSkgQ29tcGFyYXRpdmUgcGxvdHMgb2YgcGFyYW1ldGVyIHByb2dyZXNzaW9uIGluIHRoZSB0d28gZ2Vub3R5cGVzLiBJbiAoRCksIHh5bGVtIHJlcHJlc2VudHMgY29tYmluZWQgdmVzc2VsLCBwYXJlbmNoeW1hLCBhbmQgZmliZXIgY2VsbHMsIHBobG9lbSByZXByZXNlbnRzIGNvbWJpbmVkIHBobG9lbSBwYXJlbmNoeW1hIGFuZCBidW5kbGUgY2VsbHMuIEVycm9yIGJhcnMgaW5kaWNhdGUgc3RhbmRhcmQgZXJyb3IuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNjwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzM8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgNC4gQmltb2RhbCBkaXN0cmlidXRpb24gb2YgaW5jbGluZSBhbmdsZSBhY2NvcmRpbmcgdG8gcG9zaXRpb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEgYW5kIEIpIFNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGNlbGwgaW5jbGluZSBhbmdsZSBpbGx1c3RyYXRlcyB0aGUgdmFzY3VsYXIgb3JnYW5pemF0aW9uIGluIExlciAoQikgYXMgY29tcGFyZWQgdG8gQ29sLTAgKEEpIGF0IGxhdGVyIHN0YWdlcyBvZiBkZXZlbG9wbWVudCwgZm9yIGV4YW1wbGUgMzAgZGFnLiBUaGUgc2l6ZSBvZiB0aGUgZGlzYyBpbmNyZWFzZXMgd2l0aCB0aGUgYXJlYSBvZiB0aGUgY2VsbC4gQmx1ZSBjb2xvciBpbmRpY2F0ZXMgcmFkaWFsIGNlbGwgb3JpZW50YXRpb24sIHJlZCBvcnRob3JhZGlhbC4gKEMgYW5kIEQpIFZpb2xpbiBwbG90cyBvZiBpbmNsaW5lIGFuZ2xlIGRpc3RyaWJ1dGlvbiwgaWxsdXN0cmF0aW5nIGluY3JlYXNpbmdseSBiaW1vZGFsIGRpc3RyaWJ1dGlvbiBjb2luY2lkZW50IHdpdGggcmVmaW5lZCB2YXNjdWxhciBvcmdhbml6YXRpb24gYW5kIGRpZmZlcmVudCBkeW5hbWljcyBvZiB0aGUgcHJvY2VzcyBpbiB0aGUgdHdvIGdlbm90eXBlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA3PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnNDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA04oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gaWxsdXN0cmF0aW9uIG9mIHRoZSBpbmNsaW5lIGFuZ2xlLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBpbmNsaW5lIGlzIHRoZSBhbmdsZSBiZXR3ZWVuIHRoZSBzZWN0aW9uIHJhZGl1cyB0aHJvdWdoIHRoZSBjZW50ZXIgb2YgYW4gZWxsaXBzZSBmaXQgdG8gYSBjZWxsIGFuZCB0aGUgbWFqb3IgYXhpcyBvZiB0aGF0IGVsbGlwc2UgZXh0ZW5kZWQgdG93YXJkcyB0aGUgeCBheGlzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDg8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9maWd1cmVzI2ZpZzRzMTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA1LiBEaXN0aW5jdCBsb2NhbCBvcmdhbml6YXRpb24gb2YgaW5jbGluZSBhbmdsZSBkdXJpbmcgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGggcHJvZ3Jlc3Npb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEHigJNKKSBEZW5zaXR5IHBsb3RzIG9mIGNlbGwgaW5jbGluZSBhbmdsZSB2cyByYWRpYWwgcG9zaXRpb24gZm9yIHRoZSB0d28gZ2Vub3R5cGVzIGF0IHRoZSBpbmRpY2F0ZWQgZGV2ZWxvcG1lbnRhbCBzdGFnZXMsIHJlcHJlc2VudGluZyBhbGwgY2VsbHMgYWNyb3NzIGFsbCBzZWN0aW9ucyBmb3IgYSBnaXZlbiB0aW1lIHBvaW50LiBUaGUgcmVkIGxpbmVzIHJlcHJlc2VudCB0aGUgZml0IG9mIHRoZXNlIGNsb3VkIGRpc3RyaWJ1dGlvbnMgd2l0aCBsb2NhbGx5IHdlaWdodGVkIGxpbmVhciByZWdyZXNzaW9uIChpLmUuLCBsb3dlc3MpLCByZXZlYWxpbmcgdGhlIGVzc2VudGlhbCBkYXRhIHRyZW5kcy4gQWxsIHNlY3Rpb25zIHdlcmUgbm9ybWFsaXplZCBmcm9tIDAuMCAodGhlIG1hbnVhbGx5IGRlZmluZWQgY2VudGVyKSB0byAxLjAgKHRoZSBhdmVyYWdlIHJhZGl1cyBpbiBhIHNldCBvZiBzZWN0aW9ucyBhcyBkZXRlcm1pbmVkIGJ5IHRoZSBhdmVyYWdlIGRpc3RhbmNlIG9mIHRoZSBvdXRlcm1vc3QgY2VsbHMgZnJvbSB0aGUgY2VudGVyIGZvciBpbmRpdmlkdWFsIHNlY3Rpb25zKS4gQm94IHBsb3RzIGluZGljYXRlIHRoZSBxdWFydGlsZXMgb2YgdGhlIHJhZGlhbiBkaXN0cmlidXRpb24gZm9yIGVhY2ggY2VsbC10eXBlIGNsYXNzIGFuZCBhcmUgcGxhY2VkIGF0IHRoZSBhdmVyYWdlIHBvc2l0aW9uIG9mIHRoZSBjZWxsIHR5cGUgd2l0aCByZXNwZWN0IHRvIHRoZSB5IGF4aXMuIE91dGxpZXJzIGFyZSBzaG93biBhcyBjaXJjbGVzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDk8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWc1PC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDXigJRmaWd1cmUgc3VwcGxlbWVudCAxLiBBbmFseXNpcyBvZiBjZWxsIG51bWJlciBpbiBkZWZpbmVkIHh5bGVtIHJlZ2lvbnMgb2YgZGlmZmVyZW50IHNpemUuPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+Q2VsbCBudW1iZXIgaW4gYSBjaXJjbGUgb2YgMjAw4oCTNTAwIHBpeGVscyBhcm91bmQgdGhlIHNlY3Rpb24gY2VudGVycyBmb3IgQ29sLTAuIENlbGwgY291bnQgaW4gYSBjb25zdGFudCBhcmVhIG9mIHh5bGVtIG92ZXIgdGltZSBhY3Jvc3MgYWxsIGF2ZXJhZ2VkIGFjcm9zcyBhbGwgc2VjdGlvbnMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnNXMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDYuIE1hcHBpbmcgb2YgcGhsb2VtIHBvbGUgcGF0dGVybmluZy48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgRXhhbXBsZSBvZiBHYXVzc2lhbiBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSBvZiB0aGUgbG9jYXRpb24gb2YgcHJlZGljdGVkIHBobG9lbSBidW5kbGVzIGNlbGxzIGluIGEgMzAgZGFnIENvbC0wIHNlY3Rpb24uIEhpZ2ggZGVuc2l0eSByZXByZXNlbnRzIHBobG9lbSBwb2xlcy4gKEIpIEV4YW1wbGUgb2YgYW4gYW5hbHlzaXMgb2YgZW1lcmdpbmcgcGhsb2VtIHBvbGUgcG9zaXRpb24gaW4gYSAzMCBkYWcgQ29sLTAgc2VjdGlvbi4gVGhlIHBsb3QgcmVwcmVzZW50cyBhIHBpeGVsIGludGVuc2l0eSBtYXAgYWZ0ZXIgbm9pc2UgcmVkdWN0aW9uIGFsb25nIGEgY2lyY3VsYXIgcmVnaW9uIG9mIGludGVyZXN0IGFjcm9zcyB0aGUgZW1lcmdpbmcgcGhsb2VtIHBvbGVzLiBJbnRlbnNpdHkgcGVha3MgYXJlIGR1ZSB0byBHVVMgc3RhaW5pbmcgY29uZmVycmVkIHRvIHBobG9lbSBidW5kbGVzIGJ5IGFuIEFQTDo6R1VTIHJlcG9ydGVyIGNvbnN0cnVjdC4gKEMpIFByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gb2YgdGhlIGRhdGEgc2hvd24gaW4gKEIpIG9idGFpbmVkIGZyb20gYW4gYXV0b21hdGVkIEJheWVzaWFuIG1vZGVsLiBUaGUgZG9taW5hbnQgc2luZ2xlIHBlYWsgaW5kaWNhdGVzIGEgY29uc3RhbnQgYXJjIGRpc3RhbmNlIG9mIGNhLiA2MiBwaXhlbCBiZXR3ZWVuIHRoZSBwaGxvZW0gcG9sZXMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzY8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgMS48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgQW4gZXhwbGFuYXRpb24gb2YgdGhlIGV4dHJhY3RlZCBwYXJhbWV0ZXJzIHRoYXQgZGVzY3JpYmUgdGhlIGNlbGx1bGFyIGZlYXR1cmVzLiAoQikgU3VtbWFyeSBpbmZvcm1hdGlvbiBvZiB0aGUgaGFuZC1sYWJlbGVkIHRyYWluaW5nIHNldCBmb3Igc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nLiAoQykgRGVmaW5pdGlvbiBvZiB0aGUgY2xhc3NpZmllcnMgc2VsZWN0ZWQgZm9yIGFuYWx5c2lzLiAoRCkgU3VtbWFyeSBvZiB0aGUgY2xhc3NpZmllciBwYXJhbWV0ZXJzIGZvciBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcuIChFKSBPdmVydmlldyBvZiB0aGUgY2VsbCB0eXBlIGNsYXNzZXMgcmVjb2duaXplZCBieSB0aGUgc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoIGFuZCB0aGVpciBhc3NpZ25tZW50IGNvZGVzIHVzZWQgaW4gRGF0YSBGaWxlcyAzIGFuZCA0Ljwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEyPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDEtZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAyLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIENvbC0wIHNlY3Rpb25zLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEzPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDItZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAzLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIExlciBzZWN0aW9ucy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0QzLWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgNC48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT5UaGUgbm9ybWFsaXplZCB2YWx1ZXMgb2YgdGhlIHBoZW5vcHJpbnRzIChGaWd1cmUgMkIpIHVzZWQgZm9yIFBDQS48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0Q0LWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5EZWNpc2lvbiBsZXR0ZXI8L3RpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9InRleHQvcGxhaW4iIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDE2PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjU0ExPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+QXV0aG9yIHJlc3BvbnNlPC90aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJ0ZXh0L3BsYWluIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNzwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I1NBMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICA8L2NvbXBvbmVudF9saXN0Pg0KICAgICAgICA8L2pvdXJuYWxfYXJ0aWNsZT4NCiAgICAgIDwvam91cm5hbD4NCiAgICA8L2Nyb3NzcmVmPg0KICA8L2RvaV9yZWNvcmQ+DQo8L2RvaV9yZWNvcmRzPg== + http_version: + recorded_at: Fri, 07 Dec 2018 18:37:49 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/updates_the_record.yml b/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/updates_the_record.yml new file mode 100644 index 000000000..8adeb834c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/dois/POST_/dois/crossref_url/updates_the_record.yml @@ -0,0 +1,82 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7554 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 18:37:47 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - 40db2943-8f7f-4775-a41f-10509bc53c67 + Etag: + - W/"381993dc8a6b0f20960c96a3639c0284" + X-Runtime: + - '0.102270' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7554","type":"prefixes","attributes":{"registration-agency":"Crossref","created":null,"updated":"2016-09-21T21:07:27Z"},"relationships":{"clients":{"data":[]},"providers":{"data":[]}}},"included":[]}' + http_version: + recorded_at: Fri, 07 Dec 2018 18:37:47 GMT +- request: + method: get + uri: http://www.crossref.org/openurl/?format=unixref&id=doi:10.7554/elife.01567&noredirect=true&pid=tech@datacite.org + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/xml + response: + status: + code: 200 + message: OK + headers: + Server: + - Apache-Coyote/1.1 + Crossref-Deployment-Name: + - cr6-1 + Content-Type: + - text/xml;charset=UTF-8 + Content-Language: + - en-US + Date: + - Fri, 07 Dec 2018 18:37:47 GMT + Connection: + - close + body: + encoding: ASCII-8BIT + string: !binary |- + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGRvaV9yZWNvcmRzPg0KICA8ZG9pX3JlY29yZCBvd25lcj0iMTAuNzU1NCIgdGltZXN0YW1wPSIyMDE4LTA4LTIzIDA5OjQxOjQ5Ij4NCiAgICA8Y3Jvc3NyZWY+DQogICAgICA8am91cm5hbD4NCiAgICAgICAgPGpvdXJuYWxfbWV0YWRhdGEgbGFuZ3VhZ2U9ImVuIj4NCiAgICAgICAgICA8ZnVsbF90aXRsZT5lTGlmZTwvZnVsbF90aXRsZT4NCiAgICAgICAgICA8aXNzbiBtZWRpYV90eXBlPSJlbGVjdHJvbmljIj4yMDUwLTA4NFg8L2lzc24+DQogICAgICAgIDwvam91cm5hbF9tZXRhZGF0YT4NCiAgICAgICAgPGpvdXJuYWxfaXNzdWU+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8am91cm5hbF92b2x1bWU+DQogICAgICAgICAgICA8dm9sdW1lPjM8L3ZvbHVtZT4NCiAgICAgICAgICA8L2pvdXJuYWxfdm9sdW1lPg0KICAgICAgICA8L2pvdXJuYWxfaXNzdWU+DQogICAgICAgIDxqb3VybmFsX2FydGljbGUgcHVibGljYXRpb25fdHlwZT0iZnVsbF90ZXh0IiByZWZlcmVuY2VfZGlzdHJpYnV0aW9uX29wdHM9ImFueSI+DQogICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgIDx0aXRsZT5BdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvdGl0bGU+DQogICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgPGNvbnRyaWJ1dG9ycz4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJmaXJzdCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPk1hcnRpYWw8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlNhbmthcjwvc3VybmFtZT4NCiAgICAgICAgICAgICAgPGFmZmlsaWF0aW9uPkRlcGFydG1lbnQgb2YgUGxhbnQgTW9sZWN1bGFyIEJpb2xvZ3ksIFVuaXZlcnNpdHkgb2YgTGF1c2FubmUsIExhdXNhbm5lLCBTd2l0emVybGFuZDwvYWZmaWxpYXRpb24+DQogICAgICAgICAgICA8L3BlcnNvbl9uYW1lPg0KICAgICAgICAgICAgPHBlcnNvbl9uYW1lIGNvbnRyaWJ1dG9yX3JvbGU9ImF1dGhvciIgc2VxdWVuY2U9ImFkZGl0aW9uYWwiPg0KICAgICAgICAgICAgICA8Z2l2ZW5fbmFtZT5LYWlzYTwvZ2l2ZW5fbmFtZT4NCiAgICAgICAgICAgICAgPHN1cm5hbWU+TmllbWluZW48L3N1cm5hbWU+DQogICAgICAgICAgICAgIDxhZmZpbGlhdGlvbj5EZXBhcnRtZW50IG9mIFBsYW50IE1vbGVjdWxhciBCaW9sb2d5LCBVbml2ZXJzaXR5IG9mIExhdXNhbm5lLCBMYXVzYW5uZSwgU3dpdHplcmxhbmQ8L2FmZmlsaWF0aW9uPg0KICAgICAgICAgICAgPC9wZXJzb25fbmFtZT4NCiAgICAgICAgICAgIDxwZXJzb25fbmFtZSBjb250cmlidXRvcl9yb2xlPSJhdXRob3IiIHNlcXVlbmNlPSJhZGRpdGlvbmFsIj4NCiAgICAgICAgICAgICAgPGdpdmVuX25hbWU+TGF1cmE8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlJhZ25pPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPklvYW5uaXM8L2dpdmVuX25hbWU+DQogICAgICAgICAgICAgIDxzdXJuYW1lPlhlbmFyaW9zPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+Vml0YWwtSVQsIFN3aXNzIEluc3RpdHV0ZSBvZiBCaW9pbmZvcm1hdGljcywgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgICA8cGVyc29uX25hbWUgY29udHJpYnV0b3Jfcm9sZT0iYXV0aG9yIiBzZXF1ZW5jZT0iYWRkaXRpb25hbCI+DQogICAgICAgICAgICAgIDxnaXZlbl9uYW1lPkNocmlzdGlhbiBTPC9naXZlbl9uYW1lPg0KICAgICAgICAgICAgICA8c3VybmFtZT5IYXJkdGtlPC9zdXJuYW1lPg0KICAgICAgICAgICAgICA8YWZmaWxpYXRpb24+RGVwYXJ0bWVudCBvZiBQbGFudCBNb2xlY3VsYXIgQmlvbG9neSwgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZSwgTGF1c2FubmUsIFN3aXR6ZXJsYW5kPC9hZmZpbGlhdGlvbj4NCiAgICAgICAgICAgIDwvcGVyc29uX25hbWU+DQogICAgICAgICAgPC9jb250cmlidXRvcnM+DQogICAgICAgICAgPGFic3RyYWN0Pg0KICAgICAgICAgICAgPHA+QW1vbmcgdmFyaW91cyBhZHZhbnRhZ2VzLCB0aGVpciBzbWFsbCBzaXplIG1ha2VzIG1vZGVsIG9yZ2FuaXNtcyBwcmVmZXJyZWQgc3ViamVjdHMgb2YgaW52ZXN0aWdhdGlvbi4gWWV0LCBldmVuIGluIG1vZGVsIHN5c3RlbXMgZGV0YWlsZWQgYW5hbHlzaXMgb2YgbnVtZXJvdXMgZGV2ZWxvcG1lbnRhbCBwcm9jZXNzZXMgYXQgY2VsbHVsYXIgbGV2ZWwgaXMgc2V2ZXJlbHkgaGFtcGVyZWQgYnkgdGhlaXIgc2NhbGUuIEZvciBpbnN0YW5jZSwgc2Vjb25kYXJ5IGdyb3d0aCBvZiBBcmFiaWRvcHNpcyBoeXBvY290eWxzIGNyZWF0ZXMgYSByYWRpYWwgcGF0dGVybiBvZiBoaWdobHkgc3BlY2lhbGl6ZWQgdGlzc3VlcyB0aGF0IGNvbXByaXNlcyBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHN0YXJ0aW5nIGZyb20gYSBmZXcgZG96ZW4uIFRoaXMgZHluYW1pYyBwcm9jZXNzIGlzIGRpZmZpY3VsdCB0byBmb2xsb3cgYmVjYXVzZSBvZiBpdHMgc2NhbGUgYW5kIGJlY2F1c2UgaXQgY2FuIG9ubHkgYmUgaW52ZXN0aWdhdGVkIGludmFzaXZlbHksIHByZWNsdWRpbmcgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBjZWxsIHByb2xpZmVyYXRpb24sIGRpZmZlcmVudGlhdGlvbiwgYW5kIHBhdHRlcm5pbmcgZXZlbnRzIGludm9sdmVkLiBUbyBvdmVyY29tZSBzdWNoIGxpbWl0YXRpb24sIHdlIGVzdGFibGlzaGVkIGFuIGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d5IGFwcHJvYWNoLiBXZSBhY3F1aXJlZCBoeXBvY290eWwgY3Jvc3Mtc2VjdGlvbnMgZnJvbSB0aWxlZCBoaWdoLXJlc29sdXRpb24gaW1hZ2VzIGFuZCBleHRyYWN0ZWQgdGhlaXIgaW5mb3JtYXRpb24gY29udGVudCB1c2luZyBjdXN0b20gaGlnaC10aHJvdWdocHV0IGltYWdlIHByb2Nlc3NpbmcgYW5kIHNlZ21lbnRhdGlvbi4gQ291cGxlZCB3aXRoIGF1dG9tYXRlZCBjZWxsIHR5cGUgcmVjb2duaXRpb24gdGhyb3VnaCBtYWNoaW5lIGxlYXJuaW5nLCB3ZSBjb3VsZCBlc3RhYmxpc2ggYSBjZWxsdWxhciByZXNvbHV0aW9uIGF0bGFzIHRoYXQgcmV2ZWFscyB2YXNjdWxhciBtb3JwaG9keW5hbWljcyBkdXJpbmcgc2Vjb25kYXJ5IGdyb3d0aCwgZm9yIGV4YW1wbGUgZXF1aWRpc3RhbnQgcGhsb2VtIHBvbGUgZm9ybWF0aW9uLjwvcD4NCiAgICAgICAgICA8L2Fic3RyYWN0Pg0KICAgICAgICAgIDxhYnN0cmFjdCBhYnN0cmFjdC10eXBlPSJleGVjdXRpdmUtc3VtbWFyeSI+DQogICAgICAgICAgICA8cD5PdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgbGl2aW5nIHdvcmxkIGhhcyBiZWVuIGFkdmFuY2VkIGdyZWF0bHkgYnkgc3R1ZGllcyBvZiDigJhtb2RlbCBvcmdhbmlzbXPigJksIHN1Y2ggYXMgbWljZSwgemVicmFmaXNoLCBhbmQgZnJ1aXQgZmxpZXMuIFN0dWR5aW5nIHRoZXNlIGNyZWF0dXJlcyBoYXMgYmVlbiBjcnVjaWFsIHRvIHVuY292ZXJpbmcgdGhlIGdlbmVzIHRoYXQgY29udHJvbCBob3cgb3VyIGJvZGllcyBkZXZlbG9wIGFuZCBncm93LCBhbmQgYWxzbyB0byBkaXNjb3ZlciB0aGUgZ2VuZXRpYyBiYXNpcyBvZiBkaXNlYXNlcyBzdWNoIGFzIGNhbmNlci48L3A+DQogICAgICAgICAgICA8cD5UaGFsZSBjcmVzc+KAlG9yIEFyYWJpZG9wc2lzIHRoYWxpYW5hIHRvIGdpdmUgaXRzIGZvcm1hbCBuYW1l4oCUaXMgdGhlIG1vZGVsIG9yZ2FuaXNtIG9mIGNob2ljZSBmb3IgbWFueSBwbGFudCBiaW9sb2dpc3RzLiBUaGlzIHRpbnkgd2VlZCBoYXMgYmVlbiB3aWRlbHkgc3R1ZGllZCBiZWNhdXNlIGl0IGNhbiBjb21wbGV0ZSBpdHMgbGlmZWN5Y2xlLCBmcm9tIHNlZWQgdG8gc2VlZCwgaW4gYWJvdXQgNiB3ZWVrcywgYW5kIGJlY2F1c2UgaXRzIHJlbGF0aXZlbHkgc21hbGwgZ2Vub21lIHNpbXBsaWZpZXMgdGhlIHNlYXJjaCBmb3IgZ2VuZXMgdGhhdCBjb250cm9sIHNwZWNpZmljIHRyYWl0cy4gSG93ZXZlciwgYXMgd2l0aCBvdGhlciBtdWNoLXN0dWRpZWQgbW9kZWwgc3lzdGVtcywgdW5kZXJzdGFuZGluZyB0aGUgY2hhbmdlcyB0aGF0IHVuZGVycGluIHRoZSBkZXZlbG9wbWVudCBvZiBzb21lIG9mIHRoZSBtb3JlIGNvbXBsZXggdGlzc3VlcyBpbiBBcmFiaWRvcHNpcyBoYXMgYmVlbiBzZXZlcmVseSBoYW1wZXJlZCBieSB0aGUgc2hlYXIgbnVtYmVyIG9mIGNlbGxzIGludm9sdmVkLjwvcD4NCiAgICAgICAgICAgIDxwPkFmdGVyIGl0IGhhcyBlbWVyZ2VkIGZyb20gdGhlIHNlZWQsIHRoZSBwbGFudOKAmXMgZmlyc3Qgc3RlbSB3aWxsIGRldmVsb3AgZnJvbSBhIGZldyBkb3plbiBjZWxscyBpbiB3aWR0aCB0byBzZXZlcmFsIHRob3VzYW5kIGNlbGxzIHdpdGggaGlnaGx5IHNwZWNpYWxpemVkIHRpc3N1ZXMgYXJyYW5nZWQgaW4gYSBjb21wbGV4IHBhdHRlcm4gb2YgY29uY2VudHJpYyBjaXJjbGVzLiBBbHRob3VnaCB0aGlzIHN0ZW0gdGhpY2tlbmluZyBwcm9jZXNzIHJlcHJlc2VudHMgYSBtYWpvciBkZXZlbG9wbWVudGFsIGNoYW5nZSBpbiBtYW55IHBsYW50c+KAlGZyb20gQXJhYmlkb3BzaXMgdG8gb2FrIHRyZWVz4oCUaXQgaGFzIGJlZW4gdW5kZXItcmVzZWFyY2hlZC4gVGhpcyBpcyBwYXJ0bHkgYmVjYXVzZSBpdCBpbnZvbHZlcyBzbyBtYW55IGRpZmZlcmVudCBjZWxscywgYW5kIGFsc28gYmVjYXVzZSBpdCBjYW4gb25seSBiZSBvYnNlcnZlZCBpbiB0aGluIHNlY3Rpb25zIGN1dCBvdXQgb2YgdGhlIHBsYW504oCZcyBzdGVtLjwvcD4NCiAgICAgICAgICAgIDxwPk5vdyBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gaGF2ZSBkZXZlbG9wZWQgYSBub3ZlbCBhcHByb2FjaCwgdGVybWVkIOKAmGF1dG9tYXRlZCBxdWFudGl0YXRpdmUgaGlzdG9sb2d54oCZLCB0byBvdmVyY29tZSB0aGVzZSBwcm9ibGVtcy4gVGhpcyBzdHJhdGVneSBpbnZvbHZlcyDigJh0ZWFjaGluZ+KAmSBhIGNvbXB1dGVyIHRvIGF1dG9tYXRpY2FsbHkgcmVjb2duaXplIGRpZmZlcmVudCBwbGFudCBjZWxscyBhbmQgdG8gbWVhc3VyZSB0aGVpciBpbXBvcnRhbnQgZmVhdHVyZXMgaW4gaGlnaC1yZXNvbHV0aW9uIGltYWdlcyBvZiB0aXNzdWUgc2VjdGlvbnMuIFRoZSByZXN1bHRpbmcg4oCYbWFw4oCZIG9mIHRoZSBkZXZlbG9waW5nIHN0ZW3igJR3aGljaCByZXF1aXJlZCBvdmVyIDgwMCBociBvZiBjb21wdXRpbmcgdGltZSB0byBjb21wbGV0ZeKAlHJldmVhbHMgdGhlIGNoYW5nZXMgdG8gY2VsbHMgYW5kIHRpc3N1ZXMgYXMgdGhleSBkZXZlbG9wIHRoYXQgYWxsb3cgdGhlIHRyYW5zcG9ydCBvZiB3YXRlciwgc3VnYXJzIGFuZCBudXRyaWVudHMgYmV0d2VlbiB0aGUgYWJvdmUtIGFuZCBiZWxvdy1ncm91bmQgb3JnYW5zLiBTYW5rYXIsIE5pZW1pbmVuLCBSYWduaSBldCBhbC4gc3VnZ2VzdCB0aGF0IHRoZWlyIG5vdmVsIGFwcHJvYWNoIGNvdWxkLCBpbiB0aGUgZnV0dXJlLCBhbHNvIGJlIGFwcGxpZWQgdG8gc3R1ZHkgdGhlIGRldmVsb3BtZW50IG9mIG90aGVyIHRpc3N1ZXMgYW5kIG9yZ2FuaXNtcywgaW5jbHVkaW5nIGFuaW1hbHMuPC9wPg0KICAgICAgICAgIDwvYWJzdHJhY3Q+DQogICAgICAgICAgPHB1YmxpY2F0aW9uX2RhdGUgbWVkaWFfdHlwZT0ib25saW5lIj4NCiAgICAgICAgICAgIDxtb250aD4wMjwvbW9udGg+DQogICAgICAgICAgICA8ZGF5PjExPC9kYXk+DQogICAgICAgICAgICA8eWVhcj4yMDE0PC95ZWFyPg0KICAgICAgICAgIDwvcHVibGljYXRpb25fZGF0ZT4NCiAgICAgICAgICA8cHVibGlzaGVyX2l0ZW0+DQogICAgICAgICAgICA8aXRlbV9udW1iZXIgaXRlbV9udW1iZXJfdHlwZT0iYXJ0aWNsZV9udW1iZXIiPmUwMTU2NzwvaXRlbV9udW1iZXI+DQogICAgICAgICAgICA8aWRlbnRpZmllciBpZF90eXBlPSJkb2kiPjEwLjc1NTQvZUxpZmUuMDE1Njc8L2lkZW50aWZpZXI+DQogICAgICAgICAgPC9wdWJsaXNoZXJfaXRlbT4NCiAgICAgICAgICA8cHJvZ3JhbSBuYW1lPSJmdW5kcmVmIj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5FTUJPIGxvbmd0ZXJtIHBvc3QtZG9jdG9yYWwgZmVsbG93c2hpcHM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj5NYXJpZSBIZWltLVZvZWd0bGluPC9hc3NlcnRpb24+DQogICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+DQogICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX2lkZW50aWZpZXIiIHByb3ZpZGVyPSJjcm9zc3JlZiI+NTAxMTAwMDA2MzkwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxwcm9ncmFtIG5hbWU9IkFjY2Vzc0luZGljYXRvcnMiPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0iYW0iPmh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzMuMC88L2xpY2Vuc2VfcmVmPg0KICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InRkbSI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgIDxjcm9zc21hcms+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3ZlcnNpb24+MTwvY3Jvc3NtYXJrX3ZlcnNpb24+DQogICAgICAgICAgICA8Y3Jvc3NtYXJrX3BvbGljeT5lTGlmZXNjaWVuY2VzPC9jcm9zc21hcmtfcG9saWN5Pg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgICA8Y3Jvc3NtYXJrX2RvbWFpbj4NCiAgICAgICAgICAgICAgICA8ZG9tYWluPnd3dy5lbGlmZXNjaWVuY2VzLm9yZzwvZG9tYWluPg0KICAgICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW4+DQogICAgICAgICAgICA8L2Nyb3NzbWFya19kb21haW5zPg0KICAgICAgICAgICAgPGNyb3NzbWFya19kb21haW5fZXhjbHVzaXZlPmZhbHNlPC9jcm9zc21hcmtfZG9tYWluX2V4Y2x1c2l2ZT4NCiAgICAgICAgICAgIDxjdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icmVjZWl2ZWQiIGxhYmVsPSJSZWNlaXZlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIwIj4yMDEzLTA5LTIwPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iYWNjZXB0ZWQiIGxhYmVsPSJBY2NlcHRlZCIgZ3JvdXBfbmFtZT0icHVibGljYXRpb25faGlzdG9yeSIgZ3JvdXBfbGFiZWw9IlB1YmxpY2F0aW9uIEhpc3RvcnkiIG9yZGVyPSIxIj4yMDEzLTEyLTI0PC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0icHVibGlzaGVkIiBsYWJlbD0iUHVibGlzaGVkIiBncm91cF9uYW1lPSJwdWJsaWNhdGlvbl9oaXN0b3J5IiBncm91cF9sYWJlbD0iUHVibGljYXRpb24gSGlzdG9yeSIgb3JkZXI9IjIiPjIwMTQtMDItMTE8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iZnVuZHJlZiI+DQogICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZ3JvdXAiPg0KICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfbmFtZSI+U3lzdGVtc1g8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgRU1CTw0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIj5odHRwOi8vZHguZG9pLm9yZy8xMC4xMzAzOS81MDExMDAwMDMwNDM8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGdyb3VwIj4NCiAgICAgICAgICAgICAgICAgIDxhc3NlcnRpb24gbmFtZT0iZnVuZGVyX25hbWUiPg0KICAgICAgICAgICAgICAgICAgICBTd2lzcyBOYXRpb25hbCBTY2llbmNlIEZvdW5kYXRpb24NCiAgICAgICAgICAgICAgICAgICAgPGFzc2VydGlvbiBuYW1lPSJmdW5kZXJfaWRlbnRpZmllciI+aHR0cDovL2R4LmRvaS5vcmcvMTAuMTMwMzkvNTAxMTAwMDAxNzExPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8L2Fzc2VydGlvbj4NCiAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRncm91cCI+DQogICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9uYW1lIj4NCiAgICAgICAgICAgICAgICAgICAgVW5pdmVyc2l0eSBvZiBMYXVzYW5uZQ0KICAgICAgICAgICAgICAgICAgICA8YXNzZXJ0aW9uIG5hbWU9ImZ1bmRlcl9pZGVudGlmaWVyIiBwcm92aWRlcj0iY3Jvc3NyZWYiPmh0dHA6Ly9keC5kb2kub3JnLzEwLjEzMDM5LzUwMTEwMDAwNjM5MDwvYXNzZXJ0aW9uPg0KICAgICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgICAgPC9hc3NlcnRpb24+DQogICAgICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICAgICAgPHByb2dyYW0gbmFtZT0iQWNjZXNzSW5kaWNhdG9ycyI+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89InZvciI+aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wLzwvbGljZW5zZV9yZWY+DQogICAgICAgICAgICAgICAgPGxpY2Vuc2VfcmVmIGFwcGxpZXNfdG89ImFtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgICA8bGljZW5zZV9yZWYgYXBwbGllc190bz0idGRtIj5odHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS8zLjAvPC9saWNlbnNlX3JlZj4NCiAgICAgICAgICAgICAgPC9wcm9ncmFtPg0KICAgICAgICAgICAgPC9jdXN0b21fbWV0YWRhdGE+DQogICAgICAgICAgPC9jcm9zc21hcms+DQogICAgICAgICAgPHByb2dyYW0+DQogICAgICAgICAgICA8cmVsYXRlZF9pdGVtPg0KICAgICAgICAgICAgICA8ZGVzY3JpcHRpb24+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvZGVzY3JpcHRpb24+DQogICAgICAgICAgICAgIDxpbnRlcl93b3JrX3JlbGF0aW9uIGlkZW50aWZpZXItdHlwZT0iZG9pIiByZWxhdGlvbnNoaXAtdHlwZT0iaXNTdXBwbGVtZW50ZWRCeSI+MTAuNTA2MS9kcnlhZC5iODM1azwvaW50ZXJfd29ya19yZWxhdGlvbj4NCiAgICAgICAgICAgIDwvcmVsYXRlZF9pdGVtPg0KICAgICAgICAgIDwvcHJvZ3JhbT4NCiAgICAgICAgICA8YXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgICA8YXJjaGl2ZSBuYW1lPSJDTE9DS1NTIiAvPg0KICAgICAgICAgIDwvYXJjaGl2ZV9sb2NhdGlvbnM+DQogICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3PC9kb2k+DQogICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NzwvcmVzb3VyY2U+DQogICAgICAgICAgICA8Y29sbGVjdGlvbiBwcm9wZXJ0eT0idGV4dC1taW5pbmciPg0KICAgICAgICAgICAgICA8aXRlbT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2UgbWltZV90eXBlPSJhcHBsaWNhdGlvbi9wZGYiPmh0dHBzOi8vY2RuLmVsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2VsaWZlLTAxNTY3LXYxLnBkZjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvaXRlbT4NCiAgICAgICAgICAgICAgPGl0ZW0+DQogICAgICAgICAgICAgICAgPHJlc291cmNlIG1pbWVfdHlwZT0iYXBwbGljYXRpb24veG1sIj5odHRwczovL2Nkbi5lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9lbGlmZS0wMTU2Ny12MS54bWw8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2l0ZW0+DQogICAgICAgICAgICA8L2NvbGxlY3Rpb24+DQogICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICA8Y2l0YXRpb25fbGlzdD4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmU8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Qm9ua2U8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT40MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTgxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkFQTCByZWd1bGF0ZXMgdmFzY3VsYXIgdGlzc3VlIGlkZW50aXR5IGluIEFyYWJpZG9wc2lzPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmF0dXJlMDIxMDA8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+R2VuZXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+QnJlbm5lcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Mjwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40MTM8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA5PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+SW4gdGhlIGJlZ2lubmluZyB3YXMgdGhlIHdvcm08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTUzNC9nZW5ldGljcy4xMDkuMTA0OTc2PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPlBoeXNpb2xvZ2lhIFBsYW50YXJ1bTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5DaGFmZmV5PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjU5NDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TZWNvbmRhcnkgeHlsZW0gZGV2ZWxvcG1lbnQgaW4gQXJhYmlkb3BzaXM6IGEgbW9kZWwgZm9yIHdvb2QgZm9ybWF0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzQvai4xMzk5LTMwNTQuMjAwMi4xMTQwNDEzLng8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TmV1cmFsIGNvbXB1dGF0aW9uPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkNoYW5nPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjExOTwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDE8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5UcmFpbmluZyBudS1zdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyczogdGhlb3J5IGFuZCBhbGdvcml0aG1zPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExNjIvMDg5OTc2NjAxNzUwMzk5MzM1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk1hY2hpbmUgTGVhcm5pbmc8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+Q29ydGVzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjczPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5NTwvY1llYXI+DQogICAgICAgICAgICAgIDxkb2kgcHJvdmlkZXI9ImNyb3NzcmVmIj4xMC4xMDA3L0JGMDA5OTQwMTg8L2RvaT4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+U3VwcG9ydC12ZWN0b3IgTmV0d29ya3M8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliNiI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkRldmVsb3BtZW50PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkRvbGFuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTE5PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjcxPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MTk5MzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNlbGx1bGFyIG9yZ2FuaXNhdGlvbiBvZiB0aGUgQXJhYmlkb3BzaXMgdGhhbGlhbmEgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI3Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+U2VtaW5hcnMgaW4gQ2VsbCAmYW1wOyBEZXZlbG9wbWVudGFsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RWxvPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTA5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5TdGVtIGNlbGwgZnVuY3Rpb24gZHVyaW5nIHBsYW50IHZhc2N1bGFyIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMTYvai5zZW1jZGIuMjAwOS4wOS4wMDk8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWI4Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+RGV2ZWxvcG1lbnQ8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+RXRjaGVsbHM8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4xNDA8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MjIyNDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTM8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5XT1g0IGFuZCBXT1gxNCBhY3QgZG93bnN0cmVhbSBvZiB0aGUgUFhZIHJlY2VwdG9yIGtpbmFzZSB0byByZWd1bGF0ZSBwbGFudCB2YXNjdWxhciBwcm9saWZlcmF0aW9uIGluZGVwZW5kZW50bHkgb2YgYW55IHJvbGUgaW4gdmFzY3VsYXIgb3JnYW5pc2F0aW9uPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEyNDIvZGV2LjA5MTMxNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QTE9TIEdlbmV0aWNzPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkV0Y2hlbGxzPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+ODwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT5lMTAwMjk5NzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudCB2YXNjdWxhciBjZWxsIGRpdmlzaW9uIGlzIG1haW50YWluZWQgYnkgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBQWFkgYW5kIGV0aHlsZW5lIHNpZ25hbGxpbmc8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTM3MS9qb3VybmFsLnBnZW4uMTAwMjk5NzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TW9sZWN1bGFyIFN5c3RlbXMgQmlvbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5GdWNoczwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MzcwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkNsdXN0ZXJpbmcgcGhlbm90eXBlIHBvcHVsYXRpb25zIGJ5IGdlbm9tZS13aWRlIFJOQWkgYW5kIG11bHRpcGFyYW1ldHJpYyBpbWFnaW5nPC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbXNiLjIwMTAuMjU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxMSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkJpbyBTeXN0ZW1zPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPkdyYW5xdmlzdDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjExMDwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42MDwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMTI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5CYVNBUi1BIHRvb2wgaW4gUiBmb3IgZnJlcXVlbmN5IGRldGVjdGlvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouYmlvc3lzdGVtcy4yMDEyLjA3LjAwNDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEyIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+Q3VycmVudCBPcGluaW9uIGluIFBsYW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+R3Jvb3ZlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjk8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NTU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGV2ZWxvcG1lbnRhbCBtZWNoYW5pc21zIHJlZ3VsYXRpbmcgc2Vjb25kYXJ5IGdyb3d0aCBpbiB3b29keSBwbGFudHM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLnBiaS4yMDA1LjExLjAxMzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjEzIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIyPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MTg8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+VERJRiBwZXB0aWRlIHNpZ25hbGluZyByZWd1bGF0ZXMgdmFzY3VsYXIgc3RlbSBjZWxsIHByb2xpZmVyYXRpb24gdmlhIHRoZSBXT1g0IGhvbWVvYm94IGdlbmUgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNS90cGMuMTEwLjA3NjA4MzwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjE0Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMgb2YgdGhlIFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5IaXJha2F3YTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjEwNTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT4xNTIwODwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDg8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5Ob24tY2VsbC1hdXRvbm9tb3VzIGNvbnRyb2wgb2YgdmFzY3VsYXIgc3RlbSBjZWxsIGZhdGUgYnkgYSBDTEUgcGVwdGlkZS9yZWNlcHRvciBzeXN0ZW08L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjA4MDg0NDQxMDU8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIxNSI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPkNlbGw8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+TWV5ZXJvd2l0ejwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjU2PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjI2MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODk8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BcmFiaWRvcHNpcywgYSB1c2VmdWwgd2VlZDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2LzAwOTItODY3NCg4OSk5MDkwMC04PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5TY2llbmNlPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk1leWVyb3dpdHo8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yOTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTQ4MjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDI8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5QbGFudHMgY29tcGFyZWQgdG8gYW5pbWFsczogdGhlIGJyb2FkZXN0IGNvbXBhcmF0aXZlIHN0dWR5IG9mIGRldmVsb3BtZW50PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjExMjYvc2NpZW5jZS4xMDY2NjA5PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTciPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5QbGFudCBQaHlzaW9sPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk5pZW1pbmVuPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTM1PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjY1MzwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjIwMDQ8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5BIHdlZWQgZm9yIHdvb2Q/IEFyYWJpZG9wc2lzIGFzIGEgZ2VuZXRpYyBtb2RlbCBmb3IgeHlsZW0gZGV2ZWxvcG1lbnQ8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTEwNC9wcC4xMDQuMDQwMjEyPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTgiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5OYXR1cmUgQmlvdGVjaG5vbG9neTwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5Ob2JsZTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjI0PC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjE1NjU8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDA2PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+V2hhdCBpcyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmU/PC9hcnRpY2xlX3RpdGxlPg0KICAgICAgICAgICAgICA8ZG9pPjEwLjEwMzgvbmJ0MTIwNi0xNTY1PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMTkiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5Qcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcyBvZiB0aGUgVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPk9sc29uPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+Nzc8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+MTUxNjwvZmlyc3RfcGFnZT4NCiAgICAgICAgICAgICAgPGNZZWFyPjE5ODA8L2NZZWFyPg0KICAgICAgICAgICAgICA8YXJ0aWNsZV90aXRsZT5DbGFzc2lmaWNhdGlvbiBvZiBjdWx0dXJlZCBtYW1tYWxpYW4gY2VsbHMgYnkgc2hhcGUgYW5hbHlzaXMgYW5kIHBhdHRlcm4gcmVjb2duaXRpb248L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTA3My9wbmFzLjc3LjMuMTUxNjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIwIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+QmlvaW5mb3JtYXRpY3M8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+UGF1PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MjY8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+OTc5PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMDwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkVCSW1hZ2XigJNhbiBSIHBhY2thZ2UgZm9yIGltYWdlIHByb2Nlc3Npbmcgd2l0aCBhcHBsaWNhdGlvbnMgdG8gY2VsbHVsYXIgcGhlbm90eXBlczwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDkzL2Jpb2luZm9ybWF0aWNzL2J0cTA0NjwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjIxIj4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+UGxhbnQgQ2VsbDwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5SYWduaTwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjIzPC92b2x1bWU+DQogICAgICAgICAgICAgIDxmaXJzdF9wYWdlPjEzMjI8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDExPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TW9iaWxlIGdpYmJlcmVsbGluIGRpcmVjdGx5IHN0aW11bGF0ZXMgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHh5bGVtIGV4cGFuc2lvbjwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTA1L3RwYy4xMTEuMDg0MDIwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjIiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5EcnlhZCBEaWdpdGFsIFJlcG9zaXRvcnk8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2Fua2FyPC9hdXRob3I+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDE0PC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RGF0YSBmcm9tOiBBdXRvbWF0ZWQgcXVhbnRpdGF0aXZlIGhpc3RvbG9neSByZXZlYWxzIHZhc2N1bGFyIG1vcnBob2R5bmFtaWNzIGR1cmluZyBBcmFiaWRvcHNpcyBoeXBvY290eWwgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC41MDYxL2RyeWFkLmI4MzVrPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjMiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DdXJyZW50IEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+U2lib3V0PC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTg8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+NDU4PC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAwODwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkZsb3dlcmluZyBhcyBhIGNvbmRpdGlvbiBmb3IgeHlsZW0gZXhwYW5zaW9uIGluIEFyYWJpZG9wc2lzIGh5cG9jb3R5bCBhbmQgcm9vdDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDE2L2ouY3ViLjIwMDguMDIuMDcwPC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjQiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5UaGUgTmV3IFBoeXRvbG9naXN0PC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlNwaWNlcjwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE4Njwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT41Nzc8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEwPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+RXZvbHV0aW9uIG9mIGRldmVsb3BtZW50IG9mIHZhc2N1bGFyIGNhbWJpYSBhbmQgc2Vjb25kYXJ5IGdyb3d0aDwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMTExL2ouMTQ2OS04MTM3LjIwMTAuMDMyMzYueDwvZG9pPg0KICAgICAgICAgICAgPC9jaXRhdGlvbj4NCiAgICAgICAgICAgIDxjaXRhdGlvbiBrZXk9ImJpYjI1Ij4NCiAgICAgICAgICAgICAgPGpvdXJuYWxfdGl0bGU+TWFjaGluZSBWaXNpb24gYW5kIEFwcGxpY2F0aW9uczwvam91cm5hbF90aXRsZT4NCiAgICAgICAgICAgICAgPGF1dGhvcj5UaGVyaWF1bHQ8L2F1dGhvcj4NCiAgICAgICAgICAgICAgPHZvbHVtZT4yMzwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT42NTk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+Q2VsbCBtb3JwaG9sb2d5IGNsYXNzaWZpY2F0aW9uIGFuZCBjbHV0dGVyIG1pdGlnYXRpb24gaW4gcGhhc2UtY29udHJhc3QgbWljcm9zY29weSBpbWFnZXMgdXNpbmcgbWFjaGluZSBsZWFybmluZzwvYXJ0aWNsZV90aXRsZT4NCiAgICAgICAgICAgICAgPGRvaT4xMC4xMDA3L3MwMDEzOC0wMTEtMDM0NS05PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgICAgPGNpdGF0aW9uIGtleT0iYmliMjYiPg0KICAgICAgICAgICAgICA8am91cm5hbF90aXRsZT5DZWxsPC9qb3VybmFsX3RpdGxlPg0KICAgICAgICAgICAgICA8YXV0aG9yPlV5dHRld2FhbDwvYXV0aG9yPg0KICAgICAgICAgICAgICA8dm9sdW1lPjE0OTwvdm9sdW1lPg0KICAgICAgICAgICAgICA8Zmlyc3RfcGFnZT40Mzk8L2ZpcnN0X3BhZ2U+DQogICAgICAgICAgICAgIDxjWWVhcj4yMDEyPC9jWWVhcj4NCiAgICAgICAgICAgICAgPGFydGljbGVfdGl0bGU+TWVjaGFuaWNhbCBzdHJlc3MgYWN0cyB2aWEga2F0YW5pbiB0byBhbXBsaWZ5IGRpZmZlcmVuY2VzIGluIGdyb3d0aCByYXRlIGJldHdlZW4gYWRqYWNlbnQgY2VsbHMgaW4gQXJhYmlkb3BzaXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAxNi9qLmNlbGwuMjAxMi4wMi4wNDg8L2RvaT4NCiAgICAgICAgICAgIDwvY2l0YXRpb24+DQogICAgICAgICAgICA8Y2l0YXRpb24ga2V5PSJiaWIyNyI+DQogICAgICAgICAgICAgIDxqb3VybmFsX3RpdGxlPk5hdHVyZSBDZWxsIEJpb2xvZ3k8L2pvdXJuYWxfdGl0bGU+DQogICAgICAgICAgICAgIDxhdXRob3I+WWluPC9hdXRob3I+DQogICAgICAgICAgICAgIDx2b2x1bWU+MTU8L3ZvbHVtZT4NCiAgICAgICAgICAgICAgPGZpcnN0X3BhZ2U+ODYwPC9maXJzdF9wYWdlPg0KICAgICAgICAgICAgICA8Y1llYXI+MjAxMzwvY1llYXI+DQogICAgICAgICAgICAgIDxhcnRpY2xlX3RpdGxlPkEgc2NyZWVuIGZvciBtb3JwaG9sb2dpY2FsIGNvbXBsZXhpdHkgaWRlbnRpZmllcyByZWd1bGF0b3JzIG9mIHN3aXRjaC1saWtlIHRyYW5zaXRpb25zIGJldHdlZW4gZGlzY3JldGUgY2VsbCBzaGFwZXM8L2FydGljbGVfdGl0bGU+DQogICAgICAgICAgICAgIDxkb2k+MTAuMTAzOC9uY2IyNzY0PC9kb2k+DQogICAgICAgICAgICA8L2NpdGF0aW9uPg0KICAgICAgICAgIDwvY2l0YXRpb25fbGlzdD4NCiAgICAgICAgICA8Y29tcG9uZW50X2xpc3Q+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5BYnN0cmFjdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDE8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNhYnN0cmFjdDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPmVMaWZlIGRpZ2VzdDwvdGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0idGV4dC9wbGFpbiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDI8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNkaWdlc3Q8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgMS4gQ2VsbHVsYXIgbGV2ZWwgYW5hbHlzaXMgb2YgQXJhYmlkb3BzaXMgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGguPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEpIExpZ2h0IG1pY3Jvc2NvcHkgb2YgY3Jvc3Mgc2VjdGlvbnMgb2J0YWluZWQgZnJvbSBBcmFiaWRvcHNpcyBoeXBvY290eWxzIChvcmdhbiBwb3NpdGlvbiBpbGx1c3RyYXRlZCBmb3IgYSA5LWRheS1vbGQgc2VlZGxpbmcsIGxvd2VyIGxlZnQpIGF0IDkgZGFnICh1cHBlciBsZWZ0KSBhbmQgMzUgZGFnIChyaWdodCkuIFNpemUgYmFycyBhcmUgMTAwIM68bS4gQmx1ZSBHVVMgc3RhaW5pbmcgZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiBhbiBBUEw6OkdVUyByZXBvcnRlciBnZW5lIGluIHRoaXMgQ29sLTAgYmFja2dyb3VuZCBsaW5lIG1hcmtzIHBobG9lbSBidW5kbGVzLiAoQikgT3ZlcnZpZXcgb2YgdGhlIGRldmVsb3BtZW50YWwgc2VyaWVzICh0aW1lIHBvaW50cyBhbmQgZGlzdGluY3Qgc2FtcGxlcyBwZXIgZ2Vub3R5cGUpIGFuYWx5emVkIGluIHRoaXMgc3R1ZHkuIChDKSBFeGFtcGxlIG9mIGEgaGlnaC1yZXNvbHV0aW9uIGh5cG9jb3R5bCBzZWN0aW9uIGltYWdlIGFzc2VtYmxlZCBmcm9tIDExIMOXIDExIHRpbGVzLiAoRCkgVGhlIHNhbWUgaW1hZ2UgYWZ0ZXIgcHJlLXByb2Nlc3NpbmcgYW5kIGJpbmFyaXphdGlvbiwgYW5kIChFKSBzdWJzZXF1ZW50IHNlZ21lbnRhdGlvbiB1c2luZyBhIHdhdGVyc2hlZCBhbGdvcml0aG0uIChGKSBOdW1iZXIgb2YgbWlzLXNlZ21lbnRlZCBjZWxscyBhcyBkZXRlcm1pbmVkIGJ5IGNhcmVmdWwgdmlzdWFsIGluc3BlY3Rpb24gaW4gMTIgc2VjdGlvbnMsIHBsb3R0ZWQgYWdhaW5zdCB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIHBlciBzZWN0aW9uIChsb2cgc2NhbGUpLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDM8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWcxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDIuIFRoZSDigJhRdWFudGl0YXRpdmUgSGlzdG9sb2d54oCZIGFwcHJvYWNoLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBPdmVydmlldyBvZiB0aGUgY29tcHV0YXRpb25hbCBwaXBlbGluZSBmcm9tIGltYWdlIGFjcXVpc2l0aW9uIHRvIGFuYWx5c2lzLiAoQikg4oCYUGhlbm9wcmludHPigJkgZm9yIHRoZSBkaWZmZXJlbnQgZ2Vub3R5cGVzIGFuZCBkZXZlbG9wbWVudGFsIHN0YWdlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA0PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSAy4oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gZXhhbXBsZSBvZiBjbGFzc2lmaWVyIHNlbGVjdGlvbiB0aHJvdWdoIFYtZm9sZCBjcm9zcyB2YWxpZGF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBncmVlbiBhcnJvdyBwb2ludHMgb3V0IHRoZSBzZWxlY3RlZCBmZWF0dXJlIGNvbWJpbmF0aW9uIGFjY29yZGluZyB0byB0aGUgY3JpdGVyaWEgb2YgbWluaW11bSBudW1iZXIgb2YgZmVhdHVyZXMgd2l0aCB0aGUgaGlnaGVzdCBwZXJmb3JtYW5jZSBhbmQgdGhlIGxvd2VzdCB2YXJpYXRpb24gKHRoZSByYWRpdXNWIGZlYXR1cmUgd2FzIGV4Y2x1ZGVkIGR1ZSB0byBpdHMgcHV0YXRpdmUgdmFyaWF0aW9uIGluIHRpc3N1ZSBsb2NhdGlvbikuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnMnMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDMuIFByb2dyZXNzaW9uIG9mIHRpc3N1ZSBwcm9saWZlcmF0aW9uLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPihBKSBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIG9mIHRoZSBwaGVub3ByaW50cyBzaG93biBpbiBGaWd1cmUgMkIsIHBlcmZvcm1lZCB3aXRoIG5vcm1hbGl6ZWQgdmFsdWVzIChTdXBwbGVtZW50YXJ5IGZpbGUgNCkuIFRoZSBpbmxheSBzY3JlZXBsb3QgZGlzcGxheXMgdGhlIHByb3BvcnRpb24gb2YgdG90YWwgdmFyaWF0aW9uIGV4cGxhaW5lZCBieSBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQuIChC4oCTRSkgQ29tcGFyYXRpdmUgcGxvdHMgb2YgcGFyYW1ldGVyIHByb2dyZXNzaW9uIGluIHRoZSB0d28gZ2Vub3R5cGVzLiBJbiAoRCksIHh5bGVtIHJlcHJlc2VudHMgY29tYmluZWQgdmVzc2VsLCBwYXJlbmNoeW1hLCBhbmQgZmliZXIgY2VsbHMsIHBobG9lbSByZXByZXNlbnRzIGNvbWJpbmVkIHBobG9lbSBwYXJlbmNoeW1hIGFuZCBidW5kbGUgY2VsbHMuIEVycm9yIGJhcnMgaW5kaWNhdGUgc3RhbmRhcmQgZXJyb3IuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAwNjwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzM8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5GaWd1cmUgNC4gQmltb2RhbCBkaXN0cmlidXRpb24gb2YgaW5jbGluZSBhbmdsZSBhY2NvcmRpbmcgdG8gcG9zaXRpb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEEgYW5kIEIpIFNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGNlbGwgaW5jbGluZSBhbmdsZSBpbGx1c3RyYXRlcyB0aGUgdmFzY3VsYXIgb3JnYW5pemF0aW9uIGluIExlciAoQikgYXMgY29tcGFyZWQgdG8gQ29sLTAgKEEpIGF0IGxhdGVyIHN0YWdlcyBvZiBkZXZlbG9wbWVudCwgZm9yIGV4YW1wbGUgMzAgZGFnLiBUaGUgc2l6ZSBvZiB0aGUgZGlzYyBpbmNyZWFzZXMgd2l0aCB0aGUgYXJlYSBvZiB0aGUgY2VsbC4gQmx1ZSBjb2xvciBpbmRpY2F0ZXMgcmFkaWFsIGNlbGwgb3JpZW50YXRpb24sIHJlZCBvcnRob3JhZGlhbC4gKEMgYW5kIEQpIFZpb2xpbiBwbG90cyBvZiBpbmNsaW5lIGFuZ2xlIGRpc3RyaWJ1dGlvbiwgaWxsdXN0cmF0aW5nIGluY3JlYXNpbmdseSBiaW1vZGFsIGRpc3RyaWJ1dGlvbiBjb2luY2lkZW50IHdpdGggcmVmaW5lZCB2YXNjdWxhciBvcmdhbml6YXRpb24gYW5kIGRpZmZlcmVudCBkeW5hbWljcyBvZiB0aGUgcHJvY2VzcyBpbiB0aGUgdHdvIGdlbm90eXBlcy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImltYWdlL3RpZmYiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDA3PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjZmlnNDwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA04oCUZmlndXJlIHN1cHBsZW1lbnQgMS4gQW4gaWxsdXN0cmF0aW9uIG9mIHRoZSBpbmNsaW5lIGFuZ2xlLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlRoZSBpbmNsaW5lIGlzIHRoZSBhbmdsZSBiZXR3ZWVuIHRoZSBzZWN0aW9uIHJhZGl1cyB0aHJvdWdoIHRoZSBjZW50ZXIgb2YgYW4gZWxsaXBzZSBmaXQgdG8gYSBjZWxsIGFuZCB0aGUgbWFqb3IgYXhpcyBvZiB0aGF0IGVsbGlwc2UgZXh0ZW5kZWQgdG93YXJkcyB0aGUgeCBheGlzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDg8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2Ny9maWd1cmVzI2ZpZzRzMTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPkZpZ3VyZSA1LiBEaXN0aW5jdCBsb2NhbCBvcmdhbml6YXRpb24gb2YgaW5jbGluZSBhbmdsZSBkdXJpbmcgaHlwb2NvdHlsIHNlY29uZGFyeSBncm93dGggcHJvZ3Jlc3Npb24uPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+KEHigJNKKSBEZW5zaXR5IHBsb3RzIG9mIGNlbGwgaW5jbGluZSBhbmdsZSB2cyByYWRpYWwgcG9zaXRpb24gZm9yIHRoZSB0d28gZ2Vub3R5cGVzIGF0IHRoZSBpbmRpY2F0ZWQgZGV2ZWxvcG1lbnRhbCBzdGFnZXMsIHJlcHJlc2VudGluZyBhbGwgY2VsbHMgYWNyb3NzIGFsbCBzZWN0aW9ucyBmb3IgYSBnaXZlbiB0aW1lIHBvaW50LiBUaGUgcmVkIGxpbmVzIHJlcHJlc2VudCB0aGUgZml0IG9mIHRoZXNlIGNsb3VkIGRpc3RyaWJ1dGlvbnMgd2l0aCBsb2NhbGx5IHdlaWdodGVkIGxpbmVhciByZWdyZXNzaW9uIChpLmUuLCBsb3dlc3MpLCByZXZlYWxpbmcgdGhlIGVzc2VudGlhbCBkYXRhIHRyZW5kcy4gQWxsIHNlY3Rpb25zIHdlcmUgbm9ybWFsaXplZCBmcm9tIDAuMCAodGhlIG1hbnVhbGx5IGRlZmluZWQgY2VudGVyKSB0byAxLjAgKHRoZSBhdmVyYWdlIHJhZGl1cyBpbiBhIHNldCBvZiBzZWN0aW9ucyBhcyBkZXRlcm1pbmVkIGJ5IHRoZSBhdmVyYWdlIGRpc3RhbmNlIG9mIHRoZSBvdXRlcm1vc3QgY2VsbHMgZnJvbSB0aGUgY2VudGVyIGZvciBpbmRpdmlkdWFsIHNlY3Rpb25zKS4gQm94IHBsb3RzIGluZGljYXRlIHRoZSBxdWFydGlsZXMgb2YgdGhlIHJhZGlhbiBkaXN0cmlidXRpb24gZm9yIGVhY2ggY2VsbC10eXBlIGNsYXNzIGFuZCBhcmUgcGxhY2VkIGF0IHRoZSBhdmVyYWdlIHBvc2l0aW9uIG9mIHRoZSBjZWxsIHR5cGUgd2l0aCByZXNwZWN0IHRvIHRoZSB5IGF4aXMuIE91dGxpZXJzIGFyZSBzaG93biBhcyBjaXJjbGVzLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iaW1hZ2UvdGlmZiIgLz4NCiAgICAgICAgICAgICAgPGRvaV9kYXRhPg0KICAgICAgICAgICAgICAgIDxkb2k+MTAuNzU1NC9lTGlmZS4wMTU2Ny4wMDk8L2RvaT4NCiAgICAgICAgICAgICAgICA8cmVzb3VyY2U+aHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy8wMTU2NyNmaWc1PC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDXigJRmaWd1cmUgc3VwcGxlbWVudCAxLiBBbmFseXNpcyBvZiBjZWxsIG51bWJlciBpbiBkZWZpbmVkIHh5bGVtIHJlZ2lvbnMgb2YgZGlmZmVyZW50IHNpemUuPC90aXRsZT4NCiAgICAgICAgICAgICAgICA8c3VidGl0bGU+Q2VsbCBudW1iZXIgaW4gYSBjaXJjbGUgb2YgMjAw4oCTNTAwIHBpeGVscyBhcm91bmQgdGhlIHNlY3Rpb24gY2VudGVycyBmb3IgQ29sLTAuIENlbGwgY291bnQgaW4gYSBjb25zdGFudCBhcmVhIG9mIHh5bGVtIG92ZXIgdGltZSBhY3Jvc3MgYWxsIGF2ZXJhZ2VkIGFjcm9zcyBhbGwgc2VjdGlvbnMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjZmlnNXMxPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+RmlndXJlIDYuIE1hcHBpbmcgb2YgcGhsb2VtIHBvbGUgcGF0dGVybmluZy48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgRXhhbXBsZSBvZiBHYXVzc2lhbiBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSBvZiB0aGUgbG9jYXRpb24gb2YgcHJlZGljdGVkIHBobG9lbSBidW5kbGVzIGNlbGxzIGluIGEgMzAgZGFnIENvbC0wIHNlY3Rpb24uIEhpZ2ggZGVuc2l0eSByZXByZXNlbnRzIHBobG9lbSBwb2xlcy4gKEIpIEV4YW1wbGUgb2YgYW4gYW5hbHlzaXMgb2YgZW1lcmdpbmcgcGhsb2VtIHBvbGUgcG9zaXRpb24gaW4gYSAzMCBkYWcgQ29sLTAgc2VjdGlvbi4gVGhlIHBsb3QgcmVwcmVzZW50cyBhIHBpeGVsIGludGVuc2l0eSBtYXAgYWZ0ZXIgbm9pc2UgcmVkdWN0aW9uIGFsb25nIGEgY2lyY3VsYXIgcmVnaW9uIG9mIGludGVyZXN0IGFjcm9zcyB0aGUgZW1lcmdpbmcgcGhsb2VtIHBvbGVzLiBJbnRlbnNpdHkgcGVha3MgYXJlIGR1ZSB0byBHVVMgc3RhaW5pbmcgY29uZmVycmVkIHRvIHBobG9lbSBidW5kbGVzIGJ5IGFuIEFQTDo6R1VTIHJlcG9ydGVyIGNvbnN0cnVjdC4gKEMpIFByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gb2YgdGhlIGRhdGEgc2hvd24gaW4gKEIpIG9idGFpbmVkIGZyb20gYW4gYXV0b21hdGVkIEJheWVzaWFuIG1vZGVsLiBUaGUgZG9taW5hbnQgc2luZ2xlIHBlYWsgaW5kaWNhdGVzIGEgY29uc3RhbnQgYXJjIGRpc3RhbmNlIG9mIGNhLiA2MiBwaXhlbCBiZXR3ZWVuIHRoZSBwaGxvZW0gcG9sZXMuPC9zdWJ0aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJpbWFnZS90aWZmIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxMTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I2ZpZzY8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgMS48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT4oQSkgQW4gZXhwbGFuYXRpb24gb2YgdGhlIGV4dHJhY3RlZCBwYXJhbWV0ZXJzIHRoYXQgZGVzY3JpYmUgdGhlIGNlbGx1bGFyIGZlYXR1cmVzLiAoQikgU3VtbWFyeSBpbmZvcm1hdGlvbiBvZiB0aGUgaGFuZC1sYWJlbGVkIHRyYWluaW5nIHNldCBmb3Igc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nLiAoQykgRGVmaW5pdGlvbiBvZiB0aGUgY2xhc3NpZmllcnMgc2VsZWN0ZWQgZm9yIGFuYWx5c2lzLiAoRCkgU3VtbWFyeSBvZiB0aGUgY2xhc3NpZmllciBwYXJhbWV0ZXJzIGZvciBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcuIChFKSBPdmVydmlldyBvZiB0aGUgY2VsbCB0eXBlIGNsYXNzZXMgcmVjb2duaXplZCBieSB0aGUgc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoIGFuZCB0aGVpciBhc3NpZ25tZW50IGNvZGVzIHVzZWQgaW4gRGF0YSBGaWxlcyAzIGFuZCA0Ljwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEyPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDEtZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAyLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIENvbC0wIHNlY3Rpb25zLjwvc3VidGl0bGU+DQogICAgICAgICAgICAgIDwvdGl0bGVzPg0KICAgICAgICAgICAgICA8Zm9ybWF0IG1pbWVfdHlwZT0iYXBwbGljYXRpb24vdm5kLm9wZW54bWxmb3JtYXRzLW9mZmljZWRvY3VtZW50LnNwcmVhZHNoZWV0bWwuc2hlZXQiIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDEzPC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcvZmlndXJlcyNTRDItZGF0YTwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICAgIDxjb21wb25lbnQgcGFyZW50X3JlbGF0aW9uPSJpc1BhcnRPZiI+DQogICAgICAgICAgICAgIDx0aXRsZXM+DQogICAgICAgICAgICAgICAgPHRpdGxlPlN1cHBsZW1lbnRhcnkgZmlsZSAzLjwvdGl0bGU+DQogICAgICAgICAgICAgICAgPHN1YnRpdGxlPlF1YWxpdHkgY29udHJvbCBmaWxlcyBmb3IgdGhlIExlciBzZWN0aW9ucy48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNDwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0QzLWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5TdXBwbGVtZW50YXJ5IGZpbGUgNC48L3RpdGxlPg0KICAgICAgICAgICAgICAgIDxzdWJ0aXRsZT5UaGUgbm9ybWFsaXplZCB2YWx1ZXMgb2YgdGhlIHBoZW5vcHJpbnRzIChGaWd1cmUgMkIpIHVzZWQgZm9yIFBDQS48L3N1YnRpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9ImFwcGxpY2F0aW9uL3ZuZC5vcGVueG1sZm9ybWF0cy1vZmZpY2Vkb2N1bWVudC5zcHJlYWRzaGVldG1sLnNoZWV0IiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNTwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3L2ZpZ3VyZXMjU0Q0LWRhdGE8L3Jlc291cmNlPg0KICAgICAgICAgICAgICA8L2RvaV9kYXRhPg0KICAgICAgICAgICAgPC9jb21wb25lbnQ+DQogICAgICAgICAgICA8Y29tcG9uZW50IHBhcmVudF9yZWxhdGlvbj0iaXNQYXJ0T2YiPg0KICAgICAgICAgICAgICA8dGl0bGVzPg0KICAgICAgICAgICAgICAgIDx0aXRsZT5EZWNpc2lvbiBsZXR0ZXI8L3RpdGxlPg0KICAgICAgICAgICAgICA8L3RpdGxlcz4NCiAgICAgICAgICAgICAgPGZvcm1hdCBtaW1lX3R5cGU9InRleHQvcGxhaW4iIC8+DQogICAgICAgICAgICAgIDxkb2lfZGF0YT4NCiAgICAgICAgICAgICAgICA8ZG9pPjEwLjc1NTQvZUxpZmUuMDE1NjcuMDE2PC9kb2k+DQogICAgICAgICAgICAgICAgPHJlc291cmNlPmh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvMDE1NjcjU0ExPC9yZXNvdXJjZT4NCiAgICAgICAgICAgICAgPC9kb2lfZGF0YT4NCiAgICAgICAgICAgIDwvY29tcG9uZW50Pg0KICAgICAgICAgICAgPGNvbXBvbmVudCBwYXJlbnRfcmVsYXRpb249ImlzUGFydE9mIj4NCiAgICAgICAgICAgICAgPHRpdGxlcz4NCiAgICAgICAgICAgICAgICA8dGl0bGU+QXV0aG9yIHJlc3BvbnNlPC90aXRsZT4NCiAgICAgICAgICAgICAgPC90aXRsZXM+DQogICAgICAgICAgICAgIDxmb3JtYXQgbWltZV90eXBlPSJ0ZXh0L3BsYWluIiAvPg0KICAgICAgICAgICAgICA8ZG9pX2RhdGE+DQogICAgICAgICAgICAgICAgPGRvaT4xMC43NTU0L2VMaWZlLjAxNTY3LjAxNzwvZG9pPg0KICAgICAgICAgICAgICAgIDxyZXNvdXJjZT5odHRwczovL2VsaWZlc2NpZW5jZXMub3JnL2FydGljbGVzLzAxNTY3I1NBMjwvcmVzb3VyY2U+DQogICAgICAgICAgICAgIDwvZG9pX2RhdGE+DQogICAgICAgICAgICA8L2NvbXBvbmVudD4NCiAgICAgICAgICA8L2NvbXBvbmVudF9saXN0Pg0KICAgICAgICA8L2pvdXJuYWxfYXJ0aWNsZT4NCiAgICAgIDwvam91cm5hbD4NCiAgICA8L2Nyb3NzcmVmPg0KICA8L2RvaV9yZWNvcmQ+DQo8L2RvaV9yZWNvcmRzPg== + http_version: + recorded_at: Fri, 07 Dec 2018 18:37:47 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/returns_status_code_201.yml b/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/returns_status_code_201.yml new file mode 100644 index 000000000..87116cfa3 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/returns_status_code_201.yml @@ -0,0 +1,99 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7272 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 17:59:13 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - 735adba6-fb5e-4444-be96-bb0a75401b71 + Etag: + - W/"434d63d02889e47275389a930375c569" + X-Runtime: + - '0.023537' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7272","type":"prefixes","attributes":{"registration-agency":"DataCite","created":"2012-03-15T09:47:35.000Z","updated":null},"relationships":{"clients":{"data":[{"id":"cdl.ucsfctsi","type":"clients"}]},"providers":{"data":[{"id":"cdl","type":"providers"}]}}},"included":[{"id":"cdl.ucsfctsi","type":"clients","attributes":{"name":"UCSF + Clinical & Translational Science Institute (CTSI)","symbol":"CDL.UCSFCTSI","year":2012,"contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","domains":"*","url":null,"created":"2012-07-10T20:59:53.000Z","updated":"2018-08-26T02:35:12.000Z","is-active":true,"has-password":true},"relationships":{"provider":{"data":{"id":"cdl","type":"providers"}},"prefixes":{"data":[{"id":"10.5072","type":"prefixes"},{"id":"10.7272","type":"prefixes"}]}}},{"id":"cdl","type":"providers","attributes":{"name":"California + Digital Library","symbol":"CDL","website":"http://ezid.cdlib.org","contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","phone":null,"description":"California + Digital Library (CDL) handles scholarly information at every stage of its + life. CDL provides technology and expertise to help the University of California + (UC) collect, publish, access, and preserve its full range of information + resources. CDL partners with organizations outside the UCs on far-reaching + problems.\n\nCDL offers DataCite DOIs to University of California scholars + and researchers via the EZID service. EZID is also available as a general + identifier service to help educational, non-profit, governmental and commercial + clients create and manage globally unique identifiers for data and other sources.","region":"AMER","country":"US","logo-url":"https://assets.datacite.org/images/members/cdl.png","organization-type":"academic_institution","focus-area":"general","is-active":true,"has-password":true,"joined":"2009-12-01","created":"2010-01-01T00:00:00.000Z","updated":"2018-10-24T20:47:51.000Z"},"relationships":{"prefixes":{"data":[{"id":"10.5062","type":"prefixes"},{"id":"10.5063","type":"prefixes"},{"id":"10.5065","type":"prefixes"},{"id":"10.5068","type":"prefixes"},{"id":"10.5069","type":"prefixes"},{"id":"10.5070","type":"prefixes"},{"id":"10.5072","type":"prefixes"},{"id":"10.6071","type":"prefixes"},{"id":"10.6072","type":"prefixes"},{"id":"10.6074","type":"prefixes"},{"id":"10.6075","type":"prefixes"},{"id":"10.6076","type":"prefixes"},{"id":"10.6078","type":"prefixes"},{"id":"10.6079","type":"prefixes"},{"id":"10.6080","type":"prefixes"},{"id":"10.6081","type":"prefixes"},{"id":"10.6085","type":"prefixes"},{"id":"10.6086","type":"prefixes"},{"id":"10.7265","type":"prefixes"},{"id":"10.7268","type":"prefixes"},{"id":"10.7269","type":"prefixes"},{"id":"10.7270","type":"prefixes"},{"id":"10.7271","type":"prefixes"},{"id":"10.7272","type":"prefixes"},{"id":"10.7276","type":"prefixes"},{"id":"10.7279","type":"prefixes"},{"id":"10.7280","type":"prefixes"},{"id":"10.7281","type":"prefixes"},{"id":"10.7282","type":"prefixes"},{"id":"10.7284","type":"prefixes"},{"id":"10.7285","type":"prefixes"},{"id":"10.7286","type":"prefixes"},{"id":"10.7288","type":"prefixes"},{"id":"10.7291","type":"prefixes"},{"id":"10.7292","type":"prefixes"},{"id":"10.7293","type":"prefixes"},{"id":"10.7295","type":"prefixes"},{"id":"10.7296","type":"prefixes"},{"id":"10.7297","type":"prefixes"},{"id":"10.7299","type":"prefixes"},{"id":"10.7300","type":"prefixes"},{"id":"10.4246","type":"prefixes"},{"id":"10.7908","type":"prefixes"},{"id":"10.7911","type":"prefixes"},{"id":"10.7913","type":"prefixes"},{"id":"10.7914","type":"prefixes"},{"id":"10.7916","type":"prefixes"},{"id":"10.7918","type":"prefixes"},{"id":"10.7919","type":"prefixes"},{"id":"10.7920","type":"prefixes"},{"id":"10.7921","type":"prefixes"},{"id":"10.7922","type":"prefixes"},{"id":"10.7925","type":"prefixes"},{"id":"10.7927","type":"prefixes"},{"id":"10.7928","type":"prefixes"},{"id":"10.7929","type":"prefixes"},{"id":"10.7932","type":"prefixes"},{"id":"10.7933","type":"prefixes"},{"id":"10.7934","type":"prefixes"},{"id":"10.7939","type":"prefixes"},{"id":"10.7940","type":"prefixes"},{"id":"10.7941","type":"prefixes"},{"id":"10.7942","type":"prefixes"},{"id":"10.7943","type":"prefixes"},{"id":"10.7944","type":"prefixes"},{"id":"10.7945","type":"prefixes"},{"id":"10.7946","type":"prefixes"},{"id":"10.13022","type":"prefixes"},{"id":"10.13026","type":"prefixes"},{"id":"10.13025","type":"prefixes"},{"id":"10.15147","type":"prefixes"},{"id":"10.15146","type":"prefixes"},{"id":"10.15142","type":"prefixes"},{"id":"10.15144","type":"prefixes"},{"id":"10.15145","type":"prefixes"},{"id":"10.15140","type":"prefixes"},{"id":"10.15141","type":"prefixes"},{"id":"10.15139","type":"prefixes"},{"id":"10.1184","type":"prefixes"},{"id":"10.15784","type":"prefixes"},{"id":"10.15779","type":"prefixes"},{"id":"10.15780","type":"prefixes"},{"id":"10.15781","type":"prefixes"},{"id":"10.15782","type":"prefixes"},{"id":"10.17612","type":"prefixes"},{"id":"10.17610","type":"prefixes"},{"id":"10.17611","type":"prefixes"},{"id":"10.17614","type":"prefixes"},{"id":"10.17615","type":"prefixes"},{"id":"10.17602","type":"prefixes"},{"id":"10.17603","type":"prefixes"},{"id":"10.17908","type":"prefixes"},{"id":"10.17916","type":"prefixes"},{"id":"10.17915","type":"prefixes"},{"id":"10.17918","type":"prefixes"},{"id":"10.17919","type":"prefixes"},{"id":"10.17911","type":"prefixes"},{"id":"10.17913","type":"prefixes"},{"id":"10.17920","type":"prefixes"},{"id":"10.18123","type":"prefixes"},{"id":"10.18119","type":"prefixes"},{"id":"10.18118","type":"prefixes"},{"id":"10.18117","type":"prefixes"},{"id":"10.18115","type":"prefixes"},{"id":"10.18431","type":"prefixes"},{"id":"10.18436","type":"prefixes"},{"id":"10.18437","type":"prefixes"},{"id":"10.18439","type":"prefixes"},{"id":"10.18737","type":"prefixes"},{"id":"10.18736","type":"prefixes"},{"id":"10.18739","type":"prefixes"},{"id":"10.18734","type":"prefixes"},{"id":"10.20353","type":"prefixes"},{"id":"10.20354","type":"prefixes"},{"id":"10.20352","type":"prefixes"},{"id":"10.20357","type":"prefixes"},{"id":"10.20358","type":"prefixes"},{"id":"10.20359","type":"prefixes"},{"id":"10.21228","type":"prefixes"},{"id":"10.21229","type":"prefixes"},{"id":"10.21224","type":"prefixes"},{"id":"10.21222","type":"prefixes"},{"id":"10.21223","type":"prefixes"},{"id":"10.21221","type":"prefixes"},{"id":"10.21237","type":"prefixes"},{"id":"10.21238","type":"prefixes"},{"id":"10.21239","type":"prefixes"},{"id":"10.21236","type":"prefixes"},{"id":"10.21430","type":"prefixes"},{"id":"10.21433","type":"prefixes"},{"id":"10.21431","type":"prefixes"},{"id":"10.21421","type":"prefixes"},{"id":"10.21422","type":"prefixes"},{"id":"10.21424","type":"prefixes"},{"id":"10.21425","type":"prefixes"},{"id":"10.21426","type":"prefixes"},{"id":"10.21427","type":"prefixes"},{"id":"10.21428","type":"prefixes"},{"id":"10.21418","type":"prefixes"},{"id":"10.21416","type":"prefixes"},{"id":"10.21414","type":"prefixes"},{"id":"10.21972","type":"prefixes"},{"id":"10.21973","type":"prefixes"},{"id":"10.21975","type":"prefixes"},{"id":"10.21976","type":"prefixes"},{"id":"10.21977","type":"prefixes"},{"id":"10.21978","type":"prefixes"},{"id":"10.21990","type":"prefixes"},{"id":"10.21983","type":"prefixes"},{"id":"10.21980","type":"prefixes"},{"id":"10.21986","type":"prefixes"},{"id":"10.5195","type":"prefixes"},{"id":"10.25334","type":"prefixes"},{"id":"10.25337","type":"prefixes"},{"id":"10.25338","type":"prefixes"},{"id":"10.25342","type":"prefixes"},{"id":"10.25349","type":"prefixes"},{"id":"10.25348","type":"prefixes"},{"id":"10.25347","type":"prefixes"},{"id":"10.25346","type":"prefixes"},{"id":"10.25345","type":"prefixes"},{"id":"10.25344","type":"prefixes"},{"id":"10.25352","type":"prefixes"},{"id":"10.25350","type":"prefixes"},{"id":"10.25351","type":"prefixes"},{"id":"10.26081","type":"prefixes"}]}}}]}' + http_version: + recorded_at: Fri, 07 Dec 2018 17:59:13 GMT +- request: + method: get + uri: https://search.test.datacite.org/api?fl=doi,url,xml,state,allocator_symbol,datacentre_symbol,media,minted,updated&q=doi:10.7272/q6g15xs4&wt=json + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 17:59:13 GMT + Content-Type: + - application/json;charset=UTF-8 + Connection: + - keep-alive + Server: + - nginx/1.10.3 (Ubuntu) + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + Access-Control-Expose-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + body: + encoding: ASCII-8BIT + string: '{"responseHeader":{"status":0,"QTime":0},"response":{"numFound":1,"start":0,"docs":[{"datacentre_symbol":"CDL.UCSFCTSI","url":"https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4","xml":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJlc291cmNlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyBodHRwOi8vc2NoZW1hLmRhdGFjaXRlLm9yZy9tZXRhL2tlcm5lbC0zL21ldGFkYXRhLnhzZCI+CiAgPGlkZW50aWZpZXIgaWRlbnRpZmllclR5cGU9IkRPSSI+MTAuNzI3Mi9RNkcxNVhTNDwvaWRlbnRpZmllcj4KICA8Y3JlYXRvcnM+CiAgICA8Y3JlYXRvcj4KICAgICAgPGNyZWF0b3JOYW1lPlJvZHJpZ3VleiwgUm9iZXJ0PC9jcmVhdG9yTmFtZT4KICAgICAgPGFmZmlsaWF0aW9uPlVDIFNhbiBGcmFuY2lzY288L2FmZmlsaWF0aW9uPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZT5Nb3dlciwgV2lsbGlhbTwvY3JlYXRvck5hbWU+CiAgICAgIDxhZmZpbGlhdGlvbj5VQ0xBPC9hZmZpbGlhdGlvbj4KICAgIDwvY3JlYXRvcj4KICA8L2NyZWF0b3JzPgogIDx0aXRsZXM+CiAgICA8dGl0bGU+TkVYVVMgSGVhZCBDVDwvdGl0bGU+CiAgPC90aXRsZXM+CiAgPHB1Ymxpc2hlcj5VQyBTYW4gRnJhbmNpc2NvPC9wdWJsaXNoZXI+CiAgPHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+CiAgPGxhbmd1YWdlPmVuPC9sYW5ndWFnZT4KICA8cmVzb3VyY2VUeXBlIHJlc291cmNlVHlwZUdlbmVyYWw9IkRhdGFzZXQiLz4KICA8c2l6ZXM+CiAgICA8c2l6ZT4xNDk1MjA0IGJ5dGVzPC9zaXplPgogIDwvc2l6ZXM+CiAgPHZlcnNpb24+MTwvdmVyc2lvbj4KICA8cmlnaHRzTGlzdD4KICAgIDxyaWdodHMgcmlnaHRzVVJJPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyI+Q3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiA0LjAgSW50ZXJuYXRpb25hbCAoQ0MgQlkgNC4wKTwvcmlnaHRzPgogIDwvcmlnaHRzTGlzdD4KICA8ZGVzY3JpcHRpb25zPgogICAgPGRlc2NyaXB0aW9uIGRlc2NyaXB0aW9uVHlwZT0iQWJzdHJhY3QiPgogICAgICBCYWNrZ3JvdW5kIENsaW5pY2lhbnMsIGFmcmFpZCBvZiBtaXNzaW5nIGludHJhY3JhbmlhbCBpbmp1cmllcywgbGliZXJhbGx5CiAgICAgIG9idGFpbiBjb21wdXRlZCB0b21vZ3JhcGhpYyAoQ1QpIGhlYWQgaW1hZ2luZyBpbiBibHVudCB0cmF1bWEgcGF0aWVudHMuCiAgICAgIFByaW9yIHdvcmsgc3VnZ2VzdHMgdGhhdCBjbGluaWNhbCBjcml0ZXJpYSAoTkVYVVMgSGVhZCBDVCBkZWNpc2lvbgogICAgICBpbnN0cnVtZW50KSBjYW4gcmVsaWFibHkgaWRlbnRpZnkgcGF0aWVudHMgd2l0aCBpbXBvcnRhbnQgaW5qdXJpZXMsIHdoaWxlCiAgICAgIGV4Y2x1ZGluZyBpbmp1cnksIGFuZCB0aGUgbmVlZCBmb3IgaW1hZ2luZyBpbiBtYW55IHBhdGllbnRzLiBNZXRob2RzIFdlCiAgICAgIGNvbmR1Y3RlZCBhIHByb3NwZWN0aXZlIG9ic2VydmF0aW9uYWwgc3R1ZHkgb2YgdGhlIE5FWFVTIEhlYWQgQ1QgZGVjaXNpb24KICAgICAgaW5zdHJ1bWVudCAoREkpIHRoYXQgcmVxdWlyZXMgcGF0aWVudHMgdG8gbWVldCBlaWdodCBjcml0ZXJpYSB0byBhY2hpZXZlCiAgICAgIOKAnGxvdy1yaXNr4oCdIGNsYXNzaWZpY2F0aW9uLiBXZSBleGFtaW5lZCB0aGUgaW5zdHJ1bWVudOKAmXMgcGVyZm9ybWFuY2UgaW4KICAgICAgaWRlbnRpZnlpbmcgcGF0aWVudHMgcmVxdWlyaW5nIG5ldXJvbG9naWNhbCBpbnRlcnZlbnRpb24gZnJvbSBhbW9uZyBhCiAgICAgIGNvaG9ydCBvZiAxMSw3NzAgYmx1bnQgaGVhZCBpbmp1cnkgcGF0aWVudHMuIFJlc3VsdHMgVGhlIE5FWFVTIEhlYWQgQ1QgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA0MjAgb2YgNDIwIHBhdGllbnRzIHJlcXVpcmluZyBuZXVyb2xvZ2ljYWwKICAgICAgaW50ZXJ2ZW50aW9uIChzZW5zaXRpdml0eSwgMTAwLjAlIFs5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBbQ0ldOiA5OS4xJSDigJMKICAgICAgMTAwLjAlXSkuIFRoZSBpbnN0cnVtZW50IGFzc2lnbmVkIGxvdy1yaXNrIHN0YXR1cyB0byAyLDgyMyBvZiAxMSwzNTAKICAgICAgcGF0aWVudHMgd2hvIGRpZCBub3QgcmVxdWlyZSBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChzcGVjaWZpY2l0eSwgMjQuOSUKICAgICAgWzk1JSBDSTogMjQuMSUgLSAyNS43JV0pLiBOb25lIG9mIHRoZSAyLDgyMyBsb3ctcmlzayBwYXRpZW50cyByZXF1aXJlZAogICAgICBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChOUFYsIDEwMC4wJSBbOTUlIENJOiA5OS45JSAtIDEwMC4wJV0pLiBUaGUgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA3NTkgb2YgNzY3IHBhdGllbnRzIHdpdGggc2lnbmlmaWNhbnQKICAgICAgaW50cmFjcmFuaWFsIGluanVyaWVzIChzZW5zaXRpdml0eSwgOTkuMCUgWzk1JSBDSTogOTguMCUgLSA5OS42JV0pLiBUaGUKICAgICAgaW5zdHJ1bWVudCBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgdG8gMiw4MTUgb2YgMTEsMDAzIHBhdGllbnRzIHdobyBkaWQKICAgICAgbm90IGhhdmUgc2lnbmlmaWNhbnQgaW5qdXJpZXMgKHNwZWNpZmljaXR5LCAyNS42JSBbOTUlIENJOiAyNC44JSAtCiAgICAgIDI2LjQlXSkuIFNpZ25pZmljYW50IGluanVyaWVzIHdlcmUgYWJzZW50IGluIDIsODE1IG9mIHRoZSAyLDgyMyBwYXRpZW50cwogICAgICBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgKE5QViwgOTkuNyUgWzk1JSBDSTogOTkuNCUgLSA5OS45JV0pLiBDb25jbHVzaW9ucwogICAgICBUaGUgTkVYVVMgSGVhZCBDVCBESSByZWxpYWJseSBpZGVudGlmaWVzIGJsdW50IHRyYXVtYSBwYXRpZW50cyB3aG8gcmVxdWlyZQogICAgICBoZWFkIENUIGltYWdpbmcsIGFuZCBjb3VsZCBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSB1c2Ugb2YgQ1QgaW1hZ2luZy4KICAgIDwvZGVzY3JpcHRpb24+CiAgICA8ZGVzY3JpcHRpb24gZGVzY3JpcHRpb25UeXBlPSJNZXRob2RzIj5Qcm9zcGVjdGl2ZSBtdWx0aWNlbnRlcjwvZGVzY3JpcHRpb24+CiAgPC9kZXNjcmlwdGlvbnM+CiAgPGdlb0xvY2F0aW9ucz4KICAgIDxnZW9Mb2NhdGlvbj4KICAgICAgPGdlb0xvY2F0aW9uUG9pbnQ+MzcuMjUwMjIgLTExOS43NTEyNjwvZ2VvTG9jYXRpb25Qb2ludD4KICAgICAgPGdlb0xvY2F0aW9uUGxhY2U+Q2FsaWZvcm5pYSwgVVNBPC9nZW9Mb2NhdGlvblBsYWNlPgogICAgPC9nZW9Mb2NhdGlvbj4KICA8L2dlb0xvY2F0aW9ucz4KPC9yZXNvdXJjZT4=","allocator_symbol":"CDL","minted":"2018-12-07T09:48:20Z","state":"findable","updated":"2018-12-07T09:48:20Z","doi":"10.7272/Q6G15XS4"}]}} + +' + http_version: + recorded_at: Fri, 07 Dec 2018 17:59:13 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/sets_state_to_findable.yml b/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/sets_state_to_findable.yml new file mode 100644 index 000000000..8b418f0df --- /dev/null +++ b/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/sets_state_to_findable.yml @@ -0,0 +1,99 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7272 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 17:59:14 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - d905d5d4-58e6-437f-a962-591bdf5c46b1 + Etag: + - W/"434d63d02889e47275389a930375c569" + X-Runtime: + - '0.022882' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7272","type":"prefixes","attributes":{"registration-agency":"DataCite","created":"2012-03-15T09:47:35.000Z","updated":null},"relationships":{"clients":{"data":[{"id":"cdl.ucsfctsi","type":"clients"}]},"providers":{"data":[{"id":"cdl","type":"providers"}]}}},"included":[{"id":"cdl.ucsfctsi","type":"clients","attributes":{"name":"UCSF + Clinical & Translational Science Institute (CTSI)","symbol":"CDL.UCSFCTSI","year":2012,"contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","domains":"*","url":null,"created":"2012-07-10T20:59:53.000Z","updated":"2018-08-26T02:35:12.000Z","is-active":true,"has-password":true},"relationships":{"provider":{"data":{"id":"cdl","type":"providers"}},"prefixes":{"data":[{"id":"10.5072","type":"prefixes"},{"id":"10.7272","type":"prefixes"}]}}},{"id":"cdl","type":"providers","attributes":{"name":"California + Digital Library","symbol":"CDL","website":"http://ezid.cdlib.org","contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","phone":null,"description":"California + Digital Library (CDL) handles scholarly information at every stage of its + life. CDL provides technology and expertise to help the University of California + (UC) collect, publish, access, and preserve its full range of information + resources. CDL partners with organizations outside the UCs on far-reaching + problems.\n\nCDL offers DataCite DOIs to University of California scholars + and researchers via the EZID service. EZID is also available as a general + identifier service to help educational, non-profit, governmental and commercial + clients create and manage globally unique identifiers for data and other sources.","region":"AMER","country":"US","logo-url":"https://assets.datacite.org/images/members/cdl.png","organization-type":"academic_institution","focus-area":"general","is-active":true,"has-password":true,"joined":"2009-12-01","created":"2010-01-01T00:00:00.000Z","updated":"2018-10-24T20:47:51.000Z"},"relationships":{"prefixes":{"data":[{"id":"10.5062","type":"prefixes"},{"id":"10.5063","type":"prefixes"},{"id":"10.5065","type":"prefixes"},{"id":"10.5068","type":"prefixes"},{"id":"10.5069","type":"prefixes"},{"id":"10.5070","type":"prefixes"},{"id":"10.5072","type":"prefixes"},{"id":"10.6071","type":"prefixes"},{"id":"10.6072","type":"prefixes"},{"id":"10.6074","type":"prefixes"},{"id":"10.6075","type":"prefixes"},{"id":"10.6076","type":"prefixes"},{"id":"10.6078","type":"prefixes"},{"id":"10.6079","type":"prefixes"},{"id":"10.6080","type":"prefixes"},{"id":"10.6081","type":"prefixes"},{"id":"10.6085","type":"prefixes"},{"id":"10.6086","type":"prefixes"},{"id":"10.7265","type":"prefixes"},{"id":"10.7268","type":"prefixes"},{"id":"10.7269","type":"prefixes"},{"id":"10.7270","type":"prefixes"},{"id":"10.7271","type":"prefixes"},{"id":"10.7272","type":"prefixes"},{"id":"10.7276","type":"prefixes"},{"id":"10.7279","type":"prefixes"},{"id":"10.7280","type":"prefixes"},{"id":"10.7281","type":"prefixes"},{"id":"10.7282","type":"prefixes"},{"id":"10.7284","type":"prefixes"},{"id":"10.7285","type":"prefixes"},{"id":"10.7286","type":"prefixes"},{"id":"10.7288","type":"prefixes"},{"id":"10.7291","type":"prefixes"},{"id":"10.7292","type":"prefixes"},{"id":"10.7293","type":"prefixes"},{"id":"10.7295","type":"prefixes"},{"id":"10.7296","type":"prefixes"},{"id":"10.7297","type":"prefixes"},{"id":"10.7299","type":"prefixes"},{"id":"10.7300","type":"prefixes"},{"id":"10.4246","type":"prefixes"},{"id":"10.7908","type":"prefixes"},{"id":"10.7911","type":"prefixes"},{"id":"10.7913","type":"prefixes"},{"id":"10.7914","type":"prefixes"},{"id":"10.7916","type":"prefixes"},{"id":"10.7918","type":"prefixes"},{"id":"10.7919","type":"prefixes"},{"id":"10.7920","type":"prefixes"},{"id":"10.7921","type":"prefixes"},{"id":"10.7922","type":"prefixes"},{"id":"10.7925","type":"prefixes"},{"id":"10.7927","type":"prefixes"},{"id":"10.7928","type":"prefixes"},{"id":"10.7929","type":"prefixes"},{"id":"10.7932","type":"prefixes"},{"id":"10.7933","type":"prefixes"},{"id":"10.7934","type":"prefixes"},{"id":"10.7939","type":"prefixes"},{"id":"10.7940","type":"prefixes"},{"id":"10.7941","type":"prefixes"},{"id":"10.7942","type":"prefixes"},{"id":"10.7943","type":"prefixes"},{"id":"10.7944","type":"prefixes"},{"id":"10.7945","type":"prefixes"},{"id":"10.7946","type":"prefixes"},{"id":"10.13022","type":"prefixes"},{"id":"10.13026","type":"prefixes"},{"id":"10.13025","type":"prefixes"},{"id":"10.15147","type":"prefixes"},{"id":"10.15146","type":"prefixes"},{"id":"10.15142","type":"prefixes"},{"id":"10.15144","type":"prefixes"},{"id":"10.15145","type":"prefixes"},{"id":"10.15140","type":"prefixes"},{"id":"10.15141","type":"prefixes"},{"id":"10.15139","type":"prefixes"},{"id":"10.1184","type":"prefixes"},{"id":"10.15784","type":"prefixes"},{"id":"10.15779","type":"prefixes"},{"id":"10.15780","type":"prefixes"},{"id":"10.15781","type":"prefixes"},{"id":"10.15782","type":"prefixes"},{"id":"10.17612","type":"prefixes"},{"id":"10.17610","type":"prefixes"},{"id":"10.17611","type":"prefixes"},{"id":"10.17614","type":"prefixes"},{"id":"10.17615","type":"prefixes"},{"id":"10.17602","type":"prefixes"},{"id":"10.17603","type":"prefixes"},{"id":"10.17908","type":"prefixes"},{"id":"10.17916","type":"prefixes"},{"id":"10.17915","type":"prefixes"},{"id":"10.17918","type":"prefixes"},{"id":"10.17919","type":"prefixes"},{"id":"10.17911","type":"prefixes"},{"id":"10.17913","type":"prefixes"},{"id":"10.17920","type":"prefixes"},{"id":"10.18123","type":"prefixes"},{"id":"10.18119","type":"prefixes"},{"id":"10.18118","type":"prefixes"},{"id":"10.18117","type":"prefixes"},{"id":"10.18115","type":"prefixes"},{"id":"10.18431","type":"prefixes"},{"id":"10.18436","type":"prefixes"},{"id":"10.18437","type":"prefixes"},{"id":"10.18439","type":"prefixes"},{"id":"10.18737","type":"prefixes"},{"id":"10.18736","type":"prefixes"},{"id":"10.18739","type":"prefixes"},{"id":"10.18734","type":"prefixes"},{"id":"10.20353","type":"prefixes"},{"id":"10.20354","type":"prefixes"},{"id":"10.20352","type":"prefixes"},{"id":"10.20357","type":"prefixes"},{"id":"10.20358","type":"prefixes"},{"id":"10.20359","type":"prefixes"},{"id":"10.21228","type":"prefixes"},{"id":"10.21229","type":"prefixes"},{"id":"10.21224","type":"prefixes"},{"id":"10.21222","type":"prefixes"},{"id":"10.21223","type":"prefixes"},{"id":"10.21221","type":"prefixes"},{"id":"10.21237","type":"prefixes"},{"id":"10.21238","type":"prefixes"},{"id":"10.21239","type":"prefixes"},{"id":"10.21236","type":"prefixes"},{"id":"10.21430","type":"prefixes"},{"id":"10.21433","type":"prefixes"},{"id":"10.21431","type":"prefixes"},{"id":"10.21421","type":"prefixes"},{"id":"10.21422","type":"prefixes"},{"id":"10.21424","type":"prefixes"},{"id":"10.21425","type":"prefixes"},{"id":"10.21426","type":"prefixes"},{"id":"10.21427","type":"prefixes"},{"id":"10.21428","type":"prefixes"},{"id":"10.21418","type":"prefixes"},{"id":"10.21416","type":"prefixes"},{"id":"10.21414","type":"prefixes"},{"id":"10.21972","type":"prefixes"},{"id":"10.21973","type":"prefixes"},{"id":"10.21975","type":"prefixes"},{"id":"10.21976","type":"prefixes"},{"id":"10.21977","type":"prefixes"},{"id":"10.21978","type":"prefixes"},{"id":"10.21990","type":"prefixes"},{"id":"10.21983","type":"prefixes"},{"id":"10.21980","type":"prefixes"},{"id":"10.21986","type":"prefixes"},{"id":"10.5195","type":"prefixes"},{"id":"10.25334","type":"prefixes"},{"id":"10.25337","type":"prefixes"},{"id":"10.25338","type":"prefixes"},{"id":"10.25342","type":"prefixes"},{"id":"10.25349","type":"prefixes"},{"id":"10.25348","type":"prefixes"},{"id":"10.25347","type":"prefixes"},{"id":"10.25346","type":"prefixes"},{"id":"10.25345","type":"prefixes"},{"id":"10.25344","type":"prefixes"},{"id":"10.25352","type":"prefixes"},{"id":"10.25350","type":"prefixes"},{"id":"10.25351","type":"prefixes"},{"id":"10.26081","type":"prefixes"}]}}}]}' + http_version: + recorded_at: Fri, 07 Dec 2018 17:59:14 GMT +- request: + method: get + uri: https://search.test.datacite.org/api?fl=doi,url,xml,state,allocator_symbol,datacentre_symbol,media,minted,updated&q=doi:10.7272/q6g15xs4&wt=json + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 17:59:14 GMT + Content-Type: + - application/json;charset=UTF-8 + Connection: + - keep-alive + Server: + - nginx/1.10.3 (Ubuntu) + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + Access-Control-Expose-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + body: + encoding: ASCII-8BIT + string: '{"responseHeader":{"status":0,"QTime":0},"response":{"numFound":1,"start":0,"docs":[{"datacentre_symbol":"CDL.UCSFCTSI","url":"https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4","xml":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJlc291cmNlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyBodHRwOi8vc2NoZW1hLmRhdGFjaXRlLm9yZy9tZXRhL2tlcm5lbC0zL21ldGFkYXRhLnhzZCI+CiAgPGlkZW50aWZpZXIgaWRlbnRpZmllclR5cGU9IkRPSSI+MTAuNzI3Mi9RNkcxNVhTNDwvaWRlbnRpZmllcj4KICA8Y3JlYXRvcnM+CiAgICA8Y3JlYXRvcj4KICAgICAgPGNyZWF0b3JOYW1lPlJvZHJpZ3VleiwgUm9iZXJ0PC9jcmVhdG9yTmFtZT4KICAgICAgPGFmZmlsaWF0aW9uPlVDIFNhbiBGcmFuY2lzY288L2FmZmlsaWF0aW9uPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZT5Nb3dlciwgV2lsbGlhbTwvY3JlYXRvck5hbWU+CiAgICAgIDxhZmZpbGlhdGlvbj5VQ0xBPC9hZmZpbGlhdGlvbj4KICAgIDwvY3JlYXRvcj4KICA8L2NyZWF0b3JzPgogIDx0aXRsZXM+CiAgICA8dGl0bGU+TkVYVVMgSGVhZCBDVDwvdGl0bGU+CiAgPC90aXRsZXM+CiAgPHB1Ymxpc2hlcj5VQyBTYW4gRnJhbmNpc2NvPC9wdWJsaXNoZXI+CiAgPHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+CiAgPGxhbmd1YWdlPmVuPC9sYW5ndWFnZT4KICA8cmVzb3VyY2VUeXBlIHJlc291cmNlVHlwZUdlbmVyYWw9IkRhdGFzZXQiLz4KICA8c2l6ZXM+CiAgICA8c2l6ZT4xNDk1MjA0IGJ5dGVzPC9zaXplPgogIDwvc2l6ZXM+CiAgPHZlcnNpb24+MTwvdmVyc2lvbj4KICA8cmlnaHRzTGlzdD4KICAgIDxyaWdodHMgcmlnaHRzVVJJPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyI+Q3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiA0LjAgSW50ZXJuYXRpb25hbCAoQ0MgQlkgNC4wKTwvcmlnaHRzPgogIDwvcmlnaHRzTGlzdD4KICA8ZGVzY3JpcHRpb25zPgogICAgPGRlc2NyaXB0aW9uIGRlc2NyaXB0aW9uVHlwZT0iQWJzdHJhY3QiPgogICAgICBCYWNrZ3JvdW5kIENsaW5pY2lhbnMsIGFmcmFpZCBvZiBtaXNzaW5nIGludHJhY3JhbmlhbCBpbmp1cmllcywgbGliZXJhbGx5CiAgICAgIG9idGFpbiBjb21wdXRlZCB0b21vZ3JhcGhpYyAoQ1QpIGhlYWQgaW1hZ2luZyBpbiBibHVudCB0cmF1bWEgcGF0aWVudHMuCiAgICAgIFByaW9yIHdvcmsgc3VnZ2VzdHMgdGhhdCBjbGluaWNhbCBjcml0ZXJpYSAoTkVYVVMgSGVhZCBDVCBkZWNpc2lvbgogICAgICBpbnN0cnVtZW50KSBjYW4gcmVsaWFibHkgaWRlbnRpZnkgcGF0aWVudHMgd2l0aCBpbXBvcnRhbnQgaW5qdXJpZXMsIHdoaWxlCiAgICAgIGV4Y2x1ZGluZyBpbmp1cnksIGFuZCB0aGUgbmVlZCBmb3IgaW1hZ2luZyBpbiBtYW55IHBhdGllbnRzLiBNZXRob2RzIFdlCiAgICAgIGNvbmR1Y3RlZCBhIHByb3NwZWN0aXZlIG9ic2VydmF0aW9uYWwgc3R1ZHkgb2YgdGhlIE5FWFVTIEhlYWQgQ1QgZGVjaXNpb24KICAgICAgaW5zdHJ1bWVudCAoREkpIHRoYXQgcmVxdWlyZXMgcGF0aWVudHMgdG8gbWVldCBlaWdodCBjcml0ZXJpYSB0byBhY2hpZXZlCiAgICAgIOKAnGxvdy1yaXNr4oCdIGNsYXNzaWZpY2F0aW9uLiBXZSBleGFtaW5lZCB0aGUgaW5zdHJ1bWVudOKAmXMgcGVyZm9ybWFuY2UgaW4KICAgICAgaWRlbnRpZnlpbmcgcGF0aWVudHMgcmVxdWlyaW5nIG5ldXJvbG9naWNhbCBpbnRlcnZlbnRpb24gZnJvbSBhbW9uZyBhCiAgICAgIGNvaG9ydCBvZiAxMSw3NzAgYmx1bnQgaGVhZCBpbmp1cnkgcGF0aWVudHMuIFJlc3VsdHMgVGhlIE5FWFVTIEhlYWQgQ1QgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA0MjAgb2YgNDIwIHBhdGllbnRzIHJlcXVpcmluZyBuZXVyb2xvZ2ljYWwKICAgICAgaW50ZXJ2ZW50aW9uIChzZW5zaXRpdml0eSwgMTAwLjAlIFs5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBbQ0ldOiA5OS4xJSDigJMKICAgICAgMTAwLjAlXSkuIFRoZSBpbnN0cnVtZW50IGFzc2lnbmVkIGxvdy1yaXNrIHN0YXR1cyB0byAyLDgyMyBvZiAxMSwzNTAKICAgICAgcGF0aWVudHMgd2hvIGRpZCBub3QgcmVxdWlyZSBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChzcGVjaWZpY2l0eSwgMjQuOSUKICAgICAgWzk1JSBDSTogMjQuMSUgLSAyNS43JV0pLiBOb25lIG9mIHRoZSAyLDgyMyBsb3ctcmlzayBwYXRpZW50cyByZXF1aXJlZAogICAgICBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChOUFYsIDEwMC4wJSBbOTUlIENJOiA5OS45JSAtIDEwMC4wJV0pLiBUaGUgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA3NTkgb2YgNzY3IHBhdGllbnRzIHdpdGggc2lnbmlmaWNhbnQKICAgICAgaW50cmFjcmFuaWFsIGluanVyaWVzIChzZW5zaXRpdml0eSwgOTkuMCUgWzk1JSBDSTogOTguMCUgLSA5OS42JV0pLiBUaGUKICAgICAgaW5zdHJ1bWVudCBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgdG8gMiw4MTUgb2YgMTEsMDAzIHBhdGllbnRzIHdobyBkaWQKICAgICAgbm90IGhhdmUgc2lnbmlmaWNhbnQgaW5qdXJpZXMgKHNwZWNpZmljaXR5LCAyNS42JSBbOTUlIENJOiAyNC44JSAtCiAgICAgIDI2LjQlXSkuIFNpZ25pZmljYW50IGluanVyaWVzIHdlcmUgYWJzZW50IGluIDIsODE1IG9mIHRoZSAyLDgyMyBwYXRpZW50cwogICAgICBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgKE5QViwgOTkuNyUgWzk1JSBDSTogOTkuNCUgLSA5OS45JV0pLiBDb25jbHVzaW9ucwogICAgICBUaGUgTkVYVVMgSGVhZCBDVCBESSByZWxpYWJseSBpZGVudGlmaWVzIGJsdW50IHRyYXVtYSBwYXRpZW50cyB3aG8gcmVxdWlyZQogICAgICBoZWFkIENUIGltYWdpbmcsIGFuZCBjb3VsZCBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSB1c2Ugb2YgQ1QgaW1hZ2luZy4KICAgIDwvZGVzY3JpcHRpb24+CiAgICA8ZGVzY3JpcHRpb24gZGVzY3JpcHRpb25UeXBlPSJNZXRob2RzIj5Qcm9zcGVjdGl2ZSBtdWx0aWNlbnRlcjwvZGVzY3JpcHRpb24+CiAgPC9kZXNjcmlwdGlvbnM+CiAgPGdlb0xvY2F0aW9ucz4KICAgIDxnZW9Mb2NhdGlvbj4KICAgICAgPGdlb0xvY2F0aW9uUG9pbnQ+MzcuMjUwMjIgLTExOS43NTEyNjwvZ2VvTG9jYXRpb25Qb2ludD4KICAgICAgPGdlb0xvY2F0aW9uUGxhY2U+Q2FsaWZvcm5pYSwgVVNBPC9nZW9Mb2NhdGlvblBsYWNlPgogICAgPC9nZW9Mb2NhdGlvbj4KICA8L2dlb0xvY2F0aW9ucz4KPC9yZXNvdXJjZT4=","allocator_symbol":"CDL","minted":"2018-12-07T09:48:20Z","state":"findable","updated":"2018-12-07T09:48:20Z","doi":"10.7272/Q6G15XS4"}]}} + +' + http_version: + recorded_at: Fri, 07 Dec 2018 17:59:14 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/updates_the_record.yml b/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/updates_the_record.yml new file mode 100644 index 000000000..fa03bd817 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/dois/POST_/dois/datacite_url/updates_the_record.yml @@ -0,0 +1,99 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.datacite.org/prefixes/10.7272 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 17:59:12 GMT + Content-Type: + - application/json; charset=utf-8 + Connection: + - keep-alive + Status: + - 200 OK + X-Anonymous-Consumer: + - 'true' + Cache-Control: + - max-age=0, private, must-revalidate + Vary: + - Accept-Encoding, Origin + X-Request-Id: + - ba89f387-9fa1-4286-9203-b4e479c19dd0 + Etag: + - W/"434d63d02889e47275389a930375c569" + X-Runtime: + - '0.025620' + X-Powered-By: + - Phusion Passenger 6.0.0 + Server: + - nginx/1.15.7 + Phusion Passenger 6.0.0 + body: + encoding: ASCII-8BIT + string: '{"data":{"id":"10.7272","type":"prefixes","attributes":{"registration-agency":"DataCite","created":"2012-03-15T09:47:35.000Z","updated":null},"relationships":{"clients":{"data":[{"id":"cdl.ucsfctsi","type":"clients"}]},"providers":{"data":[{"id":"cdl","type":"providers"}]}}},"included":[{"id":"cdl.ucsfctsi","type":"clients","attributes":{"name":"UCSF + Clinical & Translational Science Institute (CTSI)","symbol":"CDL.UCSFCTSI","year":2012,"contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","domains":"*","url":null,"created":"2012-07-10T20:59:53.000Z","updated":"2018-08-26T02:35:12.000Z","is-active":true,"has-password":true},"relationships":{"provider":{"data":{"id":"cdl","type":"providers"}},"prefixes":{"data":[{"id":"10.5072","type":"prefixes"},{"id":"10.7272","type":"prefixes"}]}}},{"id":"cdl","type":"providers","attributes":{"name":"California + Digital Library","symbol":"CDL","website":"http://ezid.cdlib.org","contact-name":"EZID + Support Desk","contact-email":"ezid@ucop.edu","phone":null,"description":"California + Digital Library (CDL) handles scholarly information at every stage of its + life. CDL provides technology and expertise to help the University of California + (UC) collect, publish, access, and preserve its full range of information + resources. CDL partners with organizations outside the UCs on far-reaching + problems.\n\nCDL offers DataCite DOIs to University of California scholars + and researchers via the EZID service. EZID is also available as a general + identifier service to help educational, non-profit, governmental and commercial + clients create and manage globally unique identifiers for data and other sources.","region":"AMER","country":"US","logo-url":"https://assets.datacite.org/images/members/cdl.png","organization-type":"academic_institution","focus-area":"general","is-active":true,"has-password":true,"joined":"2009-12-01","created":"2010-01-01T00:00:00.000Z","updated":"2018-10-24T20:47:51.000Z"},"relationships":{"prefixes":{"data":[{"id":"10.5062","type":"prefixes"},{"id":"10.5063","type":"prefixes"},{"id":"10.5065","type":"prefixes"},{"id":"10.5068","type":"prefixes"},{"id":"10.5069","type":"prefixes"},{"id":"10.5070","type":"prefixes"},{"id":"10.5072","type":"prefixes"},{"id":"10.6071","type":"prefixes"},{"id":"10.6072","type":"prefixes"},{"id":"10.6074","type":"prefixes"},{"id":"10.6075","type":"prefixes"},{"id":"10.6076","type":"prefixes"},{"id":"10.6078","type":"prefixes"},{"id":"10.6079","type":"prefixes"},{"id":"10.6080","type":"prefixes"},{"id":"10.6081","type":"prefixes"},{"id":"10.6085","type":"prefixes"},{"id":"10.6086","type":"prefixes"},{"id":"10.7265","type":"prefixes"},{"id":"10.7268","type":"prefixes"},{"id":"10.7269","type":"prefixes"},{"id":"10.7270","type":"prefixes"},{"id":"10.7271","type":"prefixes"},{"id":"10.7272","type":"prefixes"},{"id":"10.7276","type":"prefixes"},{"id":"10.7279","type":"prefixes"},{"id":"10.7280","type":"prefixes"},{"id":"10.7281","type":"prefixes"},{"id":"10.7282","type":"prefixes"},{"id":"10.7284","type":"prefixes"},{"id":"10.7285","type":"prefixes"},{"id":"10.7286","type":"prefixes"},{"id":"10.7288","type":"prefixes"},{"id":"10.7291","type":"prefixes"},{"id":"10.7292","type":"prefixes"},{"id":"10.7293","type":"prefixes"},{"id":"10.7295","type":"prefixes"},{"id":"10.7296","type":"prefixes"},{"id":"10.7297","type":"prefixes"},{"id":"10.7299","type":"prefixes"},{"id":"10.7300","type":"prefixes"},{"id":"10.4246","type":"prefixes"},{"id":"10.7908","type":"prefixes"},{"id":"10.7911","type":"prefixes"},{"id":"10.7913","type":"prefixes"},{"id":"10.7914","type":"prefixes"},{"id":"10.7916","type":"prefixes"},{"id":"10.7918","type":"prefixes"},{"id":"10.7919","type":"prefixes"},{"id":"10.7920","type":"prefixes"},{"id":"10.7921","type":"prefixes"},{"id":"10.7922","type":"prefixes"},{"id":"10.7925","type":"prefixes"},{"id":"10.7927","type":"prefixes"},{"id":"10.7928","type":"prefixes"},{"id":"10.7929","type":"prefixes"},{"id":"10.7932","type":"prefixes"},{"id":"10.7933","type":"prefixes"},{"id":"10.7934","type":"prefixes"},{"id":"10.7939","type":"prefixes"},{"id":"10.7940","type":"prefixes"},{"id":"10.7941","type":"prefixes"},{"id":"10.7942","type":"prefixes"},{"id":"10.7943","type":"prefixes"},{"id":"10.7944","type":"prefixes"},{"id":"10.7945","type":"prefixes"},{"id":"10.7946","type":"prefixes"},{"id":"10.13022","type":"prefixes"},{"id":"10.13026","type":"prefixes"},{"id":"10.13025","type":"prefixes"},{"id":"10.15147","type":"prefixes"},{"id":"10.15146","type":"prefixes"},{"id":"10.15142","type":"prefixes"},{"id":"10.15144","type":"prefixes"},{"id":"10.15145","type":"prefixes"},{"id":"10.15140","type":"prefixes"},{"id":"10.15141","type":"prefixes"},{"id":"10.15139","type":"prefixes"},{"id":"10.1184","type":"prefixes"},{"id":"10.15784","type":"prefixes"},{"id":"10.15779","type":"prefixes"},{"id":"10.15780","type":"prefixes"},{"id":"10.15781","type":"prefixes"},{"id":"10.15782","type":"prefixes"},{"id":"10.17612","type":"prefixes"},{"id":"10.17610","type":"prefixes"},{"id":"10.17611","type":"prefixes"},{"id":"10.17614","type":"prefixes"},{"id":"10.17615","type":"prefixes"},{"id":"10.17602","type":"prefixes"},{"id":"10.17603","type":"prefixes"},{"id":"10.17908","type":"prefixes"},{"id":"10.17916","type":"prefixes"},{"id":"10.17915","type":"prefixes"},{"id":"10.17918","type":"prefixes"},{"id":"10.17919","type":"prefixes"},{"id":"10.17911","type":"prefixes"},{"id":"10.17913","type":"prefixes"},{"id":"10.17920","type":"prefixes"},{"id":"10.18123","type":"prefixes"},{"id":"10.18119","type":"prefixes"},{"id":"10.18118","type":"prefixes"},{"id":"10.18117","type":"prefixes"},{"id":"10.18115","type":"prefixes"},{"id":"10.18431","type":"prefixes"},{"id":"10.18436","type":"prefixes"},{"id":"10.18437","type":"prefixes"},{"id":"10.18439","type":"prefixes"},{"id":"10.18737","type":"prefixes"},{"id":"10.18736","type":"prefixes"},{"id":"10.18739","type":"prefixes"},{"id":"10.18734","type":"prefixes"},{"id":"10.20353","type":"prefixes"},{"id":"10.20354","type":"prefixes"},{"id":"10.20352","type":"prefixes"},{"id":"10.20357","type":"prefixes"},{"id":"10.20358","type":"prefixes"},{"id":"10.20359","type":"prefixes"},{"id":"10.21228","type":"prefixes"},{"id":"10.21229","type":"prefixes"},{"id":"10.21224","type":"prefixes"},{"id":"10.21222","type":"prefixes"},{"id":"10.21223","type":"prefixes"},{"id":"10.21221","type":"prefixes"},{"id":"10.21237","type":"prefixes"},{"id":"10.21238","type":"prefixes"},{"id":"10.21239","type":"prefixes"},{"id":"10.21236","type":"prefixes"},{"id":"10.21430","type":"prefixes"},{"id":"10.21433","type":"prefixes"},{"id":"10.21431","type":"prefixes"},{"id":"10.21421","type":"prefixes"},{"id":"10.21422","type":"prefixes"},{"id":"10.21424","type":"prefixes"},{"id":"10.21425","type":"prefixes"},{"id":"10.21426","type":"prefixes"},{"id":"10.21427","type":"prefixes"},{"id":"10.21428","type":"prefixes"},{"id":"10.21418","type":"prefixes"},{"id":"10.21416","type":"prefixes"},{"id":"10.21414","type":"prefixes"},{"id":"10.21972","type":"prefixes"},{"id":"10.21973","type":"prefixes"},{"id":"10.21975","type":"prefixes"},{"id":"10.21976","type":"prefixes"},{"id":"10.21977","type":"prefixes"},{"id":"10.21978","type":"prefixes"},{"id":"10.21990","type":"prefixes"},{"id":"10.21983","type":"prefixes"},{"id":"10.21980","type":"prefixes"},{"id":"10.21986","type":"prefixes"},{"id":"10.5195","type":"prefixes"},{"id":"10.25334","type":"prefixes"},{"id":"10.25337","type":"prefixes"},{"id":"10.25338","type":"prefixes"},{"id":"10.25342","type":"prefixes"},{"id":"10.25349","type":"prefixes"},{"id":"10.25348","type":"prefixes"},{"id":"10.25347","type":"prefixes"},{"id":"10.25346","type":"prefixes"},{"id":"10.25345","type":"prefixes"},{"id":"10.25344","type":"prefixes"},{"id":"10.25352","type":"prefixes"},{"id":"10.25350","type":"prefixes"},{"id":"10.25351","type":"prefixes"},{"id":"10.26081","type":"prefixes"}]}}}]}' + http_version: + recorded_at: Fri, 07 Dec 2018 17:59:12 GMT +- request: + method: get + uri: https://search.test.datacite.org/api?fl=doi,url,xml,state,allocator_symbol,datacentre_symbol,media,minted,updated&q=doi:10.7272/q6g15xs4&wt=json + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; Maremma/4.1.1; +https://github.com/datacite/maremma) + Accept: + - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Dec 2018 17:59:13 GMT + Content-Type: + - application/json;charset=UTF-8 + Connection: + - keep-alive + Server: + - nginx/1.10.3 (Ubuntu) + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + Access-Control-Expose-Headers: + - DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization + body: + encoding: ASCII-8BIT + string: '{"responseHeader":{"status":0,"QTime":1},"response":{"numFound":1,"start":0,"docs":[{"datacentre_symbol":"CDL.UCSFCTSI","url":"https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4","xml":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJlc291cmNlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtMyBodHRwOi8vc2NoZW1hLmRhdGFjaXRlLm9yZy9tZXRhL2tlcm5lbC0zL21ldGFkYXRhLnhzZCI+CiAgPGlkZW50aWZpZXIgaWRlbnRpZmllclR5cGU9IkRPSSI+MTAuNzI3Mi9RNkcxNVhTNDwvaWRlbnRpZmllcj4KICA8Y3JlYXRvcnM+CiAgICA8Y3JlYXRvcj4KICAgICAgPGNyZWF0b3JOYW1lPlJvZHJpZ3VleiwgUm9iZXJ0PC9jcmVhdG9yTmFtZT4KICAgICAgPGFmZmlsaWF0aW9uPlVDIFNhbiBGcmFuY2lzY288L2FmZmlsaWF0aW9uPgogICAgPC9jcmVhdG9yPgogICAgPGNyZWF0b3I+CiAgICAgIDxjcmVhdG9yTmFtZT5Nb3dlciwgV2lsbGlhbTwvY3JlYXRvck5hbWU+CiAgICAgIDxhZmZpbGlhdGlvbj5VQ0xBPC9hZmZpbGlhdGlvbj4KICAgIDwvY3JlYXRvcj4KICA8L2NyZWF0b3JzPgogIDx0aXRsZXM+CiAgICA8dGl0bGU+TkVYVVMgSGVhZCBDVDwvdGl0bGU+CiAgPC90aXRsZXM+CiAgPHB1Ymxpc2hlcj5VQyBTYW4gRnJhbmNpc2NvPC9wdWJsaXNoZXI+CiAgPHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+CiAgPGxhbmd1YWdlPmVuPC9sYW5ndWFnZT4KICA8cmVzb3VyY2VUeXBlIHJlc291cmNlVHlwZUdlbmVyYWw9IkRhdGFzZXQiLz4KICA8c2l6ZXM+CiAgICA8c2l6ZT4xNDk1MjA0IGJ5dGVzPC9zaXplPgogIDwvc2l6ZXM+CiAgPHZlcnNpb24+MTwvdmVyc2lvbj4KICA8cmlnaHRzTGlzdD4KICAgIDxyaWdodHMgcmlnaHRzVVJJPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyI+Q3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiA0LjAgSW50ZXJuYXRpb25hbCAoQ0MgQlkgNC4wKTwvcmlnaHRzPgogIDwvcmlnaHRzTGlzdD4KICA8ZGVzY3JpcHRpb25zPgogICAgPGRlc2NyaXB0aW9uIGRlc2NyaXB0aW9uVHlwZT0iQWJzdHJhY3QiPgogICAgICBCYWNrZ3JvdW5kIENsaW5pY2lhbnMsIGFmcmFpZCBvZiBtaXNzaW5nIGludHJhY3JhbmlhbCBpbmp1cmllcywgbGliZXJhbGx5CiAgICAgIG9idGFpbiBjb21wdXRlZCB0b21vZ3JhcGhpYyAoQ1QpIGhlYWQgaW1hZ2luZyBpbiBibHVudCB0cmF1bWEgcGF0aWVudHMuCiAgICAgIFByaW9yIHdvcmsgc3VnZ2VzdHMgdGhhdCBjbGluaWNhbCBjcml0ZXJpYSAoTkVYVVMgSGVhZCBDVCBkZWNpc2lvbgogICAgICBpbnN0cnVtZW50KSBjYW4gcmVsaWFibHkgaWRlbnRpZnkgcGF0aWVudHMgd2l0aCBpbXBvcnRhbnQgaW5qdXJpZXMsIHdoaWxlCiAgICAgIGV4Y2x1ZGluZyBpbmp1cnksIGFuZCB0aGUgbmVlZCBmb3IgaW1hZ2luZyBpbiBtYW55IHBhdGllbnRzLiBNZXRob2RzIFdlCiAgICAgIGNvbmR1Y3RlZCBhIHByb3NwZWN0aXZlIG9ic2VydmF0aW9uYWwgc3R1ZHkgb2YgdGhlIE5FWFVTIEhlYWQgQ1QgZGVjaXNpb24KICAgICAgaW5zdHJ1bWVudCAoREkpIHRoYXQgcmVxdWlyZXMgcGF0aWVudHMgdG8gbWVldCBlaWdodCBjcml0ZXJpYSB0byBhY2hpZXZlCiAgICAgIOKAnGxvdy1yaXNr4oCdIGNsYXNzaWZpY2F0aW9uLiBXZSBleGFtaW5lZCB0aGUgaW5zdHJ1bWVudOKAmXMgcGVyZm9ybWFuY2UgaW4KICAgICAgaWRlbnRpZnlpbmcgcGF0aWVudHMgcmVxdWlyaW5nIG5ldXJvbG9naWNhbCBpbnRlcnZlbnRpb24gZnJvbSBhbW9uZyBhCiAgICAgIGNvaG9ydCBvZiAxMSw3NzAgYmx1bnQgaGVhZCBpbmp1cnkgcGF0aWVudHMuIFJlc3VsdHMgVGhlIE5FWFVTIEhlYWQgQ1QgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA0MjAgb2YgNDIwIHBhdGllbnRzIHJlcXVpcmluZyBuZXVyb2xvZ2ljYWwKICAgICAgaW50ZXJ2ZW50aW9uIChzZW5zaXRpdml0eSwgMTAwLjAlIFs5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBbQ0ldOiA5OS4xJSDigJMKICAgICAgMTAwLjAlXSkuIFRoZSBpbnN0cnVtZW50IGFzc2lnbmVkIGxvdy1yaXNrIHN0YXR1cyB0byAyLDgyMyBvZiAxMSwzNTAKICAgICAgcGF0aWVudHMgd2hvIGRpZCBub3QgcmVxdWlyZSBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChzcGVjaWZpY2l0eSwgMjQuOSUKICAgICAgWzk1JSBDSTogMjQuMSUgLSAyNS43JV0pLiBOb25lIG9mIHRoZSAyLDgyMyBsb3ctcmlzayBwYXRpZW50cyByZXF1aXJlZAogICAgICBuZXVyb2xvZ2ljYWwgaW50ZXJ2ZW50aW9uIChOUFYsIDEwMC4wJSBbOTUlIENJOiA5OS45JSAtIDEwMC4wJV0pLiBUaGUgREkKICAgICAgYXNzaWduZWQgaGlnaC1yaXNrIHN0YXR1cyB0byA3NTkgb2YgNzY3IHBhdGllbnRzIHdpdGggc2lnbmlmaWNhbnQKICAgICAgaW50cmFjcmFuaWFsIGluanVyaWVzIChzZW5zaXRpdml0eSwgOTkuMCUgWzk1JSBDSTogOTguMCUgLSA5OS42JV0pLiBUaGUKICAgICAgaW5zdHJ1bWVudCBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgdG8gMiw4MTUgb2YgMTEsMDAzIHBhdGllbnRzIHdobyBkaWQKICAgICAgbm90IGhhdmUgc2lnbmlmaWNhbnQgaW5qdXJpZXMgKHNwZWNpZmljaXR5LCAyNS42JSBbOTUlIENJOiAyNC44JSAtCiAgICAgIDI2LjQlXSkuIFNpZ25pZmljYW50IGluanVyaWVzIHdlcmUgYWJzZW50IGluIDIsODE1IG9mIHRoZSAyLDgyMyBwYXRpZW50cwogICAgICBhc3NpZ25lZCBsb3ctcmlzayBzdGF0dXMgKE5QViwgOTkuNyUgWzk1JSBDSTogOTkuNCUgLSA5OS45JV0pLiBDb25jbHVzaW9ucwogICAgICBUaGUgTkVYVVMgSGVhZCBDVCBESSByZWxpYWJseSBpZGVudGlmaWVzIGJsdW50IHRyYXVtYSBwYXRpZW50cyB3aG8gcmVxdWlyZQogICAgICBoZWFkIENUIGltYWdpbmcsIGFuZCBjb3VsZCBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSB1c2Ugb2YgQ1QgaW1hZ2luZy4KICAgIDwvZGVzY3JpcHRpb24+CiAgICA8ZGVzY3JpcHRpb24gZGVzY3JpcHRpb25UeXBlPSJNZXRob2RzIj5Qcm9zcGVjdGl2ZSBtdWx0aWNlbnRlcjwvZGVzY3JpcHRpb24+CiAgPC9kZXNjcmlwdGlvbnM+CiAgPGdlb0xvY2F0aW9ucz4KICAgIDxnZW9Mb2NhdGlvbj4KICAgICAgPGdlb0xvY2F0aW9uUG9pbnQ+MzcuMjUwMjIgLTExOS43NTEyNjwvZ2VvTG9jYXRpb25Qb2ludD4KICAgICAgPGdlb0xvY2F0aW9uUGxhY2U+Q2FsaWZvcm5pYSwgVVNBPC9nZW9Mb2NhdGlvblBsYWNlPgogICAgPC9nZW9Mb2NhdGlvbj4KICA8L2dlb0xvY2F0aW9ucz4KPC9yZXNvdXJjZT4=","allocator_symbol":"CDL","minted":"2018-12-07T09:48:20Z","state":"findable","updated":"2018-12-07T09:48:20Z","doi":"10.7272/Q6G15XS4"}]}} + +' + http_version: + recorded_at: Fri, 07 Dec 2018 17:59:13 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/jobs/doi_import_by_day_job.rb b/spec/jobs/doi_import_by_day_job.rb new file mode 100644 index 000000000..3d9375455 --- /dev/null +++ b/spec/jobs/doi_import_by_day_job.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +describe DoiImportByDayJob, type: :job do + let(:doi) { create(:doi) } + subject(:job) { DoiImportByDayJob.perform_later(Time.zone.now.strftime("%F")) } + + it 'queues the job' do + expect { job }.to have_enqueued_job(DoiImportByDayJob) + .on_queue("test_lupo_background") + end + + after do + clear_enqueued_jobs + clear_performed_jobs + end +end \ No newline at end of file diff --git a/spec/models/doi_spec.rb b/spec/models/doi_spec.rb index cee8aaa33..48a6482ea 100644 --- a/spec/models/doi_spec.rb +++ b/spec/models/doi_spec.rb @@ -55,7 +55,7 @@ expect(subject).to have_state(:findable) end - it "can't register with test prefix" do + it "can't publish with test prefix" do subject = create(:doi, doi: "10.5072/x") subject.publish expect(subject).to have_state(:draft) @@ -64,7 +64,7 @@ describe "flagged" do it "can flag" do - subject.register + subject.publish subject.flag expect(subject).to have_state(:flagged) end @@ -77,7 +77,7 @@ describe "broken" do it "can link_check" do - subject.register + subject.publish subject.link_check expect(subject).to have_state(:broken) end @@ -227,49 +227,57 @@ end end - context "no url" do - let(:provider) { create(:provider, symbol: "ADMIN") } - let(:client) { create(:client, provider: provider) } - let(:url) { "https://www.example.org" } - subject { build(:doi, client: client, url: nil, current_user: current_user) } - - # it "don't update state change" do - # subject.publish - # expect { subject.save }.not_to have_enqueued_job(HandleJob) - # expect(subject).to have_state(:findable) - # end - - it "update url change" do - subject.publish - subject.url = url - expect { subject.save }.to have_enqueued_job(HandleJob) - end - end + # context "no url" do + # let(:provider) { create(:provider, symbol: "ADMIN") } + # let(:client) { create(:client, provider: provider) } + # let(:url) { "https://www.example.org" } + # subject { build(:doi, client: client, url: nil, current_user: current_user) } + + # it "don't update state change" do + # subject.publish + # expect { subject.save }.not_to have_enqueued_job(HandleJob) + # expect(subject).to have_state(:findable) + # end + + # it "update url change" do + # subject.publish + # subject.url = url + # expect { subject.save }.to have_enqueued_job(HandleJob) + # end + # end end describe "metadata" do - let(:xml) { "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxyZXNvdXJjZSB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC0zIGh0dHA6Ly9zY2hlbWEuZGF0YWNpdGUub3JnL21ldGEva2VybmVsLTMvbWV0YWRhdGEueHNkIiB4bWxucz0iaHR0cDovL2RhdGFjaXRlLm9yZy9zY2hlbWEva2VybmVsLTMiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPjxpZGVudGlmaWVyIGlkZW50aWZpZXJUeXBlPSJET0kiPjEwLjUyNTYvZjEwMDByZXNlYXJjaC44NTcwLnI2NDIwPC9pZGVudGlmaWVyPjxjcmVhdG9ycz48Y3JlYXRvcj48Y3JlYXRvck5hbWU+ZCBzPC9jcmVhdG9yTmFtZT48L2NyZWF0b3I+PC9jcmVhdG9ycz48dGl0bGVzPjx0aXRsZT5SZWZlcmVlIHJlcG9ydC4gRm9yOiBSRVNFQVJDSC0zNDgyIFt2ZXJzaW9uIDU7IHJlZmVyZWVzOiAxIGFwcHJvdmVkLCAxIGFwcHJvdmVkIHdpdGggcmVzZXJ2YXRpb25zXTwvdGl0bGU+PC90aXRsZXM+PHB1Ymxpc2hlcj5GMTAwMCBSZXNlYXJjaCBMaW1pdGVkPC9wdWJsaXNoZXI+PHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+PHJlc291cmNlVHlwZSByZXNvdXJjZVR5cGVHZW5lcmFsPSJUZXh0Ii8+PC9yZXNvdXJjZT4=" } + subject { create(:doi) } - subject { create(:doi, xml: xml) } + it "valid" do + expect(subject.valid?).to be true + end - it "title" do - expect(subject.title).to eq("Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]") + it "titles" do + expect(subject.titles).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) end - it "author" do - expect(subject.author).to eq("name"=>"D S") + it "creators" do + expect(subject.creators.length).to eq(8) + expect(subject.creators.first).to eq("familyName"=>"Ollomo", "givenName"=>"Benjamin", "name"=>"Benjamin Ollomo", "type"=>"Person") end - it "date_published" do - expect(subject.date_published).to eq("2017") + it "dates" do + expect(subject.get_date(subject.dates, "Issued")).to eq("2011") end it "publication_year" do - expect(subject.publication_year).to eq(2017) + expect(subject.publication_year).to eq(2011) end it "schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-3") + expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") + end + + it "xml" do + doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) + expect(doc.at_css("identifier").content).to eq(subject.doi) end it "metadata" do @@ -278,104 +286,84 @@ end it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-3") + expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") end end describe "change metadata" do - let(:xml) { "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxyZXNvdXJjZSB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC0zIGh0dHA6Ly9zY2hlbWEuZGF0YWNpdGUub3JnL21ldGEva2VybmVsLTMvbWV0YWRhdGEueHNkIiB4bWxucz0iaHR0cDovL2RhdGFjaXRlLm9yZy9zY2hlbWEva2VybmVsLTMiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPjxpZGVudGlmaWVyIGlkZW50aWZpZXJUeXBlPSJET0kiPjEwLjUyNTYvZjEwMDByZXNlYXJjaC44NTcwLnI2NDIwPC9pZGVudGlmaWVyPjxjcmVhdG9ycz48Y3JlYXRvcj48Y3JlYXRvck5hbWU+ZCBzPC9jcmVhdG9yTmFtZT48L2NyZWF0b3I+PC9jcmVhdG9ycz48dGl0bGVzPjx0aXRsZT5SZWZlcmVlIHJlcG9ydC4gRm9yOiBSRVNFQVJDSC0zNDgyIFt2ZXJzaW9uIDU7IHJlZmVyZWVzOiAxIGFwcHJvdmVkLCAxIGFwcHJvdmVkIHdpdGggcmVzZXJ2YXRpb25zXTwvdGl0bGU+PC90aXRsZXM+PHB1Ymxpc2hlcj5GMTAwMCBSZXNlYXJjaCBMaW1pdGVkPC9wdWJsaXNoZXI+PHB1YmxpY2F0aW9uWWVhcj4yMDE3PC9wdWJsaWNhdGlvblllYXI+PHJlc291cmNlVHlwZSByZXNvdXJjZVR5cGVHZW5lcmFsPSJUZXh0Ii8+PC9yZXNvdXJjZT4=" } - - subject { create(:doi, xml: xml) } - - it "title" do - title = "Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes" - subject.title = title - subject.save - - expect(subject.title).to eq(title) + let(:xml) { File.read(file_fixture('datacite_f1000.xml')) } + let(:title) { "Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes" } + let(:creators) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } + let(:publisher) { "Zenodo" } + let(:publication_year) { 2011 } + let(:types) { { "resourceTypeGeneral" => "Software", "resourceType" => "BlogPosting", "schemaOrg" => "BlogPosting" } } + let(:description) { "Eating your own dog food is a slang term to describe that an organization should itself use the products and services it provides. For DataCite this means that we should use DOIs with appropriate metadata and strategies for long-term preservation for..." } + + subject { create(:doi, + xml: xml, + titles: [{ "title" => title }], + creators: creators, + publisher: publisher, + publication_year: publication_year, + types: types, + descriptions: [{ "description" => description }], + event: "publish") + } + + it "titles" do + expect(subject.titles).to eq([{ "title" => title }]) xml = Maremma.from_xml(subject.xml).fetch("resource", {}) expect(xml.dig("titles", "title")).to eq(title) end - it "author" do - author = [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] - subject.author = author - subject.save - - expect(subject.author).to eq(author) + it "creators" do + expect(subject.creators).to eq(creators) xml = Maremma.from_xml(subject.xml).fetch("resource", {}) expect(xml.dig("creators", "creator")).to eq([{"creatorName"=>"Ollomi, Benjamin"}, {"creatorName"=>"Duran, Patrick"}]) end it "publisher" do - publisher = "Zenodo" - subject.publisher = publisher - subject.save - expect(subject.publisher).to eq(publisher) xml = Maremma.from_xml(subject.xml).fetch("resource", {}) expect(xml.dig("publisher")).to eq(publisher) end - it "date_published" do - date_published = "2011-05-26" - subject.date_published = date_published - subject.save - - expect(subject.date_published).to eq(date_published) + it "publication_year" do + expect(subject.publication_year).to eq(2011) xml = Maremma.from_xml(subject.xml).fetch("resource", {}) - expect(xml.dig("dates", "date")).to eq("dateType"=>"Issued", "__content__"=>date_published) expect(xml.dig("publicationYear")).to eq("2011") end - it "additional_type" do - additional_type = "BlogPosting" - subject.additional_type = additional_type - subject.save - - expect(subject.additional_type).to eq(additional_type) + it "resource_type" do + expect(subject.types["resourceType"]).to eq("BlogPosting") xml = Maremma.from_xml(subject.xml).fetch("resource", {}) - expect(xml.dig("resourceType")).to eq("resourceTypeGeneral"=>"Text", "__content__"=>"BlogPosting") + expect(xml.dig("resourceType")).to eq("resourceTypeGeneral"=>"Software", "__content__"=>"BlogPosting") end it "resource_type_general" do - resource_type_general = "Software" - subject.resource_type_general = resource_type_general - subject.save - - expect(subject.resource_type_general).to eq(resource_type_general) + expect(subject.types["resourceTypeGeneral"]).to eq("Software") xml = Maremma.from_xml(subject.xml).fetch("resource", {}) - expect(xml.dig("resourceType")).to eq("resourceTypeGeneral"=>resource_type_general, "__content__"=>"ScholarlyArticle") + expect(xml.dig("resourceType")).to eq("resourceTypeGeneral"=>"Software", "__content__"=>"BlogPosting") end - it "description" do - description = "Eating your own dog food is a slang term to describe that an organization should itself use the products and services it provides. For DataCite this means that we should use DOIs with appropriate metadata and strategies for long-term preservation for..." - subject.description = description - subject.save - - expect(subject.description).to eq(description) + it "descriptions" do + expect(subject.descriptions).to eq([{ "description" => description }]) xml = Maremma.from_xml(subject.xml).fetch("resource", {}) expect(xml.dig("descriptions", "description")).to eq("__content__" => "Eating your own dog food is a slang term to describe that an organization should itself use the products and services it provides. For DataCite this means that we should use DOIs with appropriate metadata and strategies for long-term preservation for...", "descriptionType" => "Abstract") end it "schema_version" do - title = "Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes" - subject.title = title - subject.save - - xml = Maremma.from_xml(subject.xml).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq(title) - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") + xml = Maremma.from_xml(subject.xml).fetch("resource", {}) expect(xml.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") - #expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") + expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") end end @@ -393,555 +381,11 @@ end end - context "parses Crossref xml" do - let(:xml) { Base64.strict_encode64(file_fixture('crossref.xml').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes") - end - - it "date_published" do - expect(subject.date_published).to eq("2006-12-20") - end - - it "publication_year" do - expect(subject.publication_year).to eq(2006) - end - - it "author" do - expect(subject.author.length).to eq(5) - expect(subject.author.first).to eq("type"=>"Person", "name"=>"Markus Ralser", "givenName"=>"Markus", "familyName"=>"Ralser") - end - - it "schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses namespaced xml" do - let(:xml) { Base64.strict_encode64(file_fixture('ns0.xml').read) } - - subject { create(:doi, doi: "10.4231/D38G8FK8B", xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - doc.remove_namespaces! - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("LAMMPS Data-File Generator") - end - - it "date_published" do - expect(subject.date_published).to eq("2018") - end - - it "publication_year" do - expect(subject.publication_year).to eq(2018) - end - - it "author" do - expect(subject.author.length).to eq(5) - expect(subject.author.first).to eq("type"=>"Person", "name"=>"Carlos PatiñO", "givenName"=>"Carlos", "familyName"=>"PatiñO") - end - - it "schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-2.2") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - doc.remove_namespaces! - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-2.2") - end - end - - context "parses schema" do - let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Eating your own Dog Food") - end - - it "author" do - expect(subject.author).to eq("type"=>"Person", "id"=>"https://orcid.org/0000-0003-1419-2405", "name"=>"Fenner, Martin", "givenName"=>"Martin", "familyName"=>"Fenner") - end - - it "date_published" do - expect(subject.date_published).to eq("2016-12-20") - end - - it "publication_year" do - expect(subject.publication_year).to eq(2016) - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses schema 3" do - let(:xml) { Base64.strict_encode64(file_fixture('datacite_schema_3.xml').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "title" do - expect(subject.title).to eq("Data from: A new malaria agent in African hominids.") - end - - it "author" do - expect(subject.author.length).to eq(8) - expect(subject.author.first).to eq("type"=>"Person", "name"=>"Benjamin Ollomo", "givenName"=>"Benjamin", "familyName"=>"Ollomo") - end - - it "date_published" do - expect(subject.date_published).to eq("2011") - end - - it "publication_year" do - expect(subject.publication_year).to eq(2011) - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-3") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-3") - end - end - - context "parses schema 2.2" do - let(:xml) { Base64.strict_encode64(file_fixture('datacite_schema_2.2.xml').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "title" do - expect(subject.title).to eq(["Właściwości rzutowań podprzestrzeniowych", {"title_type"=>"TranslatedTitle", "text"=>"Translation of Polish titles"}]) - end - - it "author" do - expect(subject.author.length).to eq(2) - expect(subject.author.first).to eq("type"=>"Person", "name"=>"John Smith", "givenName"=>"John", "familyName"=>"Smith") - end - - it "date_published" do - expect(subject.date_published).to eq("2010") - end - - it "publication_year" do - expect(subject.publication_year).to eq(2010) - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-2.2") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-2.2") - end - end - - context "parses bibtex" do - let(:xml) { Base64.strict_encode64(file_fixture('crossref.bib').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") - end - - it "author" do - expect(subject.author.length).to eq(5) - expect(subject.author.first).to eq("type"=>"Person", "name"=>"Martial Sankar", "givenName"=>"Martial", "familyName"=>"Sankar") - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses ris" do - let(:xml) { ::Base64.strict_encode64(file_fixture('crossref.ris').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") - end - - it "author" do - expect(subject.author.length).to eq(5) - expect(subject.author.first).to eq("type"=>"Person", "name"=>"Martial Sankar", "givenName"=>"Martial", "familyName"=>"Sankar") - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses citeproc" do - let(:xml) { ::Base64.strict_encode64(file_fixture('citeproc.json').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Eating your own Dog Food") - end - - it "author" do - expect(subject.author).to eq("type"=>"Person", "name"=>"Martin Fenner", "givenName"=>"Martin", "familyName"=>"Fenner") - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses codemeta" do - let(:xml) { ::Base64.strict_encode64(file_fixture('codemeta.json').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("R Interface to the DataONE REST API") - end - - it "author" do - expect(subject.author.length).to eq(3) - expect(subject.author.first).to eq("type"=>"Person", "id"=>"http://orcid.org/0000-0003-0077-4738", "name"=>"Matt Jones", "givenName"=>"Matt", "familyName"=>"Jones") - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses crosscite" do - let(:xml) { ::Base64.strict_encode64(file_fixture('crosscite.json').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Analysis Tools for Crossover Experiment of UI using Choice Architecture") - end - - it "author" do - expect(subject.author).to eq("type"=>"Person", "name"=>"Kristian Garza", "givenName"=>"Kristian", "familyName"=>"Garza") - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses schema.org" do - let(:xml) { ::Base64.strict_encode64(file_fixture('schema_org.json').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("Eating your own Dog Food") - end - - it "author" do - expect(subject.author).to eq("type"=>"Person", "id"=>"http://orcid.org/0000-0003-1419-2405", "name"=>"Martin Fenner", "givenName"=>"Martin", "familyName"=>"Fenner") - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - end - - context "parses schema.org topmed" do - let(:xml) { ::Base64.strict_encode64(file_fixture('schema_org_topmed.json').read) } - - subject { create(:doi, xml: xml, event: "publish") } - - it "creates xml" do - doc = Nokogiri::XML(subject.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - expect(doc.at_css("relatedIdentifiers").content).to eq("10.23725/2g4s-qv04") - end - - it "valid model" do - expect(subject.valid?).to be true - end - - it "validates against schema" do - expect(subject.validation_errors).to be_empty - end - - it "title" do - expect(subject.title).to eq("NWD165827.recab.cram") - end - - it "author" do - expect(subject.author).to eq("name"=>"TOPMed IRC", "type"=>"Organization") - end - - it "content_url" do - expect(subject.content_url).to eq(["s3://cgp-commons-public/topmed_open_access/197bc047-e917-55ed-852d-d563cdbc50e4/NWD165827.recab.cram", "gs://topmed-irc-share/public/NWD165827.recab.cram"]) - end - - it "references" do - expect(subject.references).to eq("id"=>"https://doi.org/10.23725/2g4s-qv04", "type"=>"Dataset") - end - - it "funding" do - expect(subject.funding).to eq("id"=>"https://doi.org/10.13039/100000050", "name"=>"National Heart, Lung, and Blood Institute (NHLBI)", "type"=>"Organization") - end - - it "alternate_identifier" do - expect(subject.alternate_identifier).to eq([{"name"=>"3b33f6b9338fccab0901b7d317577ea3", "type"=>"md5"}, {"name"=>"ark:/99999/fk41CrU4eszeLUDe", "type"=>"minid"}, {"name"=>"dg.4503/c3d66dc9-58da-411c-83c4-dd656aa3c4b7", "type"=>"dataguid"}]) - end - - it "creates schema_version" do - expect(subject.schema_version).to eq("http://datacite.org/schema/kernel-4") - end - - it "metadata" do - doc = Nokogiri::XML(subject.metadata.first.xml, nil, 'UTF-8', &:noblanks) - expect(doc.at_css("identifier").content).to eq(subject.doi) - end - - it "namespace" do - expect(subject.metadata.first.namespace).to eq("http://datacite.org/schema/kernel-4") - end - - it "media" do - expect(subject.media.pluck(:url)).to eq(["s3://cgp-commons-public/topmed_open_access/197bc047-e917-55ed-852d-d563cdbc50e4/NWD165827.recab.cram", "gs://topmed-irc-share/public/NWD165827.recab.cram"]) - end - end - describe "content negotiation" do - let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } - - subject { create(:doi, doi: "10.5438/4k3m-nyvg", xml: xml, event: "publish") } + subject { create(:doi, doi: "10.5438/4k3m-nyvg", event: "publish") } it "validates against schema" do - expect(subject.validation_errors).to be_empty + expect(subject.valid?).to be true end it "generates datacite_xml" do @@ -951,49 +395,95 @@ it "generates bibtex" do bibtex = BibTeX.parse(subject.bibtex).to_a(quotes: '').first - expect(bibtex[:bibtex_type].to_s).to eq("article") - expect(bibtex[:title].to_s).to eq("Eating your own Dog Food") + expect(bibtex[:bibtex_type].to_s).to eq("misc") + expect(bibtex[:title].to_s).to eq("Data from: A new malaria agent in African hominids.") end it "generates ris" do ris = subject.ris.split("\r\n") - expect(ris[0]).to eq("TY - RPRT") - expect(ris[1]).to eq("T1 - Eating your own Dog Food") + expect(ris[0]).to eq("TY - DATA") + expect(ris[1]).to eq("T1 - Data from: A new malaria agent in African hominids.") end it "generates schema_org" do json = JSON.parse(subject.schema_org) - expect(json["@type"]).to eq("ScholarlyArticle") - expect(json["name"]).to eq("Eating your own Dog Food") + expect(json["@type"]).to eq("Dataset") + expect(json["name"]).to eq("Data from: A new malaria agent in African hominids.") end it "generates datacite_json" do json = JSON.parse(subject.datacite_json) expect(json["doi"]).to eq("10.5438/4K3M-NYVG") - expect(json["title"]).to eq("Eating your own Dog Food") + expect(json["titles"]).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) end it "generates codemeta" do json = JSON.parse(subject.codemeta) - expect(json["@type"]).to eq("ScholarlyArticle") - expect(json["title"]).to eq("Eating your own Dog Food") + expect(json["@type"]).to eq("Dataset") + expect(json["title"]).to eq("Data from: A new malaria agent in African hominids.") end it "generates jats" do jats = Maremma.from_xml(subject.jats).fetch("element_citation", {}) - expect(jats.dig("publication_type")).to eq("journal") - expect(jats.dig("article_title")).to eq("Eating your own Dog Food") + expect(jats.dig("publication_type")).to eq("data") + expect(jats.dig("data_title")).to eq("Data from: A new malaria agent in African hominids.") end + end - it "generates rdf_xml" do - rdf_xml = Maremma.from_xml(subject.rdf_xml).fetch("RDF", {}) - expect(rdf_xml.dig("ScholarlyArticle", "rdf:about")).to eq(subject.identifier) - end + describe "migrates landing page" do + let(:provider) { create(:provider, symbol: "ADMIN") } + let(:client) { create(:client, provider: provider) } - it "generates turtle" do - ttl = subject.turtle.split("\n") - expect(ttl[0]).to eq("@prefix schema: .") - expect(ttl[2]).to eq(" a schema:ScholarlyArticle;") + let(:last_landing_page_status_result) { { + "error" => nil, + "redirect-count" => 0, + "redirect-urls" => ["http://example.com", "https://example.com"], + "download-latency" => 200.323232, + "has-schema-org" => true, + "schema-org-id" => "10.14454/10703", + "dc-identifier" => nil, + "citation-doi" => nil, + "body-has-pid" => true + } } + + let(:timeNow) { Time.zone.now.iso8601 } + + let(:doi) { + create( + :doi, + client: client, + last_landing_page_status: 200, + last_landing_page_status_check: timeNow, + last_landing_page_content_type: "text/html", + last_landing_page: "http://example.com", + last_landing_page_status_result: last_landing_page_status_result + ) + } + + let(:landing_page) { { + "checked" => timeNow, + "status" => 200, + "url" => "http://example.com", + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => ["http://example.com", "https://example.com"], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true + } } + + before { doi.save } + + it "migrates and corrects data" do + Doi.migrate_landing_page + + changed_doi = Doi.find(doi.id) + + expect(changed_doi.landing_page).to eq(landing_page) end end end diff --git a/spec/models/metadata_spec.rb b/spec/models/metadata_spec.rb index 377929e1f..24cb8f56c 100644 --- a/spec/models/metadata_spec.rb +++ b/spec/models/metadata_spec.rb @@ -80,9 +80,10 @@ expect(subject.namespace).to eq("http://datacite.org/schema/kernel-4") end - it "valid model" do - expect(subject.valid?).to be false - end + # TODO: db-fields-for-attributes + # it "valid model" do + # expect(subject.valid?).to be false + # end it "validates xml" do expect(subject.errors[:xml]).to be_empty diff --git a/spec/models/resource_type_spec.rb b/spec/models/resource_type_spec.rb deleted file mode 100644 index db3b325d4..000000000 --- a/spec/models/resource_type_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'rails_helper' - -describe ResourceType, type: :model, vcr: true do - it "all" do - resource_types = ResourceType.all[:data] - expect(resource_types.length).to eq(15) - resource_type = resource_types.first - expect(resource_type.title).to eq("Audiovisual") - end - - it "query" do - resource_types = ResourceType.where(query: "data")[:data] - expect(resource_types.length).to eq(2) - resource_type = resource_types.first - expect(resource_type.title).to eq("DataPaper") - end - - it "one" do - resource_type = ResourceType.where(id: "text")[:data] - expect(resource_type.title).to eq("Text") - end -end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index ffa8008ac..b340e9dc5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -46,8 +46,9 @@ expect(user.role_id).to eq("provider_admin") end - it "has provider_id" do + it "has provider" do expect(user.provider_id).to eq(provider.symbol.downcase) + expect(user.provider.name).to eq(provider.name) end it "has name" do @@ -70,8 +71,9 @@ expect(user.provider_id).to eq(client.symbol.downcase.split(".").first) end - it "has client_id" do + it "has client" do expect(user.client_id).to eq(client.symbol.downcase) + expect(user.client.name).to eq(client.name) end it "has name" do diff --git a/spec/requests/client_prefixes_spec.rb b/spec/requests/client_prefixes_spec.rb index f2097728b..c3dc8fb74 100644 --- a/spec/requests/client_prefixes_spec.rb +++ b/spec/requests/client_prefixes_spec.rb @@ -93,7 +93,6 @@ before { post '/client-prefixes', params: valid_attributes.to_json, headers: headers } it 'creates a client-prefix' do - puts response.body expect(json.dig('data', 'id')).not_to be_nil end diff --git a/spec/requests/clients_spec.rb b/spec/requests/clients_spec.rb index e698de94d..c2735bb5c 100644 --- a/spec/requests/clients_spec.rb +++ b/spec/requests/clients_spec.rb @@ -11,8 +11,8 @@ "attributes" => { "symbol" => provider.symbol + ".IMPERIAL", "name" => "Imperial College", - "contact-name" => "Madonna", - "contact-email" => "bob@example.com" + "contactName" => "Madonna", + "contactEmail" => "bob@example.com" }, "relationships": { "provider": { @@ -107,7 +107,7 @@ it 'creates a client' do attributes = json.dig('data', 'attributes') expect(attributes["name"]).to eq("Imperial College") - expect(attributes["contact-name"]).to eq("Madonna") + expect(attributes["contactName"]).to eq("Madonna") relationships = json.dig('data', 'relationships') expect(relationships.dig("provider", "data", "id")).to eq(provider.symbol.downcase) @@ -124,7 +124,7 @@ "attributes" => { "symbol" => provider.symbol + ".IMPERIAL", "name" => "Imperial College", - "contact-name" => "Madonna" + "contactName" => "Madonna" }, "relationships": { "provider": { @@ -158,7 +158,6 @@ before { put "/clients/#{client.symbol}", params: params.to_json, headers: headers } it 'updates the record' do - puts response.body expect(json.dig('data', 'attributes', 'name')).to eq("Imperial College 2") expect(json.dig('data', 'attributes', 'name')).not_to eq(client.name) end @@ -214,6 +213,7 @@ before { delete "/clients/#{client.uid}", headers: headers } it 'returns status code 204' do + puts response.body expect(response).to have_http_status(204) end diff --git a/spec/requests/dois_spec.rb b/spec/requests/dois_spec.rb index 1910982e6..10d2a7700 100644 --- a/spec/requests/dois_spec.rb +++ b/spec/requests/dois_spec.rb @@ -7,27 +7,27 @@ let(:provider) { create(:provider, symbol: "DATACITE") } let(:client) { create(:client, provider: provider, symbol: ENV['MDS_USERNAME'], password: ENV['MDS_PASSWORD']) } - let(:prefix) { create(:prefix, prefix: "10.14454") } + let!(:prefix) { create(:prefix, prefix: "10.14454") } let!(:client_prefix) { create(:client_prefix, client: client, prefix: prefix) } let!(:dois) { create_list(:doi, 3, client: client) } let(:doi) { create(:doi, client: client) } let(:bearer) { Client.generate_token(role_id: "client_admin", uid: client.symbol, provider_id: provider.symbol.downcase, client_id: client.symbol.downcase, password: client.password) } let(:headers) { { 'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + bearer }} - # describe 'GET /dois', elasticsearch: true do - # before do - # sleep 1 - # get '/dois', headers: headers - # end + describe 'GET /dois', elasticsearch: true do + before do + sleep 1 + get '/dois', headers: headers + end - # it 'returns dois' do - # expect(json['data'].size).to eq(3) - # end + # it 'returns dois' do + # expect(json['data'].size).to eq(0) + # end - # it 'returns status code 200' do - # expect(response).to have_http_status(200) - # end - # end + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end describe 'GET /dois/:id' do context 'when the record exists' do @@ -54,16 +54,240 @@ expect(json).to eq("errors"=>[{"status"=>"404", "title"=>"The resource you are looking for doesn't exist."}]) end end + + context 'anonymous user' do + before { get "/dois/#{doi.doi}" } + + it 'returns the Doi' do + expect(json).not_to be_empty + expect(json.fetch('errors')).to eq([{"status"=>"401", "title"=>"Bad credentials."}]) + end + + it 'returns status code 401' do + expect(response).to have_http_status(401) + end + end + + context 'invalid password' do + let(:bearer) { Client.generate_token(role_id: "client_admin", uid: client.symbol, provider_id: provider.symbol.downcase, client_id: client.symbol.downcase, password: "abc") } + let(:headers) { { 'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + bearer }} + + before { get "/dois/#{doi.doi}" } + + it 'returns the Doi' do + expect(json).not_to be_empty + expect(json.fetch('errors')).to eq([{"status"=>"401", "title"=>"Bad credentials."}]) + end + + it 'returns status code 401' do + expect(response).to have_http_status(401) + end + end end - describe 'PATCH /dois/:id' do + describe 'state' do + let(:doi_id) { "10.14454/4K3M-NYVG" } + let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:bearer) { User.generate_token(role_id: "client_admin", client_id: client.symbol.downcase) } let(:headers) { {'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + bearer}} - before(:each) do - Rails.cache.clear + context 'initial state draft' do + before { get "/dois/#{doi.doi}", headers: headers } + + it 'fetches the record' do + expect(json.dig('data', 'attributes', 'url')).to eq(doi.url) + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + expect(json.dig('data', 'attributes', 'titles')).to eq(doi.titles) + expect(json.dig('data', 'attributes', 'isActive')).to be false + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + it 'initial state' do + expect(json.dig('data', 'attributes', 'state')).to eq("draft") + end + end + + context 'register' do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "url" => "http://www.bl.uk/pdf/pat.pdf", + "event" => "register" + } + } + } + end + before { patch "/dois/#{doi_id}", params: valid_attributes.to_json, headers: headers } + + it 'creates the record' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi_id.downcase) + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig('data', 'attributes', 'isActive')).to be false + end + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'sets state to registered' do + expect(json.dig('data', 'attributes', 'state')).to eq("registered") + end end + context 'register no url' do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "event" => "register" + } + } + } + end + before { patch "/dois/#{doi_id}", params: valid_attributes.to_json, headers: headers } + + it 'creates the record' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi_id.downcase) + expect(json.dig('data', 'attributes', 'url')).to be_nil + expect(json.dig('data', 'attributes', 'isActive')).to be false + end + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'state remains draft' do + expect(json.dig('data', 'attributes', 'state')).to eq("draft") + end + end + + context 'publish' do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "event" => "publish" + } + } + } + end + before { patch "/dois/#{doi_id}", params: valid_attributes.to_json, headers: headers } + + it 'updates the record' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi_id.downcase) + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig('data', 'attributes', 'isActive')).to be true + end + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") + end + end + + context 'publish no url' do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "event" => "publish" + } + } + } + end + before { patch "/dois/#{doi_id}", params: valid_attributes.to_json, headers: headers } + + it 'updates the record' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi_id.downcase) + expect(json.dig('data', 'attributes', 'url')).to be_nil + expect(json.dig('data', 'attributes', 'isActive')).to be false + end + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'state remains draft' do + expect(json.dig('data', 'attributes', 'state')).to eq("draft") + end + end + + context 'hide' do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "event" => "hide" + } + } + } + end + before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + + it 'updates the record' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + expect(json.dig('data', 'attributes', 'isActive')).to be false + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + it 'state changes to register' do + expect(json.dig('data', 'attributes', 'state')).to eq("registered") + end + end + + context 'hide with reason' do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "event" => "hide", + "reason" => "withdrawn by author" + } + } + } + end + before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + + it 'updates the record' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + expect(json.dig('data', 'attributes', 'isActive')).to be false + expect(json.dig('data', 'attributes', 'reason')).to eq("withdrawn by author") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + it 'state changes to register' do + expect(json.dig('data', 'attributes', 'state')).to eq("registered") + end + end + end + + describe 'PATCH /dois/:id' do context 'when the record exists' do let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:valid_attributes) do @@ -82,10 +306,7 @@ it 'updates the record' do expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq("Eating your own Dog Food") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) end it 'returns status code 200' do @@ -97,24 +318,35 @@ end end - context 'when the record exists no creator validate' do - let(:xml) { Base64.strict_encode64(file_fixture('datacite_missing_creator.xml').read) } + context 'when the record exists no data attribute' do + let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } + let(:valid_attributes) do + { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml + } + end + before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + + it 'raises an error' do + expect(json.dig('errors')).to eq([{"status"=>"400", "title"=>"You need to provide a payload following the JSONAPI spec"}]) + end + + it 'returns status code 400' do + expect(response).to have_http_status(400) + end + end + + context 'no creators validate' do + let(:doi) { create(:doi, client: client, creators: nil) } let(:valid_attributes) do { "data" => { "type" => "dois", "attributes" => { "url" => "http://www.bl.uk/pdf/pat.pdf", - "xml" => xml, - "validate" => "true" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "xml" => Base64.strict_encode64(doi.xml), + "event" => "publish" } } } @@ -131,8 +363,8 @@ end context 'when the record exists https://github.com/datacite/lupo/issues/89' do - let(:doi) { create(:doi, doi: "10.24425/119496", client: client, state: "registered") } - let(:valid_attributes) {file_fixture('datacite_89.json').read} + let(:doi) { create(:doi, doi: "10.14454/119496", client: client) } + let(:valid_attributes) { file_fixture('datacite_89.json').read } before { put "/dois/#{doi.doi}", params: valid_attributes, headers: headers } @@ -145,8 +377,7 @@ end end - context 'when the record exists 2.2' do - let(:doi) { create(:doi, doi: "10.24425/119497", client: client, state: "registered") } + context 'schema 2.2' do let(:xml) { Base64.strict_encode64(file_fixture('datacite_schema_2.2.xml').read) } let(:valid_attributes) do { @@ -154,57 +385,54 @@ "type" => "dois", "attributes" => { "xml" => xml, - "title" => "Eating your own Dog Food", + "url" => "http://www.bl.uk/pdf/patspec.pdf", "event" => "publish" } } } end - before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + before { patch "/dois/10.14454/10703", params: valid_attributes.to_json, headers: headers } it 'updates the record' do - expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Właściwości rzutowań podprzestrzeniowych"}, {"title"=>"Translation of Polish titles", "titleType"=>"TranslatedTitle"}]) + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-2.2") xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq("Eating your own Dog Food") + expect(xml.dig("titles", "title")).to eq(["Właściwości rzutowań podprzestrzeniowych", {"__content__"=>"Translation of Polish titles", "titleType"=>"TranslatedTitle"}]) end - it 'returns status code 200' do - expect(response).to have_http_status(200) + it 'returns status code 201' do + expect(response).to have_http_status(201) end end context 'NoMethodError https://github.com/datacite/lupo/issues/84' do - let(:doi_id) { "10.14454/m9.figshare.6839054.v1" } + let(:doi) { create(:doi, client: client) } + let(:url) { "https://figshare.com/articles/Additional_file_1_of_Contemporary_ancestor_Adaptive_divergence_from_standing_genetic_variation_in_Pacific_marine_threespine_stickleback/6839054/1" } let(:valid_attributes) do { "data" => { "type" => "dois", "attributes" => { - "url"=> "https://figshare.com/articles/Additional_file_1_of_Contemporary_ancestor_Adaptive_divergence_from_standing_genetic_variation_in_Pacific_marine_threespine_stickleback/6839054/1", + "url"=> url, + "xml" => Base64.strict_encode64(doi.xml), "event" => "publish" - }, - "relationships" => { - "client" => { - "data" => { - "type" => "clients", - "id" => client.symbol.downcase - } - } } } } end - before { put "/dois/#{doi_id}", params: valid_attributes.to_json, headers: headers } + before { put "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } it 'returns no errors' do - expect(json.dig('data', 'attributes', 'doi')).to eq(doi_id) + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + expect(json.dig('data', 'attributes', 'url')).to eq(url) end - it 'returns status code 201' do - expect(response).to have_http_status(201) + it 'returns status code 200' do + expect(response).to have_http_status(200) end end @@ -217,15 +445,8 @@ "type" => "dois", "attributes" => { "url" => "http://www.bl.uk/pdf/pat.pdf", - "xml" => xml - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "xml" => xml, + "event" => "publish" } } } @@ -235,22 +456,19 @@ it 'creates the record' do expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") expect(json.dig('data', 'attributes', 'doi')).to eq(doi_id.downcase) - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq("Eating your own Dog Food") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to draft' do - expect(json.dig('data', 'attributes', 'state')).to eq("draft") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the record doesn\'t exist no creator validate' do + context 'when the record doesn\'t exist no creators publish' do let(:doi_id) { "10.14454/077d-fj48" } let(:xml) { Base64.strict_encode64(file_fixture('datacite_missing_creator.xml').read) } let(:valid_attributes) do @@ -260,15 +478,7 @@ "attributes" => { "url" => "http://www.bl.uk/pdf/pat.pdf", "xml" => xml, - "validate" => "true" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -302,10 +512,7 @@ it 'updates the record' do expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - expect(json.dig('data', 'attributes', 'title')).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) end it 'returns status code 200' do @@ -319,7 +526,7 @@ context 'when the title is changed' do let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } - let(:title) { "Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N" } + let(:titles) { [{ "title" => "Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N" }] } let(:valid_attributes) do { "data" => { @@ -327,16 +534,8 @@ "attributes" => { "url" => "http://www.bl.uk/pdf/pat.pdf", "xml" => xml, - "title" => title, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "titles" => titles, + "event" => "publish" } } } @@ -346,24 +545,21 @@ it 'updates the record' do expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - expect(json.dig('data', 'attributes', 'title')).to eq(title) - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq(title) + expect(json.dig('data', 'attributes', 'titles')).to eq(titles) end it 'returns status code 200' do expect(response).to have_http_status(200) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the author changes' do + context 'when the creators change' do let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } - let(:author) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } + let(:creators) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } let(:valid_attributes) do { "data" => { @@ -371,16 +567,8 @@ "attributes" => { "url" => "http://www.bl.uk/pdf/pat.pdf", "xml" => xml, - "author" => author, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "creators" => creators, + "event" => "publish" } } } @@ -390,18 +578,15 @@ it 'updates the record' do expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - expect(json.dig('data', 'attributes', 'author')).to eq(author) - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("creators", "creator")).to eq([{"creatorName"=>"Ollomi, Benjamin"}, {"creatorName"=>"Duran, Patrick"}]) + expect(json.dig('data', 'attributes', 'creators')).to eq(creators) end it 'returns status code 200' do expect(response).to have_http_status(200) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end @@ -412,7 +597,8 @@ let(:doi) { create(:doi, client: client) } let(:new_client) { create(:client, symbol: "#{provider.symbol}.magic", provider: provider, password: ENV['MDS_PASSWORD']) } - ## Attributes MUST be empty + + # attributes MUST be empty let(:valid_attributes) {file_fixture('transfer.json').read } before { put "/dois/#{doi.doi}", params: valid_attributes, headers: provider_headers } @@ -422,14 +608,14 @@ end end - context 'passeswhen we transfer a DOI as provider' do - + context 'passes when we transfer a DOI as provider' do let(:provider_bearer) { User.generate_token(uid: "datacite", role_id: "provider_admin", name: "DataCite", email:"support@datacite.org", provider_id: "datacite") } let(:provider_headers) { {'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + provider_bearer}} let(:doi) { create(:doi, client: client) } let(:new_client) { create(:client, symbol: "#{provider.symbol}.magic", provider: provider, password: ENV['MDS_PASSWORD']) } - ## Attributes MUST be empty + + # attributes MUST be empty let(:valid_attributes) do { "data" => { @@ -457,13 +643,14 @@ end it 'updates the client id' do + # TODO: db-fields-for-attributes relates to delay in Elasticsearch indexing expect(json.dig('data', 'relationships', 'client','data','id')).to eq(new_client.symbol.downcase) - expect(json.dig('data', 'attributes', 'title')).to eq(doi.title) + expect(json.dig('data', 'attributes', 'titles')).to eq(doi.titles) end end - context 'when we transfer a DOI as staff' do - let(:doi) { create(:doi, doi: "10.24425/119495", client: client, state: "registered") } + context 'when we transfer a DOI as staff' do + let(:doi) { create(:doi, doi: "10.14454/119495", url: "http://www.bl.uk/pdf/pat.pdf", client: client, aasm_state: "registered") } let(:new_client) { create(:client, symbol: "#{provider.symbol}.magic", provider: provider, password: ENV['MDS_PASSWORD']) } let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:valid_attributes) do @@ -471,8 +658,7 @@ "data" => { "type" => "dois", "attributes" => { - "url" => "http://www.bl.uk/pdf/pat.pdf", - "xml" => xml + "mode" => "transfer" }, "relationships"=> { "client"=> { @@ -486,166 +672,257 @@ } end - before { put "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: admin_headers } + before { put "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: admin_headers } + + it 'returns no errors' do + puts response.body + expect(response).to have_http_status(200) + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi) + end + + it 'updates the client id' do + # TODO: db-fields-for-attributes relates to delay in Elasticsearch indexing + expect(json.dig('data', 'relationships', 'client','data','id')).to eq(new_client.symbol.downcase) + end + end + + context 'when the resource_type_general changes' do + let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } + let(:types) { { "resourceTypeGeneral" => "DataPaper", "resourceType" => "BlogPosting" } } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "types" => types, + "event" => "publish" + } + } + } + end + before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + + it 'updates the record' do + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + expect(json.dig('data', 'attributes', 'types')).to eq("resourceType"=>"BlogPosting", "resourceTypeGeneral"=>"DataPaper") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") + end + end + end + + describe 'POST /dois' do + context 'when the request is valid' do + let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish" + } + } + } + end + + before { post '/dois', params: valid_attributes.to_json, headers: headers } + + it 'creates a Doi' do + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(json.dig('data', 'attributes', 'creators')).to eq([{"familyName"=>"Fenner", + "givenName"=>"Martin", + "name"=>"Fenner, Martin", + "nameIdentifiers"=> + [{"nameIdentifier"=>"https://orcid.org/0000-0003-1419-2405", + "nameIdentifierScheme"=>"ORCID"}], + "nameType"=>"Personal"}]) + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + expect(json.dig('data', 'attributes', 'source')).to eq("test") + expect(json.dig('data', 'attributes', 'types')).to eq("bibtex"=>"article", "citeproc"=>"article-journal", "resourceType"=>"BlogPosting", "resourceTypeGeneral"=>"Text", "ris"=>"RPRT", "schemaOrg"=>"ScholarlyArticle") + + doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + expect(doc.at_css("identifier").content).to eq("10.14454/10703") + end + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") + end + end + + context 'when the request is valid with attributes' do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "types" => { "bibtex"=>"article", "citeproc"=>"article-journal", "resourceType"=>"BlogPosting", "resourceTypeGeneral"=>"Text", "ris"=>"RPRT", "schemaOrg"=>"ScholarlyArticle" }, + "titles" => [{"title"=>"Eating your own Dog Food"}], + "publisher" => "DataCite", + "publicationYear" => 2016, + "creators" => [{"familyName"=>"Fenner", "givenName"=>"Martin", "id"=>"https://orcid.org/0000-0003-1419-2405", "name"=>"Fenner, Martin", "type"=>"Person"}], + "source" => "test", + "event" => "publish" + } + } + } + end + + before { post '/dois', params: valid_attributes.to_json, headers: headers } + + it 'creates a Doi' do + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(json.dig('data', 'attributes', 'creators')).to eq( [{"familyName"=>"Fenner", "givenName"=>"Martin", "id"=>"https://orcid.org/0000-0003-1419-2405", "name"=>"Fenner, Martin", "type"=>"Person"}]) + expect(json.dig('data', 'attributes', 'publisher')).to eq("DataCite") + expect(json.dig('data', 'attributes', 'publicationYear')).to eq(2016) + # expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + expect(json.dig('data', 'attributes', 'source')).to eq("test") + expect(json.dig('data', 'attributes', 'types')).to eq("bibtex"=>"article", "citeproc"=>"article-journal", "resourceType"=>"BlogPosting", "resourceTypeGeneral"=>"Text", "ris"=>"RPRT", "schemaOrg"=>"ScholarlyArticle") + doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + expect(doc.at_css("identifier").content).to eq("10.14454/10703") + end + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") + end + end + + context 'schema_org' do + let(:xml) { Base64.strict_encode64(file_fixture('schema_org_topmed.json').read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "https://ors.datacite.org/doi:/10.14454/8na3-9s47", + "xml" => xml, + "source" => "test", + "event" => "publish" + } + } + } + end + + before { patch "/dois/10.14454/8na3-9s47", params: valid_attributes.to_json, headers: headers } + + it 'updates the record' do + expect(json.dig('data', 'attributes', 'url')).to eq("https://ors.datacite.org/doi:/10.14454/8na3-9s47") + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/8na3-9s47") + expect(json.dig('data', 'attributes', 'contentUrl')).to eq(["s3://cgp-commons-public/topmed_open_access/197bc047-e917-55ed-852d-d563cdbc50e4/NWD165827.recab.cram", "gs://topmed-irc-share/public/NWD165827.recab.cram"]) + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"NWD165827.recab.cram"}]) - it 'returns no errors' do - expect(response).to have_http_status(200) - expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi) + xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) + expect(xml.dig("titles", "title")).to eq("NWD165827.recab.cram") end - it 'updates the client id' do - expect(json.dig('data', 'relationships', 'client','data','id')).to eq(new_client.symbol.downcase) + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the resource_type_general changes' do - let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } - let(:resource_type_general) { "data-paper" } + context 'crossref url', vcr: true do + let(:xml) { Base64.strict_encode64("https://doi.org/10.7554/elife.01567") } let(:valid_attributes) do { "data" => { "type" => "dois", "attributes" => { - "url" => "http://www.bl.uk/pdf/pat.pdf", + "url" => "https://elifesciences.org/articles/01567", "xml" => xml, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - }, - "resource-type"=> { - "data"=> { - "type"=> "resource-types", - "id"=> resource_type_general - } - } + "source" => "test", + "event" => "publish" } } } end - before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + + before { patch "/dois/10.14454/elife.01567", params: valid_attributes.to_json, headers: headers } it 'updates the record' do - expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") - expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - # expect(json.dig('data', 'relationships', 'resource-type')).to eq(2) + expect(json.dig('data', 'attributes', 'url')).to eq("https://elifesciences.org/articles/01567") + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/elife.01567") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("resourceType")).to eq("resourceTypeGeneral"=>"DataPaper", "__content__"=>"BlogPosting") + expect(xml.dig("titles", "title")).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") end - it 'returns status code 200' do - expect(response).to have_http_status(200) + it 'returns status code 201' do + puts response.body + expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - end - - describe 'POST /dois' do - before(:each) do - Rails.cache.clear - end - context 'when the request is valid' do - let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } + context 'datacite url', vcr: true do + let(:xml) { Base64.strict_encode64("https://doi.org/10.7272/q6g15xs4") } let(:valid_attributes) do { "data" => { "type" => "dois", "attributes" => { - "doi" => "10.14454/10703", - "url" => "http://www.bl.uk/pdf/patspec.pdf", + "url" => "https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4", "xml" => xml, "source" => "test", - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } end - before { post '/dois', params: valid_attributes.to_json, headers: headers } + before { patch "/dois/10.14454/q6g15xs4", params: valid_attributes.to_json, headers: headers } - it 'creates a Doi' do - expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") - expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - expect(json.dig('data', 'attributes', 'schema-version')).to eq("http://datacite.org/schema/kernel-4") - expect(json.dig('data', 'attributes', 'source')).to eq("test") - expect(json.dig('data', 'relationships', 'resource-type', 'data', 'id')).to eq("text") + it 'updates the record' do + expect(json.dig('data', 'attributes', 'url')).to eq("https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4") + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/q6g15xs4") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"NEXUS Head CT"}]) xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("resourceType")).to eq("resourceTypeGeneral"=>"Text", "__content__"=>"BlogPosting") + expect(xml.dig("titles", "title")).to eq("NEXUS Head CT") end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - # context 'schema_org' do - # let(:xml) { Base64.strict_encode64(file_fixture('schema_org_topmed.json').read) } - # let(:valid_attributes) do - # { - # "data" => { - # "type" => "dois", - # "attributes" => { - # "url" => "https://ors.datacite.org/doi:/10.14454/8na3-9s47", - # "xml" => xml, - # "source" => "test", - # "event" => "register" - # } - # }, - # "relationships"=> { - # "client"=> { - # "data"=> { - # "type"=> "clients", - # "id"=> client.symbol.downcase - # } - # } - # } - # } - # end - - # before { patch "/dois/10.14454/8na3-9s47", params: valid_attributes.to_json, headers: headers } - - # it 'updates the record' do - # expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/pat.pdf") - # expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) - # expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - - # xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - # expect(xml.dig("titles", "title")).to eq("Eating your own Dog Food") - # end - - # it 'returns status code 200' do - # puts response.body - # expect(response).to have_http_status(200) - # end - - # it 'sets state to draft' do - # expect(json.dig('data', 'attributes', 'state')).to eq("draft") - # end - # end - context 'when the request uses schema 3' do let(:xml) { Base64.strict_encode64(file_fixture('datacite_schema_3.xml').read) } let(:valid_attributes) do @@ -657,15 +934,7 @@ "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, "source" => "test", - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -676,17 +945,17 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Data from: A new malaria agent in African hominids.") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) expect(json.dig('data', 'attributes', 'source')).to eq("test") - # expect(json.dig('data', 'attributes', 'schema-version')).to eq("http://datacite.org/schema/kernel-3") + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-3") end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end @@ -700,15 +969,7 @@ # "doi" => "10.14454/10703", # "url" => "http://www.bl.uk/pdf/patspec.pdf", # "xml" => xml, - # "event" => "register" - # }, - # "relationships"=> { - # "client"=> { - # "data"=> { - # "type"=> "clients", - # "id"=> client.symbol.downcase - # } - # } + # "event" => "publish" # } # } # } @@ -719,7 +980,16 @@ # it 'creates a Doi' do # expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") # expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - # expect(json.dig('data', 'attributes', 'title')).to eq("A dataset with a large file for testing purpose. Will be a but over 2.5 MB") + + # expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"A dataset with a large file for testing purpose. Will be a but over 2.5 MB"}]) + # expect(json.dig('data', 'attributes', 'creators')).to eq([{"familyName"=>"Testing", "givenName"=>"Chris Baars At DANS For", "name"=>"Chris Baars At DANS For Testing", "type"=>"Person"}]) + # expect(json.dig('data', 'attributes', 'publisher')).to eq("DANS/KNAW") + # expect(json.dig('data', 'attributes', 'publicationYear')).to eq(2018) + # expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + # expect(json.dig('data', 'attributes', 'types')).to eq("bibtex"=>"misc", "citeproc"=>"dataset", "resourceType"=>"Dataset", "resourceTypeGeneral"=>"Dataset", "ris"=>"DATA", "schemaOrg"=>"Dataset") + + # doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + # expect(doc.at_css("identifier").content).to eq("10.14454/10703") # end # it 'returns status code 201' do @@ -737,15 +1007,7 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -755,16 +1017,16 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("LAMMPS Data-File Generator") - # expect(json.dig('data', 'attributes', 'schema-version')).to eq("http://datacite.org/schema/kernel-3") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"LAMMPS Data-File Generator"}]) + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-2.2") end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end @@ -778,15 +1040,7 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -796,21 +1050,20 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Southern Sierra Critical Zone Observatory (SSCZO), Providence Creek\n meteorological data, soil moisture and temperature, snow depth and air\n temperature") - expect(json.dig('data', 'attributes', 'schema-version')).to eq("http://datacite.org/schema/kernel-4") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Southern Sierra Critical Zone Observatory (SSCZO), Providence Creek\n meteorological data, soil moisture and temperature, snow depth and air\n temperature"}]) + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the request uses namespaced xml and the title changes' do - let(:title) { "Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]" } + context 'when the request uses namespaced xml' do let(:xml) { Base64.strict_encode64(file_fixture('ns0.xml').read) } let(:valid_attributes) do { @@ -820,16 +1073,7 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, - "title" => title, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -839,22 +1083,21 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]") - # expect(json.dig('data', 'attributes', 'schema-version')).to eq("http://datacite.org/schema/kernel-3") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"LAMMPS Data-File Generator"}]) + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-2.2") end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the title changes' do - let(:title) { "Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]" } + let(:titles) { { "title" => "Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]" } } let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:valid_attributes) do { @@ -865,16 +1108,8 @@ "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, "source" => "test", - "title" => title, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "titles" => titles, + "event" => "publish" } } } @@ -884,20 +1119,17 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]") + expect(json.dig('data', 'attributes', 'titles')).to eq("title"=>"Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]") expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") expect(json.dig('data', 'attributes', 'source')).to eq("test") - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq(title) end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end @@ -912,15 +1144,7 @@ "doi" => "10.14454/10703", "url" => url, "xml" => xml, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -937,12 +1161,12 @@ expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the title changes to nil' do + context 'when the titles changes to nil' do let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:valid_attributes) do { @@ -952,16 +1176,8 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, - "title" => nil, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "titles" => nil, + "event" => "publish" } } } @@ -971,23 +1187,21 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq("Eating your own Dog Food") end it 'returns status code 201' do + expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the title changes to blank' do + context 'when the titles changes to blank' do let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:valid_attributes) do { @@ -997,16 +1211,8 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, - "title" => '', - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.uid - } - } + "titles" => nil, + "event" => "publish" } } } @@ -1014,27 +1220,23 @@ before { post '/dois', params: valid_attributes.to_json, headers: headers } - # it 'creates a Doi' do - # expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - # expect(json.dig('data', 'attributes', 'title')).to eq("") - # expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") - - # xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - # expect(xml.dig("titles", "title")).to be_nil - # end + it 'creates a Doi' do + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") + end - # it 'returns status code 201' do - # expect(response.body).to eq(2) - # expect(response).to have_http_status(201) - # end + it 'returns status code 201' do + expect(response).to have_http_status(201) + end - # it 'sets state to registered' do - # expect(json.dig('data', 'attributes', 'state')).to eq("registered") - # end + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") + end end - context 'when the author changes' do - let(:author) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } + context 'when the creators change' do + let(:creators) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } let(:valid_attributes) do { @@ -1044,16 +1246,8 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, - "author" => author, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "creators" => creators, + "event" => "publish" } } } @@ -1063,24 +1257,21 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'author')).to eq(author) + expect(json.dig('data', 'attributes', 'creators')).to eq(creators) expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("creators", "creator")).to eq([{"creatorName"=>"Ollomi, Benjamin"}, {"creatorName"=>"Duran, Patrick"}]) end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end - context 'when the author changes no xml' do - let(:author) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } + context 'creators no xml' do + let(:creators) { [{ "name"=>"Ollomi, Benjamin" }, { "name"=>"Duran, Patrick" }] } let(:valid_attributes) do { "data" => { @@ -1089,16 +1280,8 @@ "doi" => "10.14454/10703", "url" => "http://www.bl.uk/pdf/patspec.pdf", "xml" => nil, - "author" => author, + "creators" => creators, "event" => "publish" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1106,23 +1289,13 @@ before { post '/dois', params: valid_attributes.to_json, headers: headers } - # it 'creates a Doi' do - # expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - # expect(json.dig('data', 'attributes', 'author')).to eq(author) - # expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") - - # xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - # expect(xml.dig("creators", "creator")).to eq([{"creatorName"=>"Ollomi, Benjamin"}, {"creatorName"=>"Duran, Patrick"}]) - # end - - # it 'returns status code 201' do - # expect(response.body).to eq(2) - # expect(response).to have_http_status(201) - # end + it 'returns validation error' do + expect(json.dig('errors')).to eq([{"source"=>"metadata", "title"=>"Is invalid"}]) + end - # it 'sets state to registered' do - # expect(json.dig('data', 'attributes', 'state')).to eq("draft") - # end + it 'returns status code 422' do + expect(response).to have_http_status(422) + end end context 'state change with test prefix' do @@ -1136,15 +1309,7 @@ "attributes" => { "doi" => "10.5072/10704", "url" => "http://www.bl.uk/pdf/patspec.pdf", - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "event" => "publish" } } } @@ -1173,14 +1338,6 @@ "attributes" => { "doi" => "10.aaaa03", "url"=> "http://www.bl.uk/pdf/patspec.pdf", - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1206,14 +1363,6 @@ "doi" => "10.14454/10703", "url"=> "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1226,36 +1375,23 @@ it 'creates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") - expect(json.dig('data', 'attributes', 'author')).to be_blank - - xml = Maremma.from_xml(Base64.decode64(json.dig('data', 'attributes', 'xml'))).fetch("resource", {}) - expect(xml.dig("titles", "title")).to eq("Eating your own Dog Food") - expect(xml.dig("creators", "creator")).to be_nil + expect(json.dig('data', 'attributes', 'creators')).to be_blank end end context 'when the xml is invalid' do - let(:doi) { create(:doi, client: client, doi: "10.14454/4f6f-zr33") } let(:xml) { Base64.strict_encode64(file_fixture('datacite_missing_creator.xml').read) } let(:not_valid_attributes) do { "data" => { "type" => "dois", "attributes" => { - "doi" => doi.doi, + "doi" => "10.14454/4K3M-NYVG", "url"=> "http://www.bl.uk/pdf/patspec.pdf", "xml" => xml, "event" => "publish" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1272,9 +1408,6 @@ end describe 'POST /dois/validate' do - let(:bearer) { User.generate_token(role_id: "client_admin", client_id: client.symbol.downcase) } - let(:headers) { {'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + bearer}} - context 'validates' do let(:xml) { ::Base64.strict_encode64(File.read(file_fixture('datacite.xml'))) } let(:params) do @@ -1284,14 +1417,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1301,8 +1426,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - expect(json.dig('data', 'attributes', 'published')).to eq("2016-12-20") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2016-12-20", "dateType"=>"Created"}, {"date"=>"2016-12-20", "dateType"=>"Issued"}, {"date"=>"2016-12-20", "dateType"=>"Updated"}]) end it 'returns status code 200' do @@ -1319,14 +1444,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1336,8 +1453,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Data from: A new malaria agent in African hominids.") - expect(json.dig('data', 'attributes', 'published')).to eq("2011") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2011", "dateType"=>"Issued"}]) end it 'returns status code 200' do @@ -1345,23 +1462,15 @@ end end - context 'when the creator is missing' do + context 'when the creators are missing' do let(:xml) { ::Base64.strict_encode64(File.read(file_fixture('datacite_missing_creator.xml'))) } let(:params) do { "data" => { "type" => "dois", "attributes" => { - "doi" => "10.14454/10703", - "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "doi" => "10.14454/10703", + "xml" => xml } } } @@ -1379,7 +1488,7 @@ end end - context 'when the creator is malformed' do + context 'when the creators are malformed' do let(:xml) { ::Base64.strict_encode64(File.read(file_fixture('datacite_malformed_creator.xml'))) } let(:params) do { @@ -1388,14 +1497,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1422,14 +1523,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1439,8 +1532,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - expect(json.dig('data', 'attributes', 'published')).to eq("2016-12-20") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2016-12-20", "dateType"=>"Issued"}]) end it 'returns status code 200' do @@ -1457,14 +1550,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1474,8 +1559,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("R Interface to the DataONE REST API") - expect(json.dig('data', 'attributes', 'published')).to eq("2016-05-27") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"R Interface to the DataONE REST API"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2016-05-27", "dateType"=>"Issued"}, {"date"=>"2016-05-27", "dateType"=>"Created"}, {"date"=>"2016-05-27", "dateType"=>"Updated"}]) end it 'returns status code 200' do @@ -1492,14 +1577,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1509,8 +1586,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Analysis Tools for Crossover Experiment of UI using Choice Architecture") - expect(json.dig('data', 'attributes', 'published')).to eq("2016-03-27") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Analysis Tools for Crossover Experiment of UI using Choice Architecture"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq("date"=>"2016-03-27", "dateType"=>"Issued") end it 'returns status code 200' do @@ -1527,14 +1604,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1544,8 +1613,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") - expect(json.dig('data', 'attributes', 'published')).to eq("2014") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2014", "dateType"=>"Issued"}]) end it 'returns status code 200' do @@ -1561,15 +1630,7 @@ "type" => "dois", "attributes" => { "doi" => "10.14454/10703", - "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "xml" => xml } } } @@ -1579,8 +1640,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth") - expect(json.dig('data', 'attributes', 'published')).to eq("2014") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2014", "dateType"=>"Issued"}]) end it 'returns status code 200' do @@ -1597,14 +1658,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1614,8 +1667,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes") - expect(json.dig('data', 'attributes', 'published')).to eq("2006-12-20") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Triose Phosphate Isomerase Deficiency Is Caused by Altered Dimerization–Not Catalytic Inactivity–of the Mutant Enzymes"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2006-12-20", "dateType"=>"Issued"}, {"date"=>"2017-01-01T03:37:08Z", "dateType"=>"Updated"}]) end it 'returns status code 200' do @@ -1632,14 +1685,6 @@ "attributes" => { "doi" => "10.14454/10703", "xml" => xml, - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } } } } @@ -1649,8 +1694,8 @@ it 'validates a Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'title')).to eq("Eating your own Dog Food") - expect(json.dig('data', 'attributes', 'published')).to eq("2016-12-20") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Eating your own Dog Food"}]) + expect(json.dig('data', 'attributes', 'dates')).to eq([{"date"=>"2016-12-20", "dateType"=>"Issued"}, {"date"=>"2016-12-20", "dateType"=>"Created"}, {"date"=>"2016-12-20", "dateType"=>"Updated"}]) end it 'returns status code 200' do @@ -1659,19 +1704,73 @@ end end + context 'update individual attribute' do + let(:xml) { Base64.strict_encode64(file_fixture('datacite_schema_3.xml').read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish" + } + } + } + end + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "schemaVersion" => "http://datacite.org/schema/kernel-4", + "regenerate" => true + } + } + } + end + + before { post '/dois', params: valid_attributes.to_json, headers: headers } + + it 'creates a Doi' do + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"Data from: A new malaria agent in African hominids."}]) + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-3") + + doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + expect(doc.collect_namespaces).to eq("xmlns" => "http://datacite.org/schema/kernel-3","xmlns:dim" => "http://www.dspace.org/xmlns/dspace/dim","xmlns:dryad" => "http://purl.org/dryad/terms/","xmlns:dspace" => "http://www.dspace.org/xmlns/dspace/dim","xmlns:mets" => "http://www.loc.gov/METS/","xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance") + end + + it 'updates to schema 4.0' do + put "/dois/10.14454/10703", params: update_attributes.to_json, headers: headers + + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + + doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + expect(doc.collect_namespaces).to eq("xmlns"=>"http://datacite.org/schema/kernel-4", "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance") + end + end + context 'landing page' do let(:url) { "https://blog.datacite.org/re3data-science-europe/" } - let(:xml) { "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48cmVzb3VyY2UgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM9Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC00IiB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC00IGh0dHA6Ly9zY2hlbWEuZGF0YWNpdGUub3JnL21ldGEva2VybmVsLTQvbWV0YWRhdGEueHNkIj48aWRlbnRpZmllciBpZGVudGlmaWVyVHlwZT0iRE9JIj4xMC4yNTQ5OS94dWRhMnB6cmFocm9lcXBlZnZucTV6dDZkYzwvaWRlbnRpZmllcj48Y3JlYXRvcnM+PGNyZWF0b3I+PGNyZWF0b3JOYW1lPklhbiBQYXJyeTwvY3JlYXRvck5hbWU+PG5hbWVJZGVudGlmaWVyIHNjaGVtZVVSST0iaHR0cDovL29yY2lkLm9yZy8iIG5hbWVJZGVudGlmaWVyU2NoZW1lPSJPUkNJRCI+MDAwMC0wMDAxLTYyMDItNTEzWDwvbmFtZUlkZW50aWZpZXI+PC9jcmVhdG9yPjwvY3JlYXRvcnM+PHRpdGxlcz48dGl0bGU+U3VibWl0dGVkIGNoZW1pY2FsIGRhdGEgZm9yIEluQ2hJS2V5PVlBUFFCWFFZTEpSWFNBLVVIRkZGQU9ZU0EtTjwvdGl0bGU+PC90aXRsZXM+PHB1Ymxpc2hlcj5Sb3lhbCBTb2NpZXR5IG9mIENoZW1pc3RyeTwvcHVibGlzaGVyPjxwdWJsaWNhdGlvblllYXI+MjAxNzwvcHVibGljYXRpb25ZZWFyPjxyZXNvdXJjZVR5cGUgcmVzb3VyY2VUeXBlR2VuZXJhbD0iRGF0YXNldCI+U3Vic3RhbmNlPC9yZXNvdXJjZVR5cGU+PHJpZ2h0c0xpc3Q+PHJpZ2h0cyByaWdodHNVUkk9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9zaGFyZS15b3VyLXdvcmsvcHVibGljLWRvbWFpbi9jYzAvIj5ObyBSaWdodHMgUmVzZXJ2ZWQ8L3JpZ2h0cz48L3JpZ2h0c0xpc3Q+PC9yZXNvdXJjZT4=" } - let(:link_check_result) { { + let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } + let(:landingPage) { { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => url, + "contentType" => "text/html", "error" => nil, - "redirect-count" => 0, - "redirect-urls" => [], - "download-latency" => 200, - "has-schema-org" => true, - "schema-org-id" => "10.14454/10703", - "dc-identifier" => nil, - "citation-doi" => nil, - "body-has-pid" => true + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true } } let(:valid_attributes) do { @@ -1681,20 +1780,8 @@ "doi" => "10.14454/10703", "url" => url, "xml" => xml, - "last-landing-page" => url, - "last-landing-page-status" => 200, - "last-landing-page-status-check" => Time.zone.now, - "last-landing-page-content-type" => "text/html", - "last-landing-page-status-result" => link_check_result, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "landingPage" => landingPage, + "event" => "publish" } } } @@ -1702,11 +1789,10 @@ before { post '/dois', params: valid_attributes.to_json, headers: headers } - it 'creates a Doi' do + it 'creates a doi' do expect(json.dig('data', 'attributes', 'url')).to eq(url) expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'landing-page', 'status')).to eq(200) - expect(json.dig('data', 'attributes', 'landing-page', 'result')).to eq(link_check_result) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(landingPage) end it 'returns status code 201' do @@ -1714,29 +1800,79 @@ end it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + expect(json.dig('data', 'attributes', 'state')).to eq("findable") + end + end + + context 'update with landing page info as admin' do + let(:url) { "https://blog.datacite.org/re3data-science-europe/" } + let(:doi) { create(:doi, doi: "10.14454/10703", url: url, client: client) } + let(:landingPage) { { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => url, + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true + } } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "landingPage" => landingPage, + "event" => "publish" + } + } + } + end + + before { put "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: admin_headers } + + it 'creates a doi' do + expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + expect(json.dig('data', 'attributes', 'landingPage')).to eq(landingPage) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end context 'landing page schema-org-id hash' do let(:url) { "https://blog.datacite.org/re3data-science-europe/" } - let(:xml) { "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48cmVzb3VyY2UgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM9Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC00IiB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly9kYXRhY2l0ZS5vcmcvc2NoZW1hL2tlcm5lbC00IGh0dHA6Ly9zY2hlbWEuZGF0YWNpdGUub3JnL21ldGEva2VybmVsLTQvbWV0YWRhdGEueHNkIj48aWRlbnRpZmllciBpZGVudGlmaWVyVHlwZT0iRE9JIj4xMC4yNTQ5OS94dWRhMnB6cmFocm9lcXBlZnZucTV6dDZkYzwvaWRlbnRpZmllcj48Y3JlYXRvcnM+PGNyZWF0b3I+PGNyZWF0b3JOYW1lPklhbiBQYXJyeTwvY3JlYXRvck5hbWU+PG5hbWVJZGVudGlmaWVyIHNjaGVtZVVSST0iaHR0cDovL29yY2lkLm9yZy8iIG5hbWVJZGVudGlmaWVyU2NoZW1lPSJPUkNJRCI+MDAwMC0wMDAxLTYyMDItNTEzWDwvbmFtZUlkZW50aWZpZXI+PC9jcmVhdG9yPjwvY3JlYXRvcnM+PHRpdGxlcz48dGl0bGU+U3VibWl0dGVkIGNoZW1pY2FsIGRhdGEgZm9yIEluQ2hJS2V5PVlBUFFCWFFZTEpSWFNBLVVIRkZGQU9ZU0EtTjwvdGl0bGU+PC90aXRsZXM+PHB1Ymxpc2hlcj5Sb3lhbCBTb2NpZXR5IG9mIENoZW1pc3RyeTwvcHVibGlzaGVyPjxwdWJsaWNhdGlvblllYXI+MjAxNzwvcHVibGljYXRpb25ZZWFyPjxyZXNvdXJjZVR5cGUgcmVzb3VyY2VUeXBlR2VuZXJhbD0iRGF0YXNldCI+U3Vic3RhbmNlPC9yZXNvdXJjZVR5cGU+PHJpZ2h0c0xpc3Q+PHJpZ2h0cyByaWdodHNVUkk9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9zaGFyZS15b3VyLXdvcmsvcHVibGljLWRvbWFpbi9jYzAvIj5ObyBSaWdodHMgUmVzZXJ2ZWQ8L3JpZ2h0cz48L3JpZ2h0c0xpc3Q+PC9yZXNvdXJjZT4=" } - let(:link_check_result) { { + let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } + let(:landingPage) { { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => url, + "contentType" => "text/html", "error" => nil, - "redirect-count" => 0, - "redirect-urls" => [], - "download-latency" => 200, - "has-schema-org" => true, - "schema-org-id" => [ + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => [ { "@type" => "PropertyValue", "value" => "http://dx.doi.org/10.4225/06/564AB348340D5", "propertyID" => "URL" } ], - "dc-identifier" => nil, - "citation-doi" => nil, - "body-has-pid" => true + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true } } let(:valid_attributes) do { @@ -1746,20 +1882,8 @@ "doi" => "10.14454/10703", "url" => url, "xml" => xml, - "last-landing-page" => url, - "last-landing-page-status" => 200, - "last-landing-page-status-check" => Time.zone.now, - "last-landing-page-content-type" => "text/html", - "last-landing-page-status-result" => link_check_result, - "event" => "register" - }, - "relationships"=> { - "client"=> { - "data"=> { - "type"=> "clients", - "id"=> client.symbol.downcase - } - } + "landingPage" => landingPage, + "event" => "publish" } } } @@ -1768,19 +1892,17 @@ before { post '/dois', params: valid_attributes.to_json, headers: headers } it 'creates a Doi' do - puts response.body expect(json.dig('data', 'attributes', 'url')).to eq(url) expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") - expect(json.dig('data', 'attributes', 'landing-page', 'status')).to eq(200) - expect(json.dig('data', 'attributes', 'landing-page', 'result')).to eq(link_check_result) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(landingPage) end it 'returns status code 201' do expect(response).to have_http_status(201) end - it 'sets state to registered' do - expect(json.dig('data', 'attributes', 'state')).to eq("registered") + it 'sets state to findable' do + expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end end @@ -1884,16 +2006,20 @@ end describe 'GET /dois/ linkcheck results' do - let(:last_landing_page_status_result) { { + let(:landing_page) { { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => "http://example.com", + "contentType" => "text/html", "error" => nil, - "redirect-count" => 0, - "redirect-urls" => [], - "download-latency" => 200, - "has-schema-org" => true, - "schema-org-id" => "10.14454/10703", - "dc-identifier" => nil, - "citation-doi" => nil, - "body-has-pid" => true + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true } } # Setup an initial DOI with results will check permissions against. @@ -1903,7 +2029,7 @@ client: client, state: "findable", event: 'publish', - last_landing_page_status_result: last_landing_page_status_result + landing_page: landing_page ) } @@ -1916,7 +2042,7 @@ client: other_client, state: "findable", event: 'publish', - last_landing_page_status_result: last_landing_page_status_result + landing_page: landing_page ) } @@ -1924,10 +2050,9 @@ let(:headers) { { 'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json' } } before { get "/dois/#{doi.doi}", headers: headers} - it 'returns without link_check_results' do - puts json + it 'returns without landing page results' do expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi) - expect(json.dig('data', 'attributes', 'landing-page', 'result')).to eq(nil) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(nil) end end @@ -1937,9 +2062,9 @@ before { get "/dois/#{doi.doi}", headers: headers } - it 'returns with link_check_results' do + it 'returns with landing page results' do expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi) - expect(json.dig('data', 'attributes', 'landing-page', 'result')).to eq(last_landing_page_status_result) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(landing_page) end end @@ -1950,9 +2075,9 @@ before { get "/dois/#{other_doi.doi}", headers: headers } - it 'returns with link_check_results' do + it 'returns with landing page results' do expect(json.dig('data', 'attributes', 'doi')).to eq(other_doi.doi) - expect(json.dig('data', 'attributes', 'landing-page', 'result')).to eq(nil) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(nil) end end @@ -1963,9 +2088,9 @@ before { get "/dois/#{doi.doi}", headers: headers } - it 'returns with link_check_results' do + it 'returns with landing page results' do expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi) - expect(json.dig('data', 'attributes', 'landing-page', 'result')).to eq(last_landing_page_status_result) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(landing_page) end end @@ -2058,7 +2183,7 @@ end describe 'GET /dois/DOI/get-url', vcr: true do - let(:doi) { create(:doi, client: client, doi: "10.5438/fj3w-0shd", event: "publish") } + let(:doi) { create(:doi, client: client, doi: "10.5438/fj3w-0shd", url: "https://blog.datacite.org/data-driven-development/", event: "publish") } before { get "/dois/#{doi.doi}/get-url", headers: headers } @@ -2102,7 +2227,7 @@ describe 'GET /dois/DOI/get-url no permission', vcr: true do let(:other_client) { create(:client, provider: provider) } - let(:doi) { create(:doi, client: other_client, doi: "10.5438/8syz-ym47", event: "publish") } + let(:doi) { create(:doi, client: other_client, doi: "10.14454/8syz-ym47", event: "publish") } before { get "/dois/#{doi.doi}/get-url", headers: headers } @@ -2184,4 +2309,370 @@ expect(response).to have_http_status(401) end end + + describe "content_negotation", type: :request do + let(:provider) { create(:provider, symbol: "DATACITE") } + let(:client) { create(:client, provider: provider, symbol: ENV['MDS_USERNAME'], password: ENV['MDS_PASSWORD']) } + let(:bearer) { Client.generate_token(role_id: "client_admin", uid: client.symbol, provider_id: provider.symbol.downcase, client_id: client.symbol.downcase, password: client.password) } + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + + context "no permission" do + let(:doi) { create(:doi) } + + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns error message' do + expect(json["errors"]).to eq([{"status"=>"403", "title"=>"You are not authorized to access this resource."}]) + end + + it 'returns status code 403' do + expect(response).to have_http_status(403) + end + end + + context "no authentication" do + let(:doi) { create(:doi) } + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml" } } + + it 'returns error message' do + expect(json["errors"]).to eq([{"status"=>"401", "title"=>"Bad credentials."}]) + end + + it 'returns status code 401' do + expect(response).to have_http_status(401) + end + end + + context "application/vnd.jats+xml" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + jats = Maremma.from_xml(response.body).fetch("element_citation", {}) + expect(jats.dig("publication_type")).to eq("data") + expect(jats.dig("data_title")).to eq("Data from: A new malaria agent in African hominids.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.jats+xml link" do + before { get "/dois/application/vnd.jats+xml/#{doi.doi}" } + + it 'returns the Doi' do + jats = Maremma.from_xml(response.body).fetch("element_citation", {}) + expect(jats.dig("publication_type")).to eq("data") + expect(jats.dig("data_title")).to eq("Data from: A new malaria agent in African hominids.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.datacite.datacite+xml" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) + expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") + expect(data.dig("publisher")).to eq("Dryad Digital Repository") + expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.datacite.datacite+xml link" do + before { get "/dois/application/vnd.datacite.datacite+xml/#{doi.doi}" } + + it 'returns the Doi' do + data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) + expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") + expect(data.dig("publisher")).to eq("Dryad Digital Repository") + expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.datacite.datacite+xml schema 3" do + let(:xml) { file_fixture('datacite_schema_3.xml').read } + let(:doi) { create(:doi, xml: xml, client: client, regenerate: false) } + + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) + expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-3") + expect(data.dig("publisher")).to eq("Dryad Digital Repository") + expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + # context "no metadata" do + # let(:doi) { create(:doi, xml: nil, client: client) } + + # before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } + + # it 'returns the Doi' do + # expect(response.body).to eq('') + # end + + # it 'returns status code 200' do + # expect(response).to have_http_status(200) + # end + # end + + context "application/vnd.datacite.datacite+xml not found" do + before { get "/dois/xxx", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns error message' do + expect(json["errors"]).to eq([{"status"=>"404", "title"=>"The resource you are looking for doesn't exist."}]) + end + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + end + + context "application/vnd.datacite.datacite+json" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+json", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(json["doi"]).to eq(doi.doi) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.datacite.datacite+json link" do + before { get "/dois/application/vnd.datacite.datacite+json/#{doi.doi}" } + + it 'returns the Doi' do + expect(json["doi"]).to eq(doi.doi) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.crosscite.crosscite+json" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.crosscite.crosscite+json", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(json["doi"]).to eq(doi.doi) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.crosscite.crosscite+json link" do + before { get "/dois/application/vnd.crosscite.crosscite+json/#{doi.doi}" } + + it 'returns the Doi' do + expect(json["doi"]).to eq(doi.doi) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.schemaorg.ld+json" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.schemaorg.ld+json", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(json["@type"]).to eq("Dataset") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.schemaorg.ld+json link" do + before { get "/dois/application/vnd.schemaorg.ld+json/#{doi.doi}" } + + it 'returns the Doi' do + expect(json["@type"]).to eq("Dataset") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.citationstyles.csl+json" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.citationstyles.csl+json", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(json["type"]).to eq("dataset") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/vnd.citationstyles.csl+json link" do + before { get "/dois/application/vnd.citationstyles.csl+json/#{doi.doi}" } + + it 'returns the Doi' do + expect(json["type"]).to eq("dataset") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/x-research-info-systems" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-research-info-systems", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(response.body).to start_with("TY - DATA") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/x-research-info-systems link" do + before { get "/dois/application/x-research-info-systems/#{doi.doi}" } + + it 'returns the Doi' do + expect(response.body).to start_with("TY - DATA") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/x-bibtex" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-bibtex", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(response.body).to start_with("@misc{https://doi.org/#{doi.doi.downcase}") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "application/x-bibtex link" do + before { get "/dois/application/x-bibtex/#{doi.doi}" } + + it 'returns the Doi' do + expect(response.body).to start_with("@misc{https://doi.org/#{doi.doi.downcase}") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "text/x-bibliography", vcr: true do + context "default style" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(response.body).to start_with("Ollomo, B.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "default style link" do + before { get "/dois/text/x-bibliography/#{doi.doi}" } + + it 'returns the Doi' do + expect(response.body).to start_with("Ollomo, B.") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "ieee style" do + before { get "/dois/#{doi.doi}?style=ieee", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(response.body).to start_with("B. Ollomo") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "ieee style link" do + before { get "/dois/text/x-bibliography/#{doi.doi}?style=ieee" } + + it 'returns the Doi' do + expect(response.body).to start_with("B. Ollomo") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context "style and locale" do + before { get "/dois/#{doi.doi}?style=vancouver&locale=de", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(response.body).to start_with("Ollomo B") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + end + + context "unknown content type" do + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "text/csv", 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(json["errors"]).to eq([{"status"=>"406", "title"=>"The content type is not recognized."}]) + end + + it 'returns status code 406' do + expect(response).to have_http_status(406) + end + end + + context "missing content type" do + before { get "/dois/#{doi.doi}", headers: { 'Authorization' => 'Bearer ' + bearer } } + + it 'returns the Doi' do + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + end end diff --git a/spec/requests/index_spec.rb b/spec/requests/index_spec.rb deleted file mode 100644 index f40c29e9a..000000000 --- a/spec/requests/index_spec.rb +++ /dev/null @@ -1,402 +0,0 @@ -require 'rails_helper' - -describe "content_negotation", type: :request do - let(:provider) { create(:provider, symbol: "DATACITE") } - let(:client) { create(:client, provider: provider, symbol: ENV['MDS_USERNAME'], password: ENV['MDS_PASSWORD']) } - let(:bearer) { Client.generate_token(role_id: "client_admin", uid: client.symbol, provider_id: provider.symbol.downcase, client_id: client.symbol.downcase, password: client.password) } - let(:xml) { Base64.strict_encode64(file_fixture('datacite.xml').read) } - let(:doi) { create(:doi, xml: xml, client: client) } - - context "no permission" do - let(:doi) { create(:doi, xml: xml) } - - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns error message' do - expect(json["errors"]).to eq([{"status"=>"403", "title"=>"You are not authorized to access this resource."}]) - end - - it 'returns status code 403' do - expect(response).to have_http_status(403) - end - end - - context "no authentication" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml" } } - - it 'returns error message' do - expect(json["errors"]).to eq([{"status"=>"401", "title"=>"Bad credentials."}]) - end - - it 'returns status code 401' do - expect(response).to have_http_status(401) - end - end - - context "application/vnd.jats+xml" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - jats = Maremma.from_xml(response.body).fetch("element_citation", {}) - expect(jats.dig("publication_type")).to eq("journal") - expect(jats.dig("article_title")).to eq("Eating your own Dog Food") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.jats+xml link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/vnd.jats+xml/#{doi.doi}" } - - it 'returns the Doi' do - jats = Maremma.from_xml(response.body).fetch("element_citation", {}) - expect(jats.dig("publication_type")).to eq("journal") - expect(jats.dig("article_title")).to eq("Eating your own Dog Food") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.datacite.datacite+xml" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) - expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") - expect(data.dig("publisher")).to eq("DataCite") - expect(data.dig("titles", "title")).to eq("Eating your own Dog Food") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.datacite.datacite+xml link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/vnd.datacite.datacite+xml/#{doi.doi}" } - - it 'returns the Doi' do - data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) - expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") - expect(data.dig("publisher")).to eq("DataCite") - expect(data.dig("titles", "title")).to eq("Eating your own Dog Food") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.datacite.datacite+xml schema 3" do - let(:xml) { Base64.strict_encode64(file_fixture('datacite_schema_3.xml').read) } - let(:doi) { create(:doi, xml: xml, client: client) } - - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) - expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-3") - expect(data.dig("publisher")).to eq("Dryad Digital Repository") - expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - # context "no metadata" do - # let(:doi) { create(:doi, xml: nil, client: client) } - - # before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - - # it 'returns the Doi' do - # expect(response.body).to eq('') - # end - - # it 'returns status code 200' do - # expect(response).to have_http_status(200) - # end - # end - - context "application/vnd.datacite.datacite+xml not found" do - before { get "/xxx", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns error message' do - expect(json["errors"]).to eq([{"status"=>"404", "title"=>"The resource you are looking for doesn't exist."}]) - end - - it 'returns status code 404' do - expect(response).to have_http_status(404) - end - end - - context "application/vnd.datacite.datacite+json" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+json", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(json["doi"]).to eq(doi.doi) - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.datacite.datacite+json link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/vnd.datacite.datacite+json/#{doi.doi}" } - - it 'returns the Doi' do - expect(json["doi"]).to eq(doi.doi) - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.crosscite.crosscite+json" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.crosscite.crosscite+json", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(json["doi"]).to eq(doi.doi) - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.crosscite.crosscite+json link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/vnd.crosscite.crosscite+json/#{doi.doi}" } - - it 'returns the Doi' do - expect(json["doi"]).to eq(doi.doi) - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.schemaorg.ld+json" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.schemaorg.ld+json", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(json["@type"]).to eq("ScholarlyArticle") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.schemaorg.ld+json link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/vnd.schemaorg.ld+json/#{doi.doi}" } - - it 'returns the Doi' do - expect(json["@type"]).to eq("ScholarlyArticle") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.citationstyles.csl+json" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.citationstyles.csl+json", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(json["type"]).to eq("article-journal") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/vnd.citationstyles.csl+json link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/vnd.citationstyles.csl+json/#{doi.doi}" } - - it 'returns the Doi' do - expect(json["type"]).to eq("article-journal") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/x-research-info-systems" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-research-info-systems", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(response.body).to start_with("TY - RPRT") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/x-research-info-systems link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/x-research-info-systems/#{doi.doi}" } - - it 'returns the Doi' do - expect(response.body).to start_with("TY - RPRT") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/x-bibtex" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-bibtex", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(response.body).to start_with("@article{https://handle.test.datacite.org/#{doi.doi.downcase}") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/x-bibtex link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/application/x-bibtex/#{doi.doi}" } - - it 'returns the Doi' do - expect(response.body).to start_with("@article{https://handle.test.datacite.org/#{doi.doi.downcase}") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "application/x-bibtex nasa gsfc" do - let(:xml) { Base64.strict_encode64(file_fixture('datacite_gsfc.xml').read) } - let(:doi) { create(:doi, xml: xml, client: client) } - - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-bibtex", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(response.body).to start_with("@misc{https://handle.test.datacite.org/#{doi.doi.downcase}") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "text/x-bibliography", vcr: true do - context "default style" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(response.body).to start_with("Fenner, M. (2016)") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "default style link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/text/x-bibliography/#{doi.doi}" } - - it 'returns the Doi' do - expect(response.body).to start_with("Fenner, M. (2016)") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "ieee style" do - before { get "/#{doi.doi}?style=ieee", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(response.body).to start_with("M. Fenner") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "ieee style link" do - let(:doi) { create(:doi, xml: xml, client: client, aasm_state: "findable") } - - before { get "/text/x-bibliography/#{doi.doi}?style=ieee" } - - it 'returns the Doi' do - expect(response.body).to start_with("M. Fenner") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - context "style and locale" do - before { get "/#{doi.doi}?style=vancouver&locale=de", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(response.body).to start_with("Fenner M") - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - end - - context "unknown content type" do - before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "text/csv", 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(json["errors"]).to eq([{"status"=>"406", "title"=>"The content type is not recognized."}]) - end - - it 'returns status code 406' do - expect(response).to have_http_status(406) - end - end - - context "missing content type" do - before { get "/#{doi.doi}", headers: { 'Authorization' => 'Bearer ' + bearer } } - - it 'returns the Doi' do - expect(json["errors"]).to eq([{"status"=>"406", "title"=>"The content type is not recognized."}]) - end - - it 'returns status code 406' do - expect(response).to have_http_status(406) - end - end -end diff --git a/spec/requests/media_spec.rb b/spec/requests/media_spec.rb index 1d3d4186a..1c8adc6cc 100644 --- a/spec/requests/media_spec.rb +++ b/spec/requests/media_spec.rb @@ -18,7 +18,7 @@ expect(json).not_to be_empty expect(json['data'].size).to eq(6) result = json['data'].first - expect(result.dig("attributes", "media-type")).to eq("application/json") + expect(result.dig("attributes", "mediaType")).to eq("application/json") end it 'returns status code 200' do @@ -68,12 +68,13 @@ describe 'POST /media' do context 'when the request is valid' do + let(:media_type) { "application/xml" } let(:valid_attributes) do { "data" => { "type" => "media", "attributes"=> { - "media-type" => media_type, + "mediaType" => media_type, "url" => url } } @@ -82,7 +83,7 @@ before { post "/dois/#{doi.doi}/media", params: valid_attributes.to_json, headers: headers } it 'creates a media record' do - expect(json.dig('data', 'attributes', 'media-type')).to eq(media_type) + expect(json.dig('data', 'attributes', 'mediaType')).to eq(media_type) expect(json.dig('data', 'attributes', 'url')).to eq(url) end @@ -91,13 +92,13 @@ end end - context 'when the media-type is missing' do + context 'when the mediaType is missing' do let(:valid_attributes) do { "data" => { "type" => "media", "attributes"=> { - "media-type" => nil, + "mediaType" => nil, "url" => url } } @@ -122,7 +123,7 @@ "data" => { "type" => "media", "attributes"=> { - "media-type"=> media_type, + "mediaType"=> media_type, "url"=> url }, "relationships"=> { @@ -155,7 +156,7 @@ "data" => { "type" => "media", "attributes"=> { - "media-type"=> media_type, + "mediaType"=> media_type, "url"=> url }, "relationships"=> { @@ -173,7 +174,7 @@ before { patch "/dois/#{doi.doi}/media/#{media.uid}", params: valid_attributes.to_json, headers: headers } it 'updates the record' do - expect(json.dig('data', 'attributes', 'media-type')).to eq(media_type) + expect(json.dig('data', 'attributes', 'mediaType')).to eq(media_type) expect(json.dig('data', 'attributes', 'url')).to eq(url) expect(json.dig('data', 'attributes', 'version')).to eq(1) end @@ -190,7 +191,7 @@ "data" => { "type" => "media", "attributes"=> { - "media-type"=> media_type, + "mediaType"=> media_type, "url"=> url }, "relationships"=> { diff --git a/spec/requests/prefixes_spec.rb b/spec/requests/prefixes_spec.rb index 5459d2bcc..2c3330863 100644 --- a/spec/requests/prefixes_spec.rb +++ b/spec/requests/prefixes_spec.rb @@ -89,9 +89,10 @@ before { post '/prefixes', params: valid_attributes.to_json, headers: headers } - it 'creates a prefix' do - expect(json.dig('data', 'id')).to eq("10.17177") - end + # TODO + # it 'creates a prefix' do + # expect(json.dig('data', 'id')).to eq("10.17177") + # end it 'returns status code 201' do expect(response).to have_http_status(201) diff --git a/spec/requests/providers_spec.rb b/spec/requests/providers_spec.rb index 7ccd9dbbb..4f8f8a343 100644 --- a/spec/requests/providers_spec.rb +++ b/spec/requests/providers_spec.rb @@ -10,8 +10,8 @@ "attributes" => { "symbol" => "BL", "name" => "British Library", - "contact-email" => "bob@example.com", - "country-code" => "GB" } } } + "contactEmail" => "bob@example.com", + "countryCode" => "GB" } } } end let(:headers) { {'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + token } } @@ -64,15 +64,15 @@ # "symbol" => "BL", # "name" => "British Library", # "region" => "EMEA", - # "contact-email" => "doe@joe.joe", - # "contact-name" => "timAus", - # "country-code" => "GB" } } } + # "contactEmail" => "doe@joe.joe", + # "contactName" => "timAus", + # "countryCode" => "GB" } } } # end # before { post '/providers', params: params.to_json, headers: headers } # it 'creates a provider' do - # expect(json.dig('data', 'attributes', 'contact-email')).to eq("doe@joe.joe") + # expect(json.dig('data', 'attributes', 'contactEmail')).to eq("doe@joe.joe") # end # it 'returns status code 201' do @@ -88,15 +88,15 @@ # "name" => "Admin", # "region" => "EMEA", # "role_name" => "ROLE_ADMIN", - # "contact-email" => "doe@joe.joe", - # "contact-name" => "timAus", - # "country-code" => "GB" } } } + # "contactEmail" => "doe@joe.joe", + # "contactName" => "timAus", + # "countryCode" => "GB" } } } # end # before { post '/providers', params: params.to_json, headers: headers } # it 'creates a provider' do - # expect(json.dig('data', 'attributes', 'contact-email')).to eq("doe@joe.joe") + # expect(json.dig('data', 'attributes', 'contactEmail')).to eq("doe@joe.joe") # end # it 'returns status code 201' do @@ -111,9 +111,9 @@ # "symbol" => "BL", # "name" => "British Library", # "region" => "EMEA", - # "contact-email" => "doe@joe.joe", - # "contact-name" => "timAus", - # "country-code" => "GB" } } } + # "contactEmail" => "doe@joe.joe", + # "contactName" => "timAus", + # "countryCode" => "GB" } } } # end # let(:admin) { create(:provider, symbol: "ADMIN", role_name: "ROLE_ADMIN", password_input: "12345") } # let(:credentials) { admin.encode_auth_param(username: "ADMIN", password: "12345") } @@ -122,7 +122,7 @@ # before { post '/providers', params: params.to_json, headers: headers } # it 'creates a provider' do - # expect(json.dig('data', 'attributes', 'contact-email')).to eq("doe@joe.joe") + # expect(json.dig('data', 'attributes', 'contactEmail')).to eq("doe@joe.joe") # end # it 'returns status code 201' do @@ -136,8 +136,8 @@ # "attributes" => { # "symbol" => "BL", # "name" => "British Library", - # "contact-name" => "timAus", - # "country-code" => "GB" } } } + # "contactName" => "timAus", + # "countryCode" => "GB" } } } # end # before { post '/providers', params: params.to_json, headers: headers } @@ -156,7 +156,7 @@ # { "type" => "providers", # "attributes" => { # "symbol" => "BL", - # "contact-name" => "timAus", + # "contactName" => "timAus", # "name" => "British Library", # "country-code" => "GB" } } # end @@ -180,14 +180,14 @@ "attributes" => { "name" => "British Library", "region" => "Americas", - "contact-email" => "Pepe@mdm.cod", - "contact-name" => "timAus", - "country-code" => "GB" } } } + "contactEmail" => "Pepe@mdm.cod", + "contactName" => "timAus", + "countryCode" => "GB" } } } end before { put "/providers/#{provider.symbol}", params: params.to_json, headers: headers } it 'updates the record' do - expect(json.dig('data', 'attributes', 'contact-name')).to eq("timAus") + expect(json.dig('data', 'attributes', 'contactName')).to eq("timAus") end it 'returns status code 200' do @@ -201,9 +201,9 @@ "attributes" => { "name" => "British Library", "region" => "Americas", - "contact-email" => "Pepe@mdm.cod", - "contact-name" => "timAus", - "country-code" => "GB" } } } + "contactEmail" => "Pepe@mdm.cod", + "contactName" => "timAus", + "countryCode" => "GB" } } } end let(:admin) { create(:provider, symbol: "ADMIN", role_name: "ROLE_ADMIN", password_input: "12345") } let(:credentials) { admin.encode_auth_param(username: "ADMIN", password: "12345") } @@ -212,7 +212,7 @@ before { put "/providers/#{provider.symbol}", params: params.to_json, headers: headers } it 'updates the record' do - expect(json.dig('data', 'attributes', 'contact-name')).to eq("timAus") + expect(json.dig('data', 'attributes', 'contactName')).to eq("timAus") end it 'returns status code 200' do @@ -226,9 +226,9 @@ "attributes" => { "name" => "British Library", "region" => "Americas", - "contact-email" => "Pepe@mdm.cod", - "contact-name" => "timAus", - "country-code" => "GB" } } } + "contactEmail" => "Pepe@mdm.cod", + "contactName" => "timAus", + "countryCode" => "GB" } } } end before { put '/providers/xxx', params: params.to_json, headers: headers } diff --git a/spec/requests/works_spec.rb b/spec/requests/works_spec.rb new file mode 100644 index 000000000..57c5dc04c --- /dev/null +++ b/spec/requests/works_spec.rb @@ -0,0 +1,76 @@ +require 'rails_helper' + +describe "works", type: :request do + let(:admin) { create(:provider, symbol: "ADMIN") } + let(:admin_bearer) { Client.generate_token(role_id: "staff_admin", uid: admin.symbol, password: admin.password) } + let(:admin_headers) { {'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + admin_bearer}} + + let(:provider) { create(:provider, symbol: "DATACITE") } + let(:client) { create(:client, provider: provider, symbol: ENV['MDS_USERNAME'], password: ENV['MDS_PASSWORD']) } + let!(:prefix) { create(:prefix, prefix: "10.14454") } + let!(:client_prefix) { create(:client_prefix, client: client, prefix: prefix) } + let!(:dois) { create_list(:doi, 3, client: client, event: "publish") } + let(:doi) { create(:doi, client: client, event: "publish") } + let(:bearer) { Client.generate_token(role_id: "client_admin", uid: client.symbol, provider_id: provider.symbol.downcase, client_id: client.symbol.downcase, password: client.password) } + let(:headers) { { 'ACCEPT'=>'application/vnd.api+json', 'CONTENT_TYPE'=>'application/vnd.api+json', 'Authorization' => 'Bearer ' + bearer }} + + describe 'GET /works', elasticsearch: true do + before do + sleep 1 + get '/works', headers: headers + end + + # it 'returns dois' do + # expect(json['data'].size).to eq(0) + # end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + describe 'GET /works/:id' do + context 'when the record exists' do + before { get "/works/#{doi.doi}", headers: headers } + + it 'returns the Doi' do + expect(json).not_to be_empty + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + expect(json.dig('data', 'attributes', 'author').length).to eq(8) + expect(json.dig('data', 'attributes', 'author').first).to eq("family"=>"Ollomo", "given"=>"Benjamin") + expect(json.dig('data', 'attributes', 'title')).to eq("Data from: A new malaria agent in African hominids.") + expect(json.dig('data', 'attributes', 'container-title')).to eq("Dryad Digital Repository") + expect(json.dig('data', 'attributes', 'published')).to eq("2011") + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + context 'when the record does not exist' do + before { get "/works/10.5256/xxxx", headers: headers } + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns a not found message' do + expect(json).to eq("errors"=>[{"status"=>"404", "title"=>"The resource you are looking for doesn't exist."}]) + end + end + + context 'anonymous user' do + before { get "/works/#{doi.doi}" } + + it 'returns the Doi' do + expect(json).not_to be_empty + expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + end +end \ No newline at end of file diff --git a/vendor/docker/70_index_page.sh b/vendor/docker/70_index_page.sh new file mode 100755 index 000000000..fad7b5b99 --- /dev/null +++ b/vendor/docker/70_index_page.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd vendor/middleman +/sbin/setuser app bundle exec middleman build -e ${RAILS_ENV} diff --git a/vendor/middleman/Gemfile b/vendor/middleman/Gemfile new file mode 100644 index 000000000..3410f7d6b --- /dev/null +++ b/vendor/middleman/Gemfile @@ -0,0 +1,12 @@ +# If you do not have OpenSSL installed, change +# the following line to use 'http://' +source 'https://rubygems.org' + +# Middleman Gems +gem 'middleman', "~> 4.1" +gem 'tilt', '~> 2.0', git: "https://github.com/datacite/tilt.git", branch: "pandoc-options" +gem 'tilt-handlebars', '~> 1.4' +gem 'middleman-data_source', '~> 0.8.1' +gem 'middleman-livereload' +gem 'middleman-syntax', '~> 2.0' +gem 'pandoc-ruby', '~> 2.0' diff --git a/vendor/middleman/Gemfile.lock b/vendor/middleman/Gemfile.lock new file mode 100644 index 000000000..fd3ba85c7 --- /dev/null +++ b/vendor/middleman/Gemfile.lock @@ -0,0 +1,149 @@ +GIT + remote: https://github.com/datacite/tilt.git + revision: 612652f9d03ff3c129c415b1826fb84b0c7a0845 + branch: pandoc-options + specs: + tilt (2.0.5) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (5.0.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + backports (3.10.3) + borrower (0.10.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + compass-import-once (1.0.5) + sass (>= 3.2, < 3.5) + concurrent-ruby (1.0.5) + contracts (0.13.0) + dotenv (2.2.1) + em-websocket (0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + erubis (2.7.0) + eventmachine (1.2.5) + execjs (2.7.0) + fast_blank (1.0.0) + fastimage (2.1.1) + ffi (1.9.18) + haml (5.0.4) + temple (>= 0.8.0) + tilt + hamster (3.0.0) + concurrent-ruby (~> 1.0) + handlebars (0.8.0) + handlebars-source (~> 4.0.5) + therubyracer (~> 0.12.1) + handlebars-source (4.0.11) + hashie (3.5.7) + http_parser.rb (0.6.0) + i18n (0.7.0) + kramdown (1.16.2) + libv8 (3.16.14.19) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + memoist (0.16.0) + middleman (4.2.1) + coffee-script (~> 2.2) + compass-import-once (= 1.0.5) + haml (>= 4.0.5) + kramdown (~> 1.2) + middleman-cli (= 4.2.1) + middleman-core (= 4.2.1) + sass (>= 3.4.0, < 4.0) + middleman-cli (4.2.1) + thor (>= 0.17.0, < 2.0) + middleman-core (4.2.1) + activesupport (>= 4.2, < 5.1) + addressable (~> 2.3) + backports (~> 3.6) + bundler (~> 1.1) + contracts (~> 0.13.0) + dotenv + erubis + execjs (~> 2.0) + fast_blank + fastimage (~> 2.0) + hamster (~> 3.0) + hashie (~> 3.4) + i18n (~> 0.7.0) + listen (~> 3.0.0) + memoist (~> 0.14) + padrino-helpers (~> 0.13.0) + parallel + rack (>= 1.4.5, < 3) + sass (>= 3.4) + servolux + tilt (~> 2.0) + uglifier (~> 3.0) + middleman-data_source (0.8.1) + borrower (~> 0.9) + middleman (>= 3.1) + rack-test (~> 0.6.2) + middleman-livereload (3.4.6) + em-websocket (~> 0.5.1) + middleman-core (>= 3.3) + rack-livereload (~> 0.3.15) + middleman-syntax (2.1.0) + middleman-core (>= 3.2) + rouge (~> 1.0) + minitest (5.10.3) + padrino-helpers (0.13.3.4) + i18n (~> 0.6, >= 0.6.7) + padrino-support (= 0.13.3.4) + tilt (>= 1.4.1, < 3) + padrino-support (0.13.3.4) + activesupport (>= 3.1) + pandoc-ruby (2.0.2) + parallel (1.12.1) + public_suffix (3.0.1) + rack (2.0.3) + rack-livereload (0.3.16) + rack + rack-test (0.6.3) + rack (>= 1.0) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + ref (2.0.0) + rouge (1.11.1) + sass (3.4.25) + servolux (0.13.0) + temple (0.8.0) + therubyracer (0.12.3) + libv8 (~> 3.16.14.15) + ref + thor (0.20.0) + thread_safe (0.3.6) + tilt-handlebars (1.4.0) + handlebars (~> 0.7) + tilt (>= 1.3, < 3) + tzinfo (1.2.4) + thread_safe (~> 0.1) + uglifier (3.2.0) + execjs (>= 0.3.0, < 3) + +PLATFORMS + ruby + +DEPENDENCIES + middleman (~> 4.1) + middleman-data_source (~> 0.8.1) + middleman-livereload + middleman-syntax (~> 2.0) + pandoc-ruby (~> 2.0) + tilt (~> 2.0)! + tilt-handlebars (~> 1.4) + +BUNDLED WITH + 1.16.0 diff --git a/vendor/middleman/config.rb b/vendor/middleman/config.rb new file mode 100644 index 000000000..b03ba4ad8 --- /dev/null +++ b/vendor/middleman/config.rb @@ -0,0 +1,52 @@ +### +# Page options, layouts, aliases and proxies +### + +# Default ENV variables +ENV['CDN_URL'] ||= "https://assets.datacite.org" +ENV['RAILS_ENV'] ||= "development" +ENV['SITE_TITLE'] ||= "DataCite REST API" +ENV['SITE_DESCRIPTION'] ||= "The DataCite API." +ENV['TWITTER_HANDLE'] ||= "@datacite" + +# Build into /public +set :build_dir, "../../public" + +# Per-page layout changes: +# +# With no layout +page '/*.xml', layout: false +page '/*.json', layout: false +page '/*.txt', layout: false + +# General configuration + +# Reload the browser automatically whenever files change +configure :development do + activate :livereload +end + +# Load data +activate :data_source do |c| + c.root = "#{ENV['CDN_URL']}/data" + c.files = [ + "links.json" + ] +end + +# Set markdown template engine +set :markdown_engine, :pandoc +set :markdown, smartypants: true + +# use asset host +activate :asset_host, host: ENV['CDN_URL'] + +### +# Helpers +### +# Methods defined in the helpers block are available in templates +helpers do + def stage? + ENV['RAILS_ENV'] == "stage" + end +end diff --git a/vendor/middleman/config.ru b/vendor/middleman/config.ru new file mode 100644 index 000000000..ece6fda7e --- /dev/null +++ b/vendor/middleman/config.ru @@ -0,0 +1,11 @@ +require 'middleman-core/load_paths' +::Middleman.setup_load_paths + +require 'middleman-core' +require 'middleman-core/rack' + +require 'fileutils' + +app = ::Middleman::Application.new + +run ::Middleman::Rack.new(app).to_app diff --git a/public/favicon.ico b/vendor/middleman/source/favicon.ico old mode 100644 new mode 100755 similarity index 100% rename from public/favicon.ico rename to vendor/middleman/source/favicon.ico diff --git a/vendor/middleman/source/includes/_footer.html.hbs b/vendor/middleman/source/includes/_footer.html.hbs new file mode 100644 index 000000000..a10f67d10 --- /dev/null +++ b/vendor/middleman/source/includes/_footer.html.hbs @@ -0,0 +1,61 @@ + diff --git a/vendor/middleman/source/includes/_google_analytics.html.hbs b/vendor/middleman/source/includes/_google_analytics.html.hbs new file mode 100644 index 000000000..8b18f56af --- /dev/null +++ b/vendor/middleman/source/includes/_google_analytics.html.hbs @@ -0,0 +1,9 @@ + diff --git a/vendor/middleman/source/includes/_header.html.hbs b/vendor/middleman/source/includes/_header.html.hbs new file mode 100644 index 000000000..797adbfda --- /dev/null +++ b/vendor/middleman/source/includes/_header.html.hbs @@ -0,0 +1,20 @@ + diff --git a/vendor/middleman/source/includes/_javascripts.html.hbs b/vendor/middleman/source/includes/_javascripts.html.hbs new file mode 100644 index 000000000..afa75fdff --- /dev/null +++ b/vendor/middleman/source/includes/_javascripts.html.hbs @@ -0,0 +1,4 @@ + + + + diff --git a/vendor/middleman/source/index.html.md b/vendor/middleman/source/index.html.md new file mode 100644 index 000000000..bc3ad8183 --- /dev/null +++ b/vendor/middleman/source/index.html.md @@ -0,0 +1,11 @@ +--- +layout: index +title: DataCite REST API +description: The API to interact with all DataCite resources. +--- + +The DataCite REST API allows users to interact with DataCite resources such as dois, clients, providers and prefixes. Please use +[DOI Fabrica](https://doi.datacite.org) if you are looking for a web interface. The API follows the [JSONAPI](http://jsonapi.org/) +specification. The API requires authentication for some actions. + +You will find more information about the REST API in our [documentation portal](https://support.datacite.org/docs/api). diff --git a/vendor/middleman/source/layouts/index.erb b/vendor/middleman/source/layouts/index.erb new file mode 100644 index 000000000..e74de28c0 --- /dev/null +++ b/vendor/middleman/source/layouts/index.erb @@ -0,0 +1,23 @@ +<% wrap_layout :layout do %> +
+ +
+
+
+

<%= current_page.data.title %>

+

<%= current_page.data.description %>

+
+
+
+ +
+
+
+
+ <%= yield -%> +
+
+
+
+
+<% end %> diff --git a/vendor/middleman/source/layouts/layout.erb b/vendor/middleman/source/layouts/layout.erb new file mode 100644 index 000000000..877498a34 --- /dev/null +++ b/vendor/middleman/source/layouts/layout.erb @@ -0,0 +1,70 @@ + + + + + + + + + + <%= ENV['SITE_TITLE'] %> + + + + + + + + + + + + + + + + + <% if ENV['TWITTER_HANDLE'] %> + + + + + + <% end %> + + + + + + + + + + + + + <% if ENV['BUGSNAG_JS_KEY'] %> + + <% end -%> + + + <% header_links = development? ? data.links.development_links : (stage? ? data.links.stage_links : data.links.production_links) %> + <%= partial "includes/header.html.hbs", locals: + { site_title: ENV['SITE_TITLE'], + stage: stage?, + development: development?, + header_links: header_links } -%> + <%= yield -%> + <%= partial "includes/footer.html.hbs", locals: + { about_links: data.links.about_links, + services_links: data.links.services_links, + resources_links: data.links.resources_links, + community_links: data.links.community_links, + contact_links: data.links.contact_links } -%> + <%= partial "includes/javascripts.html.hbs", locals: { cdn_url: ENV['CDN_URL'] } -%> + <%= partial "includes/google_analytics.html.hbs", locals: { site_ga: ENV['SITE_GA'] } -%> + + diff --git a/vendor/middleman/source/robots.txt b/vendor/middleman/source/robots.txt new file mode 100644 index 000000000..64253dadd --- /dev/null +++ b/vendor/middleman/source/robots.txt @@ -0,0 +1,3 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +User-Agent: * +Disallow: /tmp/