istio.io/istio@v0.0.0-20240520182934-d79c90f27776/samples/bookinfo/src/details/details.rb (about)

     1  #!/usr/bin/ruby
     2  #
     3  # Copyright Istio Authors
     4  #
     5  #   Licensed under the Apache License, Version 2.0 (the "License");
     6  #   you may not use this file except in compliance with the License.
     7  #   You may obtain a copy of the License at
     8  #
     9  #       http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  #   Unless required by applicable law or agreed to in writing, software
    12  #   distributed under the License is distributed on an "AS IS" BASIS,
    13  #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  #   See the License for the specific language governing permissions and
    15  #   limitations under the License.
    16  
    17  require 'webrick'
    18  require 'json'
    19  require 'net/http'
    20  
    21  if ARGV.length < 1 then
    22      puts "usage: #{$PROGRAM_NAME} port"
    23      exit(-1)
    24  end
    25  
    26  port = Integer(ARGV[0])
    27  
    28  server = WEBrick::HTTPServer.new :BindAddress => '*', :Port => port
    29  
    30  trap 'INT' do server.shutdown end
    31  
    32  server.mount_proc '/health' do |req, res|
    33      res.status = 200
    34      res.body = {'status' => 'Details is healthy'}.to_json
    35      res['Content-Type'] = 'application/json'
    36  end
    37  
    38  server.mount_proc '/details' do |req, res|
    39      pathParts = req.path.split('/')
    40      headers = get_forward_headers(req)
    41  
    42      begin
    43          begin
    44            id = Integer(pathParts[-1])
    45          rescue
    46            raise 'please provide numeric product id'
    47          end
    48          details = get_book_details(id, headers)
    49          res.body = details.to_json
    50          res['Content-Type'] = 'application/json'
    51      rescue => error
    52          res.body = {'error' => error}.to_json
    53          res['Content-Type'] = 'application/json'
    54          res.status = 400
    55      end
    56  end
    57  
    58  # TODO: provide details on different books.
    59  def get_book_details(id, headers)
    60      if ENV['ENABLE_EXTERNAL_BOOK_SERVICE'] === 'true' then
    61        # the ISBN of one of Comedy of Errors on the Amazon
    62        # that has Shakespeare as the single author
    63          isbn = '0486424618'
    64          return fetch_details_from_external_service(isbn, id, headers)
    65      end
    66  
    67      return {
    68          'id' => id,
    69          'author': 'William Shakespeare',
    70          'year': 1595,
    71          'type' => 'paperback',
    72          'pages' => 200,
    73          'publisher' => 'PublisherA',
    74          'language' => 'English',
    75          'ISBN-10' => '1234567890',
    76          'ISBN-13' => '123-1234567890'
    77      }
    78  end
    79  
    80  def fetch_details_from_external_service(isbn, id, headers)
    81      uri = URI.parse('https://www.googleapis.com/books/v1/volumes?q=isbn:' + isbn)
    82      http = Net::HTTP.new(uri.host, ENV['DO_NOT_ENCRYPT'] === 'true' ? 80:443)
    83      http.read_timeout = 5 # seconds
    84  
    85      # DO_NOT_ENCRYPT is used to configure the details service to use either
    86      # HTTP (true) or HTTPS (false, default) when calling the external service to
    87      # retrieve the book information.
    88      #
    89      # Unless this environment variable is set to true, the app will use TLS (HTTPS)
    90      # to access external services.
    91      unless ENV['DO_NOT_ENCRYPT'] === 'true' then
    92        http.use_ssl = true
    93      end
    94  
    95      request = Net::HTTP::Get.new(uri.request_uri)
    96      headers.each { |header, value| request[header] = value }
    97  
    98      response = http.request(request)
    99  
   100      json = JSON.parse(response.body)
   101      book = json['items'][0]['volumeInfo']
   102  
   103      language = book['language'] === 'en'? 'English' : 'unknown'
   104      type = book['printType'] === 'BOOK'? 'paperback' : 'unknown'
   105      isbn10 = get_isbn(book, 'ISBN_10')
   106      isbn13 = get_isbn(book, 'ISBN_13')
   107  
   108      return {
   109          'id' => id,
   110          'author': book['authors'][0],
   111          'year': book['publishedDate'],
   112          'type' => type,
   113          'pages' => book['pageCount'],
   114          'publisher' => book['publisher'],
   115          'language' => language,
   116          'ISBN-10' => isbn10,
   117          'ISBN-13' => isbn13
   118    }
   119  
   120  end
   121  
   122  def get_isbn(book, isbn_type)
   123    isbn_dentifiers = book['industryIdentifiers'].select do |identifier|
   124      identifier['type'] === isbn_type
   125    end
   126  
   127    return isbn_dentifiers[0]['identifier']
   128  end
   129  
   130  def get_forward_headers(request)
   131    headers = {}
   132  
   133    # Keep this in sync with the headers in productpage and reviews.
   134    incoming_headers = [
   135        # All applications should propagate x-request-id. This header is
   136        # included in access log statements and is used for consistent trace
   137        # sampling and log sampling decisions in Istio.
   138        'x-request-id',
   139  
   140        # Lightstep tracing header. Propagate this if you use lightstep tracing
   141        # in Istio (see
   142        # https://istio.io/latest/docs/tasks/observability/distributed-tracing/lightstep/)
   143        # Note: this should probably be changed to use B3 or W3C TRACE_CONTEXT.
   144        # Lightstep recommends using B3 or TRACE_CONTEXT and most application
   145        # libraries from lightstep do not support x-ot-span-context.
   146        'x-ot-span-context',
   147  
   148        # Datadog tracing header. Propagate these headers if you use Datadog
   149        # tracing.
   150        'x-datadog-trace-id',
   151        'x-datadog-parent-id',
   152        'x-datadog-sampling-priority',
   153  
   154        # W3C Trace Context. Compatible with OpenCensusAgent and Stackdriver Istio
   155        # configurations.
   156        'traceparent',
   157        'tracestate',
   158  
   159        # Cloud trace context. Compatible with OpenCensusAgent and Stackdriver Istio
   160        # configurations.
   161        'x-cloud-trace-context',
   162  
   163        # Grpc binary trace context. Compatible with OpenCensusAgent nad
   164        # Stackdriver Istio configurations.
   165        'grpc-trace-bin',
   166  
   167        # b3 trace headers. Compatible with Zipkin, OpenCensusAgent, and
   168        # Stackdriver Istio configurations.
   169        'x-b3-traceid',
   170        'x-b3-spanid',
   171        'x-b3-parentspanid',
   172        'x-b3-sampled',
   173        'x-b3-flags',
   174  
   175        # SkyWalking trace headers.
   176        'sw8',
   177  
   178        # Application-specific headers to forward.
   179        'end-user',
   180        'user-agent',
   181  
   182        # Context and session specific headers
   183        'cookie',
   184        'authorization',
   185        'jwt'
   186    ]
   187  
   188    request.each do |header, value|
   189      if incoming_headers.include? header then
   190        headers[header] = value
   191      end
   192    end
   193  
   194    return headers
   195  end
   196  
   197  server.start