diff --git a/.travis.yml b/.travis.yml index 574671c..7a324f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby rvm: - - 2.3.0 + - 2.3.1 script: rspec tests notifications: diff --git a/docker/Dockerfile b/docker/Dockerfile index a3f2e0d..c51e9b3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -17,6 +17,6 @@ COPY run_ruby.sh run_ruby.sh RUN chmod 0755 run_ruby.sh # allow interactive bash inside docker container -CMD ./run_ruby.sh $API_KEY $FILENAME $ALT_URL +CMD ./run_ruby.sh VOLUME ["/source"] diff --git a/docker/README.md b/docker/README.md index b1e8aa7..ed7ca41 100644 --- a/docker/README.md +++ b/docker/README.md @@ -9,4 +9,4 @@ Build the docker image, e.g. `docker build -t basistech/ruby:1.1 .` Run an example as `docker run -e API_KEY=api-key -v "path-to-local-ruby-dir:/source" basistech/ruby:1.1` -To test against a specific source file, add `-e FILENAME=filename` before the `-v`, to test against an alternate url, add `-e ALT_URL=alternate_url`, and optionally if you would like to regenerate gh-pages from the changes made to the development source you can add `-e GIT_USERNAME=git-username -e VERSION=version` before the `-v`. In order to push the gh-pages to git remember to mount .ssh and .gitconfig to the root dir `-v path-to-.ssh-dir:/root/.ssh -v path-to-.gitconfig:/root/.gitconfig`. \ No newline at end of file +To test against a specific source file, add `-e FILENAME=filename` before the `-v`, to test against an alternate url, add `-e ALT_URL=alternate_url`. \ No newline at end of file diff --git a/docker/run_ruby.sh b/docker/run_ruby.sh index 5c8e1e9..1a3e86d 100644 --- a/docker/run_ruby.sh +++ b/docker/run_ruby.sh @@ -2,7 +2,7 @@ ping_url="https://api.rosette.com/rest/v1" retcode=0 -errors=( "Exception" "processingFailure" ) +errors=( "Exception" "processingFailure" "badRequest" "ParseError" "ValueError" "SyntaxError" "AttributeError" "ImportError" ) #------------ Start Functions -------------------------- @@ -12,11 +12,13 @@ function HELP { echo " API_KEY - Rosette API key (required)" echo " FILENAME - Ruby source file (optional)" echo " ALT_URL - Alternate service URL (optional)" - echo " GIT_USERNAME - Git username where you would like to push regenerated gh-pages (optional)" - echo " VERSION - Build version (optional)" exit 1 } +if [ ! -z ${ALT_URL} ]; then + ping_url=${ALT_URL} +fi + #Checks if Rosette API key is valid function checkAPI { match=$(curl "${ping_url}/ping" -H "X-RosetteAPI-Key: ${API_KEY}" | grep -o "forbidden") @@ -55,27 +57,16 @@ function runExample() { #------------ End Functions ---------------------------- #Gets API_KEY, FILENAME and ALT_URL if present -while getopts ":API_KEY:FILENAME:ALT_URL:GIT_USERNAME:VERSION" arg; do +while getopts ":API_KEY:FILENAME:ALT_URL" arg; do case "${arg}" in API_KEY) API_KEY=${OPTARG} - usage ;; ALT_URL) ALT_URL=${OPTARG} - usage ;; FILENAME) FILENAME=${OPTARG} - usage - ;; - GIT_USERNAME) - GIT_USERNAME=${OPTARG} - usage - ;; - VERSION) - VERSION={OPTARG} - usage ;; esac done @@ -93,11 +84,13 @@ gem install ./rosette_api-*.gem if [ ! -z ${API_KEY} ]; then checkAPI cd tests - rspec tests_spec.rb + rspec tests_spec.rb --format documentation cd ../examples if [ ! -z ${FILENAME} ]; then + echo -e "\nRunning example against: ${ping_url}\n" runExample ${FILENAME} else + echo -e "\nRunning examples against: ${ping_url}\n" for file in *.rb; do runExample ${file} done @@ -106,22 +99,4 @@ else HELP fi -#Generate gh-pages and push them to git account (if git username is provided) -if [ ! -z ${GIT_USERNAME} ] && [ ! -z ${VERSION} ]; then - #clone ruby git repo - cd / - git clone git@github.com:${GIT_USERNAME}/ruby.git - cd ruby - git checkout origin/gh-pages -b gh-pages - git branch -d develop - #generate gh-pages and set ouput dir to git repo (gh-pages branch) - cd /ruby-dev/lib - rdoc -o /doc - cp -r /doc/. /ruby - cd /ruby - git add . - git commit -a -m "publish ruby apidocs ${VERSION}" - git push -fi - exit ${retcode} diff --git a/examples/docker/Dockerfile b/examples/docker/Dockerfile index 192f63a..46edb1f 100644 --- a/examples/docker/Dockerfile +++ b/examples/docker/Dockerfile @@ -12,6 +12,6 @@ COPY run_ruby.sh run_ruby.sh RUN chmod 0755 run_ruby.sh # allow interactive bash inside docker container -CMD ./run_ruby.sh $API_KEY $FILENAME $ALT_URL +CMD ./run_ruby.sh VOLUME ["/source"] diff --git a/examples/docker/run_ruby.sh b/examples/docker/run_ruby.sh index 30f6c1f..66636fd 100644 --- a/examples/docker/run_ruby.sh +++ b/examples/docker/run_ruby.sh @@ -2,7 +2,7 @@ ping_url="https://api.rosette.com/rest/v1" retcode=0 -errors=( "Exception" "processingFailure" ) +errors=( "Exception" "processingFailure" "badRequest" "ParseError" "ValueError" "SyntaxError" "AttributeError" "ImportError" ) #------------ Start Functions -------------------------- @@ -15,6 +15,10 @@ function HELP { exit 1 } +if [ ! -z ${ALT_URL} ]; then + ping_url=${ALT_URL} +fi + #Checks if Rosette API key is valid function checkAPI { match=$(curl "${ping_url}/ping" -H "X-RosetteAPI-Key: ${API_KEY}" | grep -o "forbidden") @@ -53,19 +57,16 @@ function runExample() { #------------ End Functions ---------------------------- #Gets API_KEY, FILENAME and ALT_URL if present -while getopts ":API_KEY:FILENAME:ALT_URL:GIT_USERNAME:VERSION" arg; do +while getopts ":API_KEY:FILENAME:ALT_URL" arg; do case "${arg}" in API_KEY) API_KEY=${OPTARG} - usage ;; ALT_URL) ALT_URL=${OPTARG} - usage ;; FILENAME) FILENAME=${OPTARG} - usage ;; esac done @@ -82,8 +83,10 @@ cp /source/examples/*.* . if [ ! -z ${API_KEY} ]; then checkAPI if [ ! -z ${FILENAME} ]; then + echo -e "\nRunning example against: ${ping_url}\n" runExample ${FILENAME} else + echo -e "\nRunning examples against: ${ping_url}\n" for file in *.rb; do runExample ${file} done diff --git a/examples/entities.rb b/examples/entities.rb index ed7625b..0ec64d0 100644 --- a/examples/entities.rb +++ b/examples/entities.rb @@ -9,6 +9,6 @@ end entities_text_data = 'Bill Murray will appear in new Ghostbusters film: Dr. Peter Venkman was spotted filming a cameo in Boston this… http://dlvr.it/BnsFfS' -params = DocumentParameters.new(content: entities_text_data) +params = DocumentParameters.new(content: entities_text_data, genre: 'social-media') response = rosette_api.get_entities(params) puts JSON.pretty_generate(response) diff --git a/examples/entities_linked.rb b/examples/entities_linked.rb index cb96868..82a3b71 100644 --- a/examples/entities_linked.rb +++ b/examples/entities_linked.rb @@ -11,4 +11,4 @@ entities_linked_text_data = 'Last month director Paul Feig announced the movie will have an all-star female cast including Kristen Wiig, Melissa McCarthy, Leslie Jones and Kate McKinnon.' params = DocumentParameters.new(content: entities_linked_text_data, genre: 'social-media') response = rosette_api.get_entities(params, true) -puts JSON.pretty_generate(response) \ No newline at end of file +puts JSON.pretty_generate(response) diff --git a/examples/language.rb b/examples/language.rb index 8175b2a..c0f7ef9 100644 --- a/examples/language.rb +++ b/examples/language.rb @@ -10,5 +10,6 @@ language_data = 'Por favor Señorita, says the man.?' params = DocumentParameters.new(content: language_data) +params.custom_headers = { 'X-RosetteAPI-App'=> 'ruby-app'} response = rosette_api.get_language(params) puts JSON.pretty_generate(response) \ No newline at end of file diff --git a/examples/relationships.rb b/examples/relationships.rb index 7111182..ab1387f 100644 --- a/examples/relationships.rb +++ b/examples/relationships.rb @@ -10,6 +10,6 @@ relationships_text_data = 'The Ghostbusters movie was filmed in Boston.' params = DocumentParameters.new(content: relationships_text_data) -params.rosette_options = { "accuracyMode" => "PRECISION" } +params.rosette_options = { accuracyMode: 'PRECISION' } response = rosette_api.get_relationships(params) puts JSON.pretty_generate(response) diff --git a/lib/document_parameters.rb b/lib/document_parameters.rb index 08f5f8e..43cc36e 100644 --- a/lib/document_parameters.rb +++ b/lib/document_parameters.rb @@ -15,6 +15,8 @@ class DocumentParameters attr_accessor :language # Rosette API options (optional, should be a hash) attr_accessor :rosette_options + # custom Rosette API headers + attr_accessor :custom_headers def initialize(options = {}) #:notnew: options = { @@ -23,7 +25,8 @@ def initialize(options = {}) #:notnew: file_path: nil, genre: nil, language: nil, - rosette_options: nil + rosette_options: nil, + custom_headers: nil }.update options @content = options[:content] @content_uri = options[:content_uri] @@ -31,6 +34,7 @@ def initialize(options = {}) #:notnew: @genre = options[:genre] @language = options[:language] @rosette_options = options[:rosette_options] + @custom_headers = options[:custom_headers] end # Validates the parameters by checking if there are multiple content sources @@ -43,6 +47,8 @@ def validate_params elsif [@content, @content_uri, @file_path].all?(&:nil?) raise BadRequestFormatError.new 'The format of the request is invalid: no content provided; must' \ ' be one of an attachment, an inline "content" field, or an external "contentUri"' + elsif !@rosette_options.nil? + raise BadRequestError.new('rosette_options can only be an instance of a Hash') unless @rosette_options.is_a? Hash end end @@ -66,7 +72,8 @@ def to_hash file_path: @file_path, genre: @genre, language: @language, - options: @rosette_options + options: @rosette_options, + custom_headers: @custom_headers } end end diff --git a/lib/name_similarity_parameters.rb b/lib/name_similarity_parameters.rb index ffbd556..d6c4607 100644 --- a/lib/name_similarity_parameters.rb +++ b/lib/name_similarity_parameters.rb @@ -21,6 +21,7 @@ def initialize(name1, name2, options = {}) #:notnew: @genre = options[:genre] @name1 = name1 @name2 = name2 + @rosette_options = options[:rosette_options] end # Validates the parameters by checking if name1 and name2 are instances of @@ -30,6 +31,8 @@ def validate_params raise BadRequestError.new('name1 option can only be an instance of a String or NameParameter') elsif [String, NameParameter].none? { |clazz| @name2.is_a? clazz } raise BadRequestError.new('name2 option can only be an instance of a String or NameParameter') + elsif !@rosette_options.nil? + raise BadRequestError.new('rosette_options can only be an instance of a Hash') unless @rosette_options.is_a? Hash end end diff --git a/lib/name_translation_parameters.rb b/lib/name_translation_parameters.rb index 5b13aa1..f8135b0 100644 --- a/lib/name_translation_parameters.rb +++ b/lib/name_translation_parameters.rb @@ -47,10 +47,18 @@ def initialize(name, target_language, options = {}) #:notnew: @target_script = options[:target_script] end + # Validates the parameters by checking if rosette_options is an instance of a Hash. + def validate_params + if !@rosette_options.nil? + raise BadRequestError.new('rosette_options can only be an instance of a Hash') unless @rosette_options.is_a? Hash + end + end + # Converts this class to Hash with its keys in lower CamelCase. # # Returns the new Hash. def load_params + self.validate_params self.to_hash.select { |_key, value| !value.nil? } .map { |key, value| [key.to_s.split('_').map(&:capitalize).join.sub!(/\D/, &:downcase), value] } .to_h diff --git a/lib/request_builder.rb b/lib/request_builder.rb index 60967ff..0dd5558 100644 --- a/lib/request_builder.rb +++ b/lib/request_builder.rb @@ -41,6 +41,18 @@ def prepare_plain_request(params) raise RosetteAPIError.new 'connectionError', 'Failed to establish connection with Rosette API server.' end + if params['customHeaders'] != nil + keys_array = params['customHeaders'].keys + for k in keys_array + if k.to_s =~ /^X-RosetteAPI-/ + request[k] = params['customHeaders'][k] + else + raise RosetteAPIError.new 'invalidHeader', 'Custom header must begin with "X-RosetteAPI-"' + end + end + params.delete 'customHeaders' + end + request['X-RosetteAPI-Key'] = @user_key request['Content-Type'] = 'application/json' request['Accept'] = 'application/json' @@ -68,6 +80,7 @@ def prepare_multipart_request(params) boundary = SecureRandom.hex post_body = [] + params.delete 'filePath' request_file = params.to_json # Add the content data @@ -92,6 +105,19 @@ def prepare_multipart_request(params) rescue raise RosetteAPIError.new 'connectionError', 'Failed to establish connection with Rosette API server.' end + + # add any custom headers from the user + if params['customHeaders'] != nil + keys_array = params['customHeaders'].keys + for k in keys_array + if k.to_s =~ /^X-RosetteAPI-/ + request.add_field k, params['customHeaders'][k] + else + raise RosetteAPIError.new 'invalidHeader', 'Custom header must begin with "X-RosetteAPI-"' + end + end + params.delete 'customHeaders' + end request.add_field 'Content-Type', "multipart/form-data; boundary=#{boundary}" request.add_field 'X-RosetteAPI-Key', @user_key diff --git a/lib/rosette_api.rb b/lib/rosette_api.rb index fa45459..0ad688a 100644 --- a/lib/rosette_api.rb +++ b/lib/rosette_api.rb @@ -9,7 +9,7 @@ # This class allows you to access all Rosette API endpoints. class RosetteAPI # Version of Ruby binding - BINDING_VERSION = '1.1.1' + BINDING_VERSION = '1.2.0' # Rosette API language endpoint LANGUAGE_ENDPOINT = '/language' # Rosette API morphology endpoint @@ -39,6 +39,8 @@ class RosetteAPI attr_accessor :user_key # Alternate Rosette API URL attr_accessor :alternate_url + # custom Rosette API headers + attr_accessor :custom_headers def initialize(user_key, alternate_url = 'https://api.rosette.com/rest/v1') #:notnew: @user_key = user_key @@ -166,7 +168,6 @@ def get_entities(params, resolve_entities = false) params = params.load_params endpoint = resolve_entities ? (ENTITIES_ENDPOINT + '/linked') : ENTITIES_ENDPOINT - RequestBuilder.new(@user_key, @alternate_url + endpoint, params, BINDING_VERSION) .send_post_request end @@ -181,10 +182,12 @@ def get_entities(params, resolve_entities = false) # base. def get_entities_linked(params) warn '[DEPRECATION] `get_entities_linked` is deprecated. Please use ' \ - '`get_entities` instead with `resolve_entities` param set to true.' + '`get_entities` instead.' get_entities(params, true) end + + # Extracts Tier 1 contextual categories from the input. # # ==== Attributes diff --git a/mock-data/request/entities_linked.json b/mock-data/request/entities_no_qids.json similarity index 62% rename from mock-data/request/entities_linked.json rename to mock-data/request/entities_no_qids.json index 8027d1c..10ff3c7 100644 --- a/mock-data/request/entities_linked.json +++ b/mock-data/request/entities_no_qids.json @@ -1 +1 @@ -{"content":"Last month director Paul Feig announced the movie will have an all-star female cast including Kristen Wiig, Melissa McCarthy, Leslie Jones and Kate McKinnon."} \ No newline at end of file +{"content":"Last month director Paul Feig announced the movie will have an all-star female cast including Kristen Wiig, Melissa McCarthy, Leslie Jones and Kate McKinnon.","options":{"linkEntities":false}} \ No newline at end of file diff --git a/rosette_api.gemspec b/rosette_api.gemspec index a8b42f7..11c0861 100644 --- a/rosette_api.gemspec +++ b/rosette_api.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.0.0' s.name = 'rosette_api' - s.version = '1.1.1' + s.version = '1.2.0' s.license = 'MIT' s.summary = 'Rosette API gem that supports multilingual text-analytics.' @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.authors = ['Basis Technology Corp'] s.email = %q{support@rosette.com} s.homepage = %q{https://developer.rosette.com/} - s.date = %q{2016-06-08} + s.date = %q{2016-07-22} all_files = `git ls-files -z`.split("\x0") s.files = all_files.grep(%r{^(bin|lib)/|^.rubocop.yml$}) diff --git a/tests/tests_spec.rb b/tests/tests_spec.rb index 4ca672a..e49bbbc 100644 --- a/tests/tests_spec.rb +++ b/tests/tests_spec.rb @@ -177,10 +177,10 @@ end end - describe '.get_entities_linked' do - request_file = File.read File.expand_path(File.join(File.dirname(__FILE__), '../mock-data/request/entities_linked.json')) + describe '.get_entities_no_qids' do + request_file = File.read File.expand_path(File.join(File.dirname(__FILE__), '../mock-data/request/entities_no_qids.json')) before do - stub_request(:post, 'https://api.rosette.com/rest/v1/entities/linked'). + stub_request(:post, 'https://api.rosette.com/rest/v1/entities'). with(body: request_file, headers: {'Accept' => 'application/json', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', @@ -189,21 +189,23 @@ 'X-Rosetteapi-Key' => '0123456789', 'X-Rosetteapi-Binding' => 'ruby', 'X-Rosetteapi-Binding-Version' => '1.1.1'}). - to_return(status: 200, body: {'test': 'entities/linked'}.to_json, headers: {}) + to_return(status: 200, body: {'test': 'entities'}.to_json, headers: {}) end - it 'test entities linked' do + it 'test entities without qids' do params = DocumentParameters.new params.content = 'Last month director Paul Feig announced the movie will have an all-star female cast including' \ ' Kristen Wiig, Melissa McCarthy, Leslie Jones and Kate McKinnon.' - response = RosetteAPI.new('0123456789').get_entities(params, true) + params.rosette_options = { linkEntities: false} + response = RosetteAPI.new('0123456789').get_entities(params) expect(response).instance_of? Hash end - it 'test entities linked for resolve_entities is not a boolean' do + it 'test rosette_options is not a Hash' do params = DocumentParameters.new params.content = 'Last month director Paul Feig announced the movie will have an all-star female cast including' \ ' Kristen Wiig, Melissa McCarthy, Leslie Jones and Kate McKinnon.' - expect { RosetteAPI.new('0123456789').get_entities(params, 'smth') }.to raise_error(BadRequestError) + params.rosette_options = 1 + expect { RosetteAPI.new('0123456789').get_entities(params) }.to raise_error(BadRequestError) end end @@ -391,5 +393,43 @@ end end + describe '.get_language_custom_header' do + request_file = File.read File.expand_path(File.join(File.dirname(__FILE__), '../mock-data/request/language.json')) + before do + stub_request(:post, 'https://api.rosette.com/rest/v1/language'). + with(body: request_file, + headers: {'Accept' => 'application/json', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'Content-Type' => 'application/json', + 'User-Agent' => 'Ruby', + 'X-Rosetteapi-Key' => '0123456789', + 'X-Rosetteapi-Binding' => 'ruby', + 'X-Rosetteapi-Binding-Version' => '1.1.1', + 'X-RosetteApi-App' => 'ruby-app'}). + to_return(status: 200, body: {'test': 'language'}.to_json, headers: {}) + end + + it 'test custom_headers is invalid' do + params = DocumentParameters.new + params.content = 'Por favor Senorita, says the man.?' + params.custom_headers = { 'test': 'ruby-app'} + expect { RosetteAPI.new('0123456789').get_language(params) }.to raise_error(RosetteAPIError) + end + end + + describe '.error_409_incompatible_client_version' do + before do + stub_request(:get, 'https://api.rosette.com/rest/v1/info'). + with(headers: {'Accept' => '*/*', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'User-Agent' => 'Ruby', + 'X-Rosetteapi-Key' => '0123456789'}). + to_return(status: 409, body: {'code': 'incompatibleClientVersion'}.to_json, headers: {}) + end + it 'test error 409 properly handled' do + expect { RosetteAPI.new('0123456789').info }.to raise_error(RosetteAPIError) + end + end + end