require'net/http'require'net/https'require'json'require'copy_tuner_client/errors'moduleCopyTunerClient# Communicates with the CopyTuner server. This class is used to actually# download and upload blurbs, as well as issuing deploys.## A client is usually instantiated when {Configuration#apply} is called, and# the application will not need to interact with it directly.classClient# These errors will be rescued when connecting CopyTuner.HTTP_ERRORS=[Timeout::Error,Errno::EINVAL,Errno::ECONNRESET,EOFError,Net::HTTPBadResponse,Net::HTTPHeaderSyntaxError,Net::ProtocolError,SocketError,OpenSSL::SSL::SSLError,Errno::ECONNREFUSED]USER_AGENT="copy_tuner_client #{CopyTunerClient::VERSION}"# Usually instantiated from {Configuration#apply}. Copies options.# @param options [Hash]# @option options [String] :api_key API key of the project to connect to# @option options [Fixnum] :port the port to connect to# @option options [Boolean] :public whether to download draft or published content# @option options [Fixnum] :http_read_timeout how long to wait before timing out when reading data from the socket# @option options [Fixnum] :http_open_timeout how long to wait before timing out when opening the socket# @option options [Boolean] :secure whether to use SSL# @option options [Logger] :logger where to log transactions# @option options [String] :ca_file path to root certificate file for ssl verificationdefinitialize(options)[:api_key,:host,:port,:public,:http_read_timeout,:http_open_timeout,:secure,:logger,:ca_file,:s3_host].eachdo|option|instance_variable_set"@#{option}",options[option]endend# Downloads all blurbs for the given api_key.## If the +public+ option was set to +true+, this will use published blurbs.# Otherwise, draft content is fetched.## The client tracks ETags between download requests, and will return# without yielding anything if the server returns a not modified response.## @yield [Hash] downloaded blurbs# @raise [ConnectionError] if the connection failsdefdownloadconnect(s3_host)do|http|request=Net::HTTP::Get.new(uri(download_resource))request['If-None-Match']=@etaglog'Start downloading translations't=Time.nowresponse=http.request(request)t_ms=((Time.now-t)*1000).to_iifcheckresponselog"Downloaded translations (#{t_ms}ms)"yieldJSON.parse(response.body)elselog"No new translations (#{t_ms}ms)"end@etag=response['ETag']endend# Uploads the given hash of blurbs as draft content.# @param data [Hash] the blurbs to upload# @raise [ConnectionError] if the connection failsdefupload(data)connect(host)do|http|response=http.post(uri('draft_blurbs'),data.to_json,'Content-Type'=>'application/json','User-Agent'=>USER_AGENT)checkresponselog'Uploaded missing translations'endend# Issues a deploy, marking all draft content as published for this project.# @raise [ConnectionError] if the connection failsdefdeployconnect(host)do|http|response=http.post(uri('deploys'),'','User-Agent'=>USER_AGENT)checkresponselog'Deployed'endendprivateattr_reader:host,:port,:api_key,:http_read_timeout,:http_open_timeout,:secure,:logger,:ca_file,:s3_hostdefpublic?@publicenddefuri(resource)"/api/v2/projects/#{api_key}/#{resource}"enddefdownload_resourceifpublic?'published_blurbs.json'else'draft_blurbs.json'endenddefconnect(host)http=Net::HTTP.new(host,port)http.open_timeout=http_open_timeouthttp.read_timeout=http_read_timeouthttp.use_ssl=securehttp.verify_mode=OpenSSL::SSL::VERIFY_PEERhttp.ca_file=ca_filebeginyieldhttprescue*HTTP_ERRORS=>exceptionraiseConnectionError,"#{exception.class.name}: #{exception.message}"endenddefcheck(response)caseresponsewhenNet::HTTPNotFoundraiseInvalidApiKey,"Invalid API key: #{api_key}"whenNet::HTTPNotModifiedfalsewhenNet::HTTPSuccesstrueelseraiseConnectionError,"#{response.code}: #{response.body}"endenddeflog(message)logger.infomessageendendend