| #!/usr/bin/ruby |
| # |
| # Copyright Istio Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| require 'webrick' |
| require 'json' |
| require 'net/http' |
| |
| if ARGV.length < 1 then |
| puts "usage: #{$PROGRAM_NAME} port" |
| exit(-1) |
| end |
| |
| port = Integer(ARGV[0]) |
| |
| server = WEBrick::HTTPServer.new :BindAddress => '*', :Port => port |
| |
| trap 'INT' do server.shutdown end |
| |
| server.mount_proc '/health' do |req, res| |
| res.status = 200 |
| res.body = {'status' => 'Details is healthy'}.to_json |
| res['Content-Type'] = 'application/json' |
| end |
| |
| server.mount_proc '/details' do |req, res| |
| pathParts = req.path.split('/') |
| headers = get_forward_headers(req) |
| |
| begin |
| begin |
| id = Integer(pathParts[-1]) |
| rescue |
| raise 'please provide numeric product id' |
| end |
| details = get_book_details(id, headers) |
| res.body = details.to_json |
| res['Content-Type'] = 'application/json' |
| rescue => error |
| res.body = {'error' => error}.to_json |
| res['Content-Type'] = 'application/json' |
| res.status = 400 |
| end |
| end |
| |
| # TODO: provide details on different books. |
| def get_book_details(id, headers) |
| if ENV['ENABLE_EXTERNAL_BOOK_SERVICE'] === 'true' then |
| # the ISBN of one of Comedy of Errors on the Amazon |
| # that has Shakespeare as the single author |
| isbn = '0486424618' |
| return fetch_details_from_external_service(isbn, id, headers) |
| end |
| |
| return { |
| 'id' => id, |
| 'author': 'William Shakespeare', |
| 'year': 1595, |
| 'type' => 'paperback', |
| 'pages' => 200, |
| 'publisher' => 'PublisherA', |
| 'language' => 'English', |
| 'ISBN-10' => '1234567890', |
| 'ISBN-13' => '123-1234567890' |
| } |
| end |
| |
| def fetch_details_from_external_service(isbn, id, headers) |
| uri = URI.parse('https://www.googleapis.com/books/v1/volumes?q=isbn:' + isbn) |
| http = Net::HTTP.new(uri.host, ENV['DO_NOT_ENCRYPT'] === 'true' ? 80:443) |
| http.read_timeout = 5 # seconds |
| |
| # DO_NOT_ENCRYPT is used to configure the details service to use either |
| # HTTP (true) or HTTPS (false, default) when calling the external service to |
| # retrieve the book information. |
| # |
| # Unless this environment variable is set to true, the app will use TLS (HTTPS) |
| # to access external services. |
| unless ENV['DO_NOT_ENCRYPT'] === 'true' then |
| http.use_ssl = true |
| end |
| |
| request = Net::HTTP::Get.new(uri.request_uri) |
| headers.each { |header, value| request[header] = value } |
| |
| response = http.request(request) |
| |
| json = JSON.parse(response.body) |
| book = json['items'][0]['volumeInfo'] |
| |
| language = book['language'] === 'en'? 'English' : 'unknown' |
| type = book['printType'] === 'BOOK'? 'paperback' : 'unknown' |
| isbn10 = get_isbn(book, 'ISBN_10') |
| isbn13 = get_isbn(book, 'ISBN_13') |
| |
| return { |
| 'id' => id, |
| 'author': book['authors'][0], |
| 'year': book['publishedDate'], |
| 'type' => type, |
| 'pages' => book['pageCount'], |
| 'publisher' => book['publisher'], |
| 'language' => language, |
| 'ISBN-10' => isbn10, |
| 'ISBN-13' => isbn13 |
| } |
| |
| end |
| |
| def get_isbn(book, isbn_type) |
| isbn_dentifiers = book['industryIdentifiers'].select do |identifier| |
| identifier['type'] === isbn_type |
| end |
| |
| return isbn_dentifiers[0]['identifier'] |
| end |
| |
| def get_forward_headers(request) |
| headers = {} |
| |
| # Keep this in sync with the headers in productpage and reviews. |
| incoming_headers = [ |
| # All applications should propagate x-request-id. This header is |
| # included in access log statements and is used for consistent trace |
| # sampling and log sampling decisions in Istio. |
| 'x-request-id', |
| |
| # Lightstep tracing header. Propagate this if you use lightstep tracing |
| # in Istio (see |
| # https://istio.io/latest/docs/tasks/observability/distributed-tracing/lightstep/) |
| # Note: this should probably be changed to use B3 or W3C TRACE_CONTEXT. |
| # Lightstep recommends using B3 or TRACE_CONTEXT and most application |
| # libraries from lightstep do not support x-ot-span-context. |
| 'x-ot-span-context', |
| |
| # Datadog tracing header. Propagate these headers if you use Datadog |
| # tracing. |
| 'x-datadog-trace-id', |
| 'x-datadog-parent-id', |
| 'x-datadog-sampling-priority', |
| |
| # W3C Trace Context. Compatible with OpenCensusAgent and Stackdriver Istio |
| # configurations. |
| 'traceparent', |
| 'tracestate', |
| |
| # Cloud trace context. Compatible with OpenCensusAgent and Stackdriver Istio |
| # configurations. |
| 'x-cloud-trace-context', |
| |
| # Grpc binary trace context. Compatible with OpenCensusAgent nad |
| # Stackdriver Istio configurations. |
| 'grpc-trace-bin', |
| |
| # b3 trace headers. Compatible with Zipkin, OpenCensusAgent, and |
| # Stackdriver Istio configurations. |
| 'x-b3-traceid', |
| 'x-b3-spanid', |
| 'x-b3-parentspanid', |
| 'x-b3-sampled', |
| 'x-b3-flags', |
| |
| # Application-specific headers to forward. |
| 'end-user', |
| 'user-agent', |
| ] |
| |
| request.each do |header, value| |
| if incoming_headers.include? header then |
| headers[header] = value |
| end |
| end |
| |
| return headers |
| end |
| |
| server.start |