From 476ae3acd9b2dcd617c7dc650ff676aa9142be7a Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Thu, 27 Oct 2022 17:31:01 +0300 Subject: [PATCH 01/14] add initial proto support --- .gemfiles/google-protobuf.gemfile | 3 + .rubocop.yml | 2 + lib/travel_time/client.rb | 44 +++++++++- lib/travel_time/middleware/proto.rb | 19 +++++ .../proto/source/RequestsCommon.proto | 61 ++++++++++++++ .../proto/source/TimeFilterFastRequest.proto | 35 ++++++++ .../proto/source/TimeFilterFastResponse.proto | 84 +++++++++++++++++++ lib/travel_time/proto/utils.rb | 28 +++++++ lib/travel_time/proto/v2/RequestsCommon_pb.rb | 44 ++++++++++ .../proto/v2/TimeFilterFastRequest_pb.rb | 40 +++++++++ .../proto/v2/TimeFilterFastResponse_pb.rb | 51 +++++++++++ travel_time.gemspec | 1 + 12 files changed, 408 insertions(+), 4 deletions(-) create mode 100644 .gemfiles/google-protobuf.gemfile create mode 100644 lib/travel_time/middleware/proto.rb create mode 100644 lib/travel_time/proto/source/RequestsCommon.proto create mode 100644 lib/travel_time/proto/source/TimeFilterFastRequest.proto create mode 100644 lib/travel_time/proto/source/TimeFilterFastResponse.proto create mode 100644 lib/travel_time/proto/utils.rb create mode 100644 lib/travel_time/proto/v2/RequestsCommon_pb.rb create mode 100644 lib/travel_time/proto/v2/TimeFilterFastRequest_pb.rb create mode 100644 lib/travel_time/proto/v2/TimeFilterFastResponse_pb.rb diff --git a/.gemfiles/google-protobuf.gemfile b/.gemfiles/google-protobuf.gemfile new file mode 100644 index 0000000..9845839 --- /dev/null +++ b/.gemfiles/google-protobuf.gemfile @@ -0,0 +1,3 @@ +eval_gemfile '../Gemfile' + +gem 'google-protobuf', '~> 3.21', '>= 3.21.9' \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 46b0c20..b0cae42 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,6 +9,8 @@ AllCops: DisplayStyleGuide: true TargetRubyVersion: 2.7 NewCops: enable + Excludes: + - lib/travel_time/proto/v2/*.rb # Disabling until TravelTime Rubygems account is MFA-protected. Gemspec/RequireMFA: diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index 080b68e..bbc11ec 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -1,7 +1,13 @@ # frozen_string_literal: true require 'faraday' +require 'google/protobuf' require 'travel_time/middleware/authentication' +require 'travel_time/middleware/proto' +require 'travel_time/proto/utils' +require 'travel_time/proto/v2/TimeFilterFastRequest_pb' +require 'travel_time/proto/v2/TimeFilterFastResponse_pb' +require 'travel_time/proto/v2/RequestsCommon_pb' module TravelTime # The Client class provides the main interface to interact with the TravelTime API @@ -85,13 +91,43 @@ def time_filter(locations:, departure_searches: nil, arrival_searches: nil) end def time_filter_fast(locations:, arrival_searches:) - payload = { - locations: locations, - arrival_searches: arrival_searches - }.compact + connection + .payload = { + locations: locations, + arrival_searches: arrival_searches + }.compact perform_request { connection.post('time-filter/fast', payload) } end + # make : + def time_filter_fast_proto(country, origin, destinations, transport, traveltime) + base_uri = 'http://proto.api.traveltimeapp.com/api/v2/' + proto_client = Faraday.new(base_uri) do |f| + f.response :raise_error if TravelTime.config.raise_on_failure + f.response :logger if TravelTime.config.enable_logging + f.use TravelTime::Middleware::ProtoMiddleware + f.adapter :net_http + end + + message = Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest.new( + oneToManyRequest: Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest::OneToMany.new( + departureLocation: origin, + locationDeltas: ProtoUtils.build_deltas(origin, destinations), + transportation: Com::Igeolise::Traveltime::Rabbitmq::Requests::Transportation.new( + { type: ProtoUtils.get_proto_transport_code(transport) } + ), + arrivalTimePeriod: 0, + travelTime: traveltime, + properties: nil + ) + ) + + serialized = Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest.encode(message) + proto_response = proto_client.post("#{country}/time-filter/fast/#{transport}", serialized) + # TODO: decode and send back as response + Com::Igeolise::Traveltime::Rabbitmq::Responses::TimeFilterFastResponse.decode(proto_response).to_h + end + def time_filter_postcodes(departure_searches: nil, arrival_searches: nil) payload = { departure_searches: departure_searches, diff --git a/lib/travel_time/middleware/proto.rb b/lib/travel_time/middleware/proto.rb new file mode 100644 index 0000000..bb016ad --- /dev/null +++ b/lib/travel_time/middleware/proto.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'faraday' +require 'base64' + +module TravelTime + module Middleware + # The Proto middleware is responsible for setting the basic auth headers for proto requests + # on each request. These are automatically taken from the `TravelTime.config`. + class ProtoMiddleware < Faraday::Middleware + def on_request(env) + env.request_headers['Authorization'] = + "Basic #{Base64.encode64("#{TravelTime.config.application_id}:#{TravelTime.config.api_key}")}" + env.request_headers['Content-Type'] = 'application/octet-stream' + env.request_headers['Accept'] = 'application/octet-stream' + end + end + end +end diff --git a/lib/travel_time/proto/source/RequestsCommon.proto b/lib/travel_time/proto/source/RequestsCommon.proto new file mode 100644 index 0000000..6621d1c --- /dev/null +++ b/lib/travel_time/proto/source/RequestsCommon.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package com.igeolise.traveltime.rabbitmq.requests; + +message Coords { + float lat = 1; + float lng = 2; +} + +message Transportation { + TransportationType type = 1; +} + +enum TransportationType { + // Considers all paths found by the following steps: + // * up to 30 minutes of walking (always included even if no stops found) + // * all connections in the 30 minute walking range from public transport + // stops to other public transport stops in travel_time_limit, AND + // * up to 30 minutes of walking from public transport stops that were visited + // by public transport (IOW a path + // [origin]--walking->[stop]--walking-->[destination] is not possible but + // [origin]--walking->[stop]--public_transport-->[stop]--walking--> is. + PUBLIC_TRANSPORT = 0; + // Considers all paths found traveling by car from origin(s) to + // destination(s) within the travel_time_limit + DRIVING = 1; + // Considers all paths found by the following steps: + // * up to 30 minutes of driving (always included even no stops found) + // * all connections in the 30 minute driving range from public transport stops + // to other public transport stops in travel_time_limit, AND + // * up to 30 minutes of walking from public transport stops that were visited + // by public transport (IOW a path + // [origin]--driving->[stop]--walking-->[destination] is not possible but + // [origin]--driving->[stop]--public_transport-->[stop]--walking--> is. + // AND/OR + // * up to 30 minutes of walking + // + DRIVING_AND_PUBLIC_TRANSPORT = 2; + // Considers all paths found travelling by car from origin(s) to + // destination(s) including all paths that are traversable by ferries that + // take cars within the travel_time_limit. + DRIVING_AND_FERRY = 3; + // Considers all paths found travelling by foot from origin(s) to + // destination(s) within the travel_time_limit + WALKING = 4; + // Considers all paths found travelling by foot from origin(s) to + // destination(s) including all paths that are traversable by ferries that + // take passengers within the travel_time_limit + WALKING_AND_FERRY = 7; + // Considers all paths found travelling by bike from origin(s) to + // destination(s) within the travel_time_limit + CYCLING = 5; + // Considers all paths found travelling by bike from origin(s) to + // destination(s) including all paths that are traversable by ferries that + // take bikes within the travel_time_limit + CYCLING_AND_FERRY = 6; +} + +enum TimePeriod { + WEEKDAY_MORNING = 0; +} diff --git a/lib/travel_time/proto/source/TimeFilterFastRequest.proto b/lib/travel_time/proto/source/TimeFilterFastRequest.proto new file mode 100644 index 0000000..05c885c --- /dev/null +++ b/lib/travel_time/proto/source/TimeFilterFastRequest.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package com.igeolise.traveltime.rabbitmq.requests; + +import "RequestsCommon.proto"; + +message TimeFilterFastRequest { + enum Property { + FARES = 0; + DISTANCES = 1; + } + message OneToMany { + Coords departureLocation = 1; + /* + * We encode arrival locations as deltas (relative to the source) using a fixedpoint encoding i.e + * deltaLat = round((lat - sourceLat) * 10^5).toInt + * deltaLon = round((lon - sourceLon) * 10^5).toInt + * + * The deltas should be interleaved in the `locationDeltas` field i.e + * + * locationDeltas[0] should be the first lat + * locationDeltas[1] should be the first lon + * locationDeltas[2] should be the second lat + * ... + * etc + */ + repeated sint32 locationDeltas = 2; + Transportation transportation = 3; + TimePeriod arrivalTimePeriod = 4; + sint32 travelTime = 5; + repeated Property properties = 6; + } + + OneToMany oneToManyRequest = 1; +} \ No newline at end of file diff --git a/lib/travel_time/proto/source/TimeFilterFastResponse.proto b/lib/travel_time/proto/source/TimeFilterFastResponse.proto new file mode 100644 index 0000000..c2b86bb --- /dev/null +++ b/lib/travel_time/proto/source/TimeFilterFastResponse.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; + +package com.igeolise.traveltime.rabbitmq.responses; + +message TimeFilterFastResponse { + message Properties { + repeated sint32 travelTimes = 1; + repeated int32 monthlyFares = 2; + repeated int32 distances = 3; + } + + message Error { + ErrorType type = 1; + } + + enum ErrorType { + /* + * Catch all unknown error type + */ + UNKNOWN = 0; + /* + * oneToManyRequest to many field must not be null + */ + ONE_TO_MANY_MUST_NOT_BE_NULL = 1; + /* + * Source (either departure or arrival location) must not be null + */ + SOURCE_MUST_NOT_BE_NULL = 2; + /* + * Transportation mode must not be null. + */ + TRANSPORTATION_MUST_NOT_BE_NULL = 3; + /* + * Source (either departure or arrival location) must not be null + */ + SOURCE_NOT_IN_GEOMETRY = 4; + + /* + * Transportation mode unrecognized. + */ + UNRECOGNIZED_TRANSPORTATION_MODE = 5; + + /* + * The travel time limit is too low to process this request. + */ + TRAVEL_TIME_LIMIT_TOO_LOW = 6; + + /* + * The travel time limit is too high to process this request. + */ + TRAVEL_TIME_LIMIT_TOO_HIGH = 7; + + /* + * User id not set. + */ + AUTH_ERROR_NO_USER_ID = 8; + + /* + * Message sent to wrong queue - transportation mode cannot be handled. + */ + SERVICE_MISMATCH_WRONG_TRANSPORTATION_MODE = 9; + + /* + * Source is in a area that doesn't have any points that can be out of + * search e.g a lake, mountains or other desolate areas. + */ + SOURCE_OUT_OF_REACH = 10; + + /* + * The interleaved deltas array should have (lat/lon) deltas and have an + * even number of elements + */ + INTERLEAVED_DELTAS_INVALID_COORDINATE_PAIRS = 11; + + /* + * Public transport requests do not support returning distances for + * returned points. + */ + DISTANCE_PROPERTY_NOT_SUPPORTED = 12; + } + + Error error = 1; + Properties properties = 2; +} diff --git a/lib/travel_time/proto/utils.rb b/lib/travel_time/proto/utils.rb new file mode 100644 index 0000000..e1b8123 --- /dev/null +++ b/lib/travel_time/proto/utils.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module TravelTime + # The Response class represent an API response. + class ProtoUtils + def self.encode_fixed_poiunt(source, target) + ((target - source) * 10.pow(5)).round + end + + def self.build_deltas(departure, destinations) + deltas = destinations.map do |destination| + [encode_fixed_poiunt(departure[:lat], destination[:lat]), + encode_fixed_poiunt(departure[:lng], destination[:lng])] + end + deltas.flatten + end + + def self.get_proto_transport_code(transport) + proto_transport_map = { + pt: 0, + 'driving+ferry': 3, + 'cycling+ferry': 6, + 'walking+ferry': 7 + } + proto_transport_map[transport.to_sym] + end + end +end diff --git a/lib/travel_time/proto/v2/RequestsCommon_pb.rb b/lib/travel_time/proto/v2/RequestsCommon_pb.rb new file mode 100644 index 0000000..751caa7 --- /dev/null +++ b/lib/travel_time/proto/v2/RequestsCommon_pb.rb @@ -0,0 +1,44 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: RequestsCommon.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("RequestsCommon.proto", :syntax => :proto3) do + add_message "com.igeolise.traveltime.rabbitmq.requests.Coords" do + optional :lat, :float, 1 + optional :lng, :float, 2 + end + add_message "com.igeolise.traveltime.rabbitmq.requests.Transportation" do + optional :type, :enum, 1, "com.igeolise.traveltime.rabbitmq.requests.TransportationType" + end + add_enum "com.igeolise.traveltime.rabbitmq.requests.TransportationType" do + value :PUBLIC_TRANSPORT, 0 + value :DRIVING, 1 + value :DRIVING_AND_PUBLIC_TRANSPORT, 2 + value :DRIVING_AND_FERRY, 3 + value :WALKING, 4 + value :WALKING_AND_FERRY, 7 + value :CYCLING, 5 + value :CYCLING_AND_FERRY, 6 + end + add_enum "com.igeolise.traveltime.rabbitmq.requests.TimePeriod" do + value :WEEKDAY_MORNING, 0 + end + end +end + +module Com + module Igeolise + module Traveltime + module Rabbitmq + module Requests + Coords = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.Coords").msgclass + Transportation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.Transportation").msgclass + TransportationType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.TransportationType").enummodule + TimePeriod = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.TimePeriod").enummodule + end + end + end + end +end diff --git a/lib/travel_time/proto/v2/TimeFilterFastRequest_pb.rb b/lib/travel_time/proto/v2/TimeFilterFastRequest_pb.rb new file mode 100644 index 0000000..5509a75 --- /dev/null +++ b/lib/travel_time/proto/v2/TimeFilterFastRequest_pb.rb @@ -0,0 +1,40 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: TimeFilterFastRequest.proto + +require 'google/protobuf' + +require_relative 'RequestsCommon_pb' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("TimeFilterFastRequest.proto", :syntax => :proto3) do + add_message "com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest" do + optional :oneToManyRequest, :message, 1, "com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest.OneToMany" + end + add_message "com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest.OneToMany" do + optional :departureLocation, :message, 1, "com.igeolise.traveltime.rabbitmq.requests.Coords" + repeated :locationDeltas, :sint32, 2 + optional :transportation, :message, 3, "com.igeolise.traveltime.rabbitmq.requests.Transportation" + optional :arrivalTimePeriod, :enum, 4, "com.igeolise.traveltime.rabbitmq.requests.TimePeriod" + optional :travelTime, :sint32, 5 + repeated :properties, :enum, 6, "com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest.Property" + end + add_enum "com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest.Property" do + value :FARES, 0 + value :DISTANCES, 1 + end + end +end + +module Com + module Igeolise + module Traveltime + module Rabbitmq + module Requests + TimeFilterFastRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest").msgclass + TimeFilterFastRequest::OneToMany = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest.OneToMany").msgclass + TimeFilterFastRequest::Property = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest.Property").enummodule + end + end + end + end +end diff --git a/lib/travel_time/proto/v2/TimeFilterFastResponse_pb.rb b/lib/travel_time/proto/v2/TimeFilterFastResponse_pb.rb new file mode 100644 index 0000000..1be6de5 --- /dev/null +++ b/lib/travel_time/proto/v2/TimeFilterFastResponse_pb.rb @@ -0,0 +1,51 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: TimeFilterFastResponse.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("TimeFilterFastResponse.proto", :syntax => :proto3) do + add_message "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse" do + optional :error, :message, 1, "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.Error" + optional :properties, :message, 2, "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.Properties" + end + add_message "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.Properties" do + repeated :travelTimes, :sint32, 1 + repeated :monthlyFares, :int32, 2 + repeated :distances, :int32, 3 + end + add_message "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.Error" do + optional :type, :enum, 1, "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.ErrorType" + end + add_enum "com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.ErrorType" do + value :UNKNOWN, 0 + value :ONE_TO_MANY_MUST_NOT_BE_NULL, 1 + value :SOURCE_MUST_NOT_BE_NULL, 2 + value :TRANSPORTATION_MUST_NOT_BE_NULL, 3 + value :SOURCE_NOT_IN_GEOMETRY, 4 + value :UNRECOGNIZED_TRANSPORTATION_MODE, 5 + value :TRAVEL_TIME_LIMIT_TOO_LOW, 6 + value :TRAVEL_TIME_LIMIT_TOO_HIGH, 7 + value :AUTH_ERROR_NO_USER_ID, 8 + value :SERVICE_MISMATCH_WRONG_TRANSPORTATION_MODE, 9 + value :SOURCE_OUT_OF_REACH, 10 + value :INTERLEAVED_DELTAS_INVALID_COORDINATE_PAIRS, 11 + value :DISTANCE_PROPERTY_NOT_SUPPORTED, 12 + end + end +end + +module Com + module Igeolise + module Traveltime + module Rabbitmq + module Responses + TimeFilterFastResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse").msgclass + TimeFilterFastResponse::Properties = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.Properties").msgclass + TimeFilterFastResponse::Error = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.Error").msgclass + TimeFilterFastResponse::ErrorType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse.ErrorType").enummodule + end + end + end + end +end diff --git a/travel_time.gemspec b/travel_time.gemspec index 3ecc198..48a3063 100644 --- a/travel_time.gemspec +++ b/travel_time.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'dry-configurable', '~> 0.14.0' spec.add_dependency 'faraday', '>= 1.10', '< 3.0' + spec.add_dependency 'google-protobuf', '>= 3.21', '< 3.21.9' # Specify which files should be added to the gem when it is released. spec.files = Dir['{bin,lib}/**/*', 'LICENSE.md', 'Rakefile', 'README.md'] From e151ced0aa1d6fcaef2457b80bcc79f626f4af8b Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 10:13:06 +0300 Subject: [PATCH 02/14] add distance endpoint --- .rubocop.yml | 8 ++--- lib/travel_time/client.rb | 65 +++++++++++++++++++--------------- lib/travel_time/proto/utils.rb | 33 ++++++++++++++++- lib/travel_time/response.rb | 9 +++++ lib/travel_time/version.rb | 2 +- 5 files changed, 82 insertions(+), 35 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b0cae42..901243d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,7 +9,8 @@ AllCops: DisplayStyleGuide: true TargetRubyVersion: 2.7 NewCops: enable - Excludes: + Exclude: + - spec/*.rb - lib/travel_time/proto/v2/*.rb # Disabling until TravelTime Rubygems account is MFA-protected. @@ -26,8 +27,7 @@ RSpec/NestedGroups: RSpec/MultipleMemoizedHelpers: Max: 10 -RSpec/Rails/HaveHttpStatus: - Enabled: false - Metrics/ParameterLists: + Enabled: false +RSpec/Rails/HaveHttpStatus: Enabled: false \ No newline at end of file diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index bbc11ec..9ae5df1 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -5,16 +5,13 @@ require 'travel_time/middleware/authentication' require 'travel_time/middleware/proto' require 'travel_time/proto/utils' -require 'travel_time/proto/v2/TimeFilterFastRequest_pb' -require 'travel_time/proto/v2/TimeFilterFastResponse_pb' -require 'travel_time/proto/v2/RequestsCommon_pb' module TravelTime # The Client class provides the main interface to interact with the TravelTime API class Client # rubocop:disable Metrics/ClassLength API_BASE_URL = 'https://api.traveltimeapp.com/v4/' - attr_reader :connection + attr_reader :connection, :proto_connection def initialize @connection = Faraday.new(API_BASE_URL) do |f| @@ -25,12 +22,27 @@ def initialize f.use TravelTime::Middleware::Authentication f.adapter TravelTime.config.http_adapter || Faraday.default_adapter end + + init_proto_connection + end + + def init_proto_connection + @proto_connection = Faraday.new do |f| + f.response :raise_error if TravelTime.config.raise_on_failure + f.response :logger if TravelTime.config.enable_logging + f.use TravelTime::Middleware::ProtoMiddleware + f.adapter TravelTime.config.http_adapter || Faraday.default_adapter + end end def unwrap(response) Response.from_object(response) end + def unwrap_proto(response) + Response.from_object_proto(response) + end + def perform_request unwrap(yield) rescue Faraday::Error => e @@ -41,6 +53,12 @@ def perform_request raise TravelTime::Error.new(exception: e) end + def perform_request_proto + unwrap_proto(yield) + rescue StandardError => e + raise TravelTime::Error.new(exception: e) + end + def map_info perform_request { connection.get('map-info') } end @@ -99,33 +117,22 @@ def time_filter_fast(locations:, arrival_searches:) perform_request { connection.post('time-filter/fast', payload) } end - # make : - def time_filter_fast_proto(country, origin, destinations, transport, traveltime) - base_uri = 'http://proto.api.traveltimeapp.com/api/v2/' - proto_client = Faraday.new(base_uri) do |f| - f.response :raise_error if TravelTime.config.raise_on_failure - f.response :logger if TravelTime.config.enable_logging - f.use TravelTime::Middleware::ProtoMiddleware - f.adapter :net_http + def time_filter_fast_proto(country:, origin:, destinations:, transport:, traveltime:) + message = ProtoUtils.make_proto_message(origin, destinations, transport, traveltime) + payload = ProtoUtils.encode_proto_message(message) + perform_request_proto do + proto_connection.post("http://proto.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}", + payload) end + end - message = Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest.new( - oneToManyRequest: Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest::OneToMany.new( - departureLocation: origin, - locationDeltas: ProtoUtils.build_deltas(origin, destinations), - transportation: Com::Igeolise::Traveltime::Rabbitmq::Requests::Transportation.new( - { type: ProtoUtils.get_proto_transport_code(transport) } - ), - arrivalTimePeriod: 0, - travelTime: traveltime, - properties: nil - ) - ) - - serialized = Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest.encode(message) - proto_response = proto_client.post("#{country}/time-filter/fast/#{transport}", serialized) - # TODO: decode and send back as response - Com::Igeolise::Traveltime::Rabbitmq::Responses::TimeFilterFastResponse.decode(proto_response).to_h + def time_filter_fast_proto_distance(country:, origin:, destinations:, transport:, traveltime:) + message = ProtoUtils.make_proto_message(origin, destinations, transport, traveltime, properties: [1]) + payload = ProtoUtils.encode_proto_message(message) + perform_request_proto do + proto_connection.post("https://proto-with-distance.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}", + payload) + end end def time_filter_postcodes(departure_searches: nil, arrival_searches: nil) diff --git a/lib/travel_time/proto/utils.rb b/lib/travel_time/proto/utils.rb index e1b8123..8c73c1b 100644 --- a/lib/travel_time/proto/utils.rb +++ b/lib/travel_time/proto/utils.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true +require 'travel_time/proto/v2/RequestsCommon_pb' +require 'travel_time/proto/v2/TimeFilterFastRequest_pb' +require 'travel_time/proto/v2/TimeFilterFastResponse_pb' + module TravelTime - # The Response class represent an API response. + # Utilities for encoding/decoding protobuf requests class ProtoUtils def self.encode_fixed_poiunt(source, target) ((target - source) * 10.pow(5)).round @@ -24,5 +28,32 @@ def self.get_proto_transport_code(transport) } proto_transport_map[transport.to_sym] end + + def self.make_one_to_many(origin, destinations, transport, traveltime, properties) + Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest::OneToMany.new( + departureLocation: origin, + locationDeltas: build_deltas(origin, destinations), + transportation: Com::Igeolise::Traveltime::Rabbitmq::Requests::Transportation.new( + { type: get_proto_transport_code(transport) } + ), + arrivalTimePeriod: 0, + travelTime: traveltime, + properties: properties + ) + end + + def self.make_proto_message(origin, destinations, transport, traveltime, properties: nil) + Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest.new( + oneToManyRequest: make_one_to_many(origin, destinations, transport, traveltime, properties) + ) + end + + def self.encode_proto_message(message) + Com::Igeolise::Traveltime::Rabbitmq::Requests::TimeFilterFastRequest.encode(message) + end + + def self.decode_proto_response(response) + Com::Igeolise::Traveltime::Rabbitmq::Responses::TimeFilterFastResponse.decode(response).to_h + end end end diff --git a/lib/travel_time/response.rb b/lib/travel_time/response.rb index f751669..db9190f 100644 --- a/lib/travel_time/response.rb +++ b/lib/travel_time/response.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'faraday' +require 'travel_time/proto/utils' module TravelTime # The Response class represent an API response. @@ -15,6 +16,14 @@ def self.from_object(response) ) end + def self.from_object_proto(response) + new( + status: response.status, + headers: response.headers, + body: ProtoUtils.decode_proto_response(response.body) + ) + end + def self.from_hash(response) new( status: response[:status], diff --git a/lib/travel_time/version.rb b/lib/travel_time/version.rb index 7c3c529..ba9cba8 100644 --- a/lib/travel_time/version.rb +++ b/lib/travel_time/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module TravelTime - VERSION = '0.3.2' + VERSION = '0.4.0' end From d88b5b5abd4c4c95161634b8e7b9edb7ce3d75f3 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 10:24:24 +0300 Subject: [PATCH 03/14] fix test and offences --- lib/travel_time/client.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index 9ae5df1..1dba130 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'faraday' -require 'google/protobuf' require 'travel_time/middleware/authentication' require 'travel_time/middleware/proto' require 'travel_time/proto/utils' @@ -109,11 +108,10 @@ def time_filter(locations:, departure_searches: nil, arrival_searches: nil) end def time_filter_fast(locations:, arrival_searches:) - connection - .payload = { - locations: locations, - arrival_searches: arrival_searches - }.compact + payload = { + locations: locations, + arrival_searches: arrival_searches + }.compact perform_request { connection.post('time-filter/fast', payload) } end From 3372be805ff6eacb84789118d41ca71fb8d6cd53 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 10:30:03 +0300 Subject: [PATCH 04/14] add test --- spec/travel_time/client_spec.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/spec/travel_time/client_spec.rb b/spec/travel_time/client_spec.rb index 5c806fe..404ae97 100644 --- a/spec/travel_time/client_spec.rb +++ b/spec/travel_time/client_spec.rb @@ -128,6 +128,34 @@ it_behaves_like 'an endpoint method' end + describe '#time_filter_fast_proto' do + country = 'uk' + transport = 'pt' + subject(:response) do + client.time_filter_fast_proto(country: country, origin: {}, destinations: {}, transport: transport, + traveltime: 0) + end + + let(:url) { "http://proto.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}" } + let(:stub) { stub_request(:post, url) } + + it_behaves_like 'an endpoint method' + end + + describe '#time_filter_fast_proto_distance' do + country = 'uk' + transport = 'driving+ferry' + subject(:response) do + client.time_filter_fast_proto_distance(country: country, origin: {}, destinations: {}, transport: transport, + traveltime: 0) + end + + let(:url) { "https://proto-with-distance.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}" } + let(:stub) { stub_request(:post, url) } + + it_behaves_like 'an endpoint method' + end + describe '#time_filter_postcodes' do subject(:response) { client.time_filter_postcodes(arrival_searches: []) } From b50661432ad9aaf40de39305e59609ca1f979722 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 10:54:39 +0300 Subject: [PATCH 05/14] update readme --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 2dd27b5..552cfa8 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,41 @@ response = client.time_filter_fast( puts response.body ``` +### [Time Filter (Fast) Proto](https://docs.traveltime.com/api/reference/travel-time-distance-matrix-proto) +A fast version of time filter communicating using [protocol buffers](https://github.com/protocolbuffers/protobuf). + +The request parameters are much more limited and only travel time is returned. In addition, the results are only approximately correct (95% of the results are guaranteed to be within 5% of the routes returned by regular time filter). + +This inflexibility comes with a benefit of faster response times (Over 5x faster compared to regular time filter) and larger limits on the amount of destination points. + +Body attributes: +* origin: Origin point. +* destinations: Destination points. Cannot be more than 200,000. +* transport: Transportation type. +* travelTime: Time limit; +* country: Return the results that are within the specified country + +```ruby +origin = { + lat: 51.508930, + lng: -0.131387, +} + +destinations = [{ + lat: 51.508824, + lng: -0.167093, +}] + +response = client.time_filter_fast_proto( + country: 'UK', + origin: origin, + destinations: destinations, + transport: 'driving+ferry', + traveltime: 7200 +) +puts(response.body) +``` + ### [Time Filter (Postcode Districts)](https://traveltime.com/docs/api/reference/postcode-district-filter) Find districts that have a certain coverage from origin (or to destination) and get statistics about postcodes within such districts. Currently only supports United Kingdom. From 3b6cc90df77ec17955e42db0918a7f5a21d4af4f Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 11:12:39 +0300 Subject: [PATCH 06/14] rubocop: exclude vendor dir --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index 901243d..044c51f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -12,6 +12,7 @@ AllCops: Exclude: - spec/*.rb - lib/travel_time/proto/v2/*.rb + - vendor/**/*.rb # Disabling until TravelTime Rubygems account is MFA-protected. Gemspec/RequireMFA: From 23a432cdede384e7209d9692b9362880a23ac45d Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 11:16:44 +0300 Subject: [PATCH 07/14] rubocop: exclude gemfiles --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index 044c51f..daf83f5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -13,6 +13,7 @@ AllCops: - spec/*.rb - lib/travel_time/proto/v2/*.rb - vendor/**/*.rb + - gemfiles/vendor/bundle/**/* # Disabling until TravelTime Rubygems account is MFA-protected. Gemspec/RequireMFA: From 1f2857bcd8378abcf32170c42e9cf6b4c04024d8 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 11:20:32 +0300 Subject: [PATCH 08/14] robocop: exclude all vendor --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index daf83f5..4d0fad0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -12,7 +12,7 @@ AllCops: Exclude: - spec/*.rb - lib/travel_time/proto/v2/*.rb - - vendor/**/*.rb + - vendor/**/* - gemfiles/vendor/bundle/**/* # Disabling until TravelTime Rubygems account is MFA-protected. From 0d68e00dc4ce91a914756b0a041c26607420be22 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 12:13:57 +0300 Subject: [PATCH 09/14] add missing test --- lib/travel_time/proto/utils.rb | 6 ++-- .../proto/travel_time/proto_utils_spec.rb | 21 +++++++++++++ .../middleware/proto_middleware_spec.rb | 30 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 spec/travel_time/middleware/proto/travel_time/proto_utils_spec.rb create mode 100644 spec/travel_time/middleware/proto_middleware_spec.rb diff --git a/lib/travel_time/proto/utils.rb b/lib/travel_time/proto/utils.rb index 8c73c1b..411d7e3 100644 --- a/lib/travel_time/proto/utils.rb +++ b/lib/travel_time/proto/utils.rb @@ -7,14 +7,14 @@ module TravelTime # Utilities for encoding/decoding protobuf requests class ProtoUtils - def self.encode_fixed_poiunt(source, target) + def self.encode_fixed_point(source, target) ((target - source) * 10.pow(5)).round end def self.build_deltas(departure, destinations) deltas = destinations.map do |destination| - [encode_fixed_poiunt(departure[:lat], destination[:lat]), - encode_fixed_poiunt(departure[:lng], destination[:lng])] + [encode_fixed_point(departure[:lat], destination[:lat]), + encode_fixed_point(departure[:lng], destination[:lng])] end deltas.flatten end diff --git a/spec/travel_time/middleware/proto/travel_time/proto_utils_spec.rb b/spec/travel_time/middleware/proto/travel_time/proto_utils_spec.rb new file mode 100644 index 0000000..236b1b6 --- /dev/null +++ b/spec/travel_time/middleware/proto/travel_time/proto_utils_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +RSpec.describe TravelTime::ProtoUtils do + let(:utils) { described_class } + + describe '.encode_fixed_point' do + it 'calculates fixed point correctly' do + expected = -11 + departure_lat = 51.508930 + destination_lat = 51.508824 + expect(utils.encode_fixed_point(departure_lat, destination_lat)).to eq(expected) + end + + it 'builds deltas correctly' do + expected = [-11, -3571] + departure = { lat: 51.508930, lng: -0.131387 } + destinations = [{ lat: 51.508824, lng: -0.167093 }] + expect(utils.build_deltas(departure, destinations)).to eq(expected) + end + end +end diff --git a/spec/travel_time/middleware/proto_middleware_spec.rb b/spec/travel_time/middleware/proto_middleware_spec.rb new file mode 100644 index 0000000..47e3f23 --- /dev/null +++ b/spec/travel_time/middleware/proto_middleware_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.describe TravelTime::Middleware::ProtoMiddleware do + let(:faraday_env) do + Faraday::Env.new.tap do |env| + env.request_headers = Faraday::Utils::Headers.new + end + end + let(:middleware) { described_class.new } + + it 'adds basic auth header' do + middleware.on_request(faraday_env) + value = faraday_env.request_headers['Authorization'] + expect(value).to be_truthy + end + + it 'automatically adds Accept type header' do + middleware.on_request(faraday_env) + value = faraday_env.request_headers['Accept'] + expected = 'application/octet-stream' + expect(value).to eq(expected) + end + + it 'automatically adds Content-Type type header' do + middleware.on_request(faraday_env) + value = faraday_env.request_headers['Content-Type'] + expected = 'application/octet-stream' + expect(value).to eq(expected) + end +end From cb715b39eb82c187ec21f2e3c21f2bc89a74c54c Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 15:20:57 +0300 Subject: [PATCH 10/14] - require proto/utils at top level lib - remove proto middleware -reorder spec directory --- lib/travel_time.rb | 1 + lib/travel_time/client.rb | 6 ++-- lib/travel_time/middleware/proto.rb | 19 ------------ lib/travel_time/response.rb | 1 - .../middleware/proto_middleware_spec.rb | 30 ------------------- .../travel_time => proto}/proto_utils_spec.rb | 0 6 files changed, 4 insertions(+), 53 deletions(-) delete mode 100644 lib/travel_time/middleware/proto.rb delete mode 100644 spec/travel_time/middleware/proto_middleware_spec.rb rename spec/travel_time/{middleware/proto/travel_time => proto}/proto_utils_spec.rb (100%) diff --git a/lib/travel_time.rb b/lib/travel_time.rb index 050261e..77d7e95 100644 --- a/lib/travel_time.rb +++ b/lib/travel_time.rb @@ -5,6 +5,7 @@ require 'travel_time/error' require 'travel_time/response' require 'travel_time/version' +require 'travel_time/proto/utils' # Main TravelTime module module TravelTime diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index a8cd9e0..8dda06a 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -2,8 +2,6 @@ require 'faraday' require 'travel_time/middleware/authentication' -require 'travel_time/middleware/proto' -require 'travel_time/proto/utils' module TravelTime # The Client class provides the main interface to interact with the TravelTime API @@ -29,7 +27,9 @@ def init_proto_connection @proto_connection = Faraday.new do |f| f.response :raise_error if TravelTime.config.raise_on_failure f.response :logger if TravelTime.config.enable_logging - f.use TravelTime::Middleware::ProtoMiddleware + f.headers['Content-Type'] = 'application/octet-stream' + f.headers['Accept'] = 'application/octet-stream' + f.request :authorization, :basic, TravelTime.config.application_id, TravelTime.config.api_key f.adapter TravelTime.config.http_adapter || Faraday.default_adapter end end diff --git a/lib/travel_time/middleware/proto.rb b/lib/travel_time/middleware/proto.rb deleted file mode 100644 index bb016ad..0000000 --- a/lib/travel_time/middleware/proto.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'faraday' -require 'base64' - -module TravelTime - module Middleware - # The Proto middleware is responsible for setting the basic auth headers for proto requests - # on each request. These are automatically taken from the `TravelTime.config`. - class ProtoMiddleware < Faraday::Middleware - def on_request(env) - env.request_headers['Authorization'] = - "Basic #{Base64.encode64("#{TravelTime.config.application_id}:#{TravelTime.config.api_key}")}" - env.request_headers['Content-Type'] = 'application/octet-stream' - env.request_headers['Accept'] = 'application/octet-stream' - end - end - end -end diff --git a/lib/travel_time/response.rb b/lib/travel_time/response.rb index db9190f..3139c96 100644 --- a/lib/travel_time/response.rb +++ b/lib/travel_time/response.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'faraday' -require 'travel_time/proto/utils' module TravelTime # The Response class represent an API response. diff --git a/spec/travel_time/middleware/proto_middleware_spec.rb b/spec/travel_time/middleware/proto_middleware_spec.rb deleted file mode 100644 index 47e3f23..0000000 --- a/spec/travel_time/middleware/proto_middleware_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe TravelTime::Middleware::ProtoMiddleware do - let(:faraday_env) do - Faraday::Env.new.tap do |env| - env.request_headers = Faraday::Utils::Headers.new - end - end - let(:middleware) { described_class.new } - - it 'adds basic auth header' do - middleware.on_request(faraday_env) - value = faraday_env.request_headers['Authorization'] - expect(value).to be_truthy - end - - it 'automatically adds Accept type header' do - middleware.on_request(faraday_env) - value = faraday_env.request_headers['Accept'] - expected = 'application/octet-stream' - expect(value).to eq(expected) - end - - it 'automatically adds Content-Type type header' do - middleware.on_request(faraday_env) - value = faraday_env.request_headers['Content-Type'] - expected = 'application/octet-stream' - expect(value).to eq(expected) - end -end diff --git a/spec/travel_time/middleware/proto/travel_time/proto_utils_spec.rb b/spec/travel_time/proto/proto_utils_spec.rb similarity index 100% rename from spec/travel_time/middleware/proto/travel_time/proto_utils_spec.rb rename to spec/travel_time/proto/proto_utils_spec.rb From 9a7cabe60676b03feefbac60f74bbe2d35ed7d72 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 15:54:44 +0300 Subject: [PATCH 11/14] fix rubocop ofences --- lib/travel_time/client.rb | 6 +++--- .../travel_time/proto/{ => travel_time}/proto_utils_spec.rb | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename spec/travel_time/proto/{ => travel_time}/proto_utils_spec.rb (100%) diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index 8dda06a..8c69d21 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -23,13 +23,13 @@ def initialize init_proto_connection end - def init_proto_connection + def init_proto_connection # rubocop:disable Metrics/AbcSize @proto_connection = Faraday.new do |f| - f.response :raise_error if TravelTime.config.raise_on_failure - f.response :logger if TravelTime.config.enable_logging f.headers['Content-Type'] = 'application/octet-stream' f.headers['Accept'] = 'application/octet-stream' f.request :authorization, :basic, TravelTime.config.application_id, TravelTime.config.api_key + f.response :raise_error if TravelTime.config.raise_on_failure + f.response :logger if TravelTime.config.enable_logging f.adapter TravelTime.config.http_adapter || Faraday.default_adapter end end diff --git a/spec/travel_time/proto/proto_utils_spec.rb b/spec/travel_time/proto/travel_time/proto_utils_spec.rb similarity index 100% rename from spec/travel_time/proto/proto_utils_spec.rb rename to spec/travel_time/proto/travel_time/proto_utils_spec.rb From c1049dbc30de5f6a98f8664edf3101bf922b7dd3 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Fri, 28 Oct 2022 17:15:35 +0300 Subject: [PATCH 12/14] remove disable distance endpoint --- lib/travel_time/client.rb | 9 --------- spec/travel_time/client_spec.rb | 14 -------------- 2 files changed, 23 deletions(-) diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index 8c69d21..6de1c40 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -126,15 +126,6 @@ def time_filter_fast_proto(country:, origin:, destinations:, transport:, travelt end end - def time_filter_fast_proto_distance(country:, origin:, destinations:, transport:, traveltime:) - message = ProtoUtils.make_proto_message(origin, destinations, transport, traveltime, properties: [1]) - payload = ProtoUtils.encode_proto_message(message) - perform_request_proto do - proto_connection.post("https://proto-with-distance.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}", - payload) - end - end - def time_filter_postcodes(departure_searches: nil, arrival_searches: nil) payload = { departure_searches: departure_searches, diff --git a/spec/travel_time/client_spec.rb b/spec/travel_time/client_spec.rb index 404ae97..5f90947 100644 --- a/spec/travel_time/client_spec.rb +++ b/spec/travel_time/client_spec.rb @@ -142,20 +142,6 @@ it_behaves_like 'an endpoint method' end - describe '#time_filter_fast_proto_distance' do - country = 'uk' - transport = 'driving+ferry' - subject(:response) do - client.time_filter_fast_proto_distance(country: country, origin: {}, destinations: {}, transport: transport, - traveltime: 0) - end - - let(:url) { "https://proto-with-distance.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}" } - let(:stub) { stub_request(:post, url) } - - it_behaves_like 'an endpoint method' - end - describe '#time_filter_postcodes' do subject(:response) { client.time_filter_postcodes(arrival_searches: []) } From 8e5c6b2e634cf63b69cb8a489954ecefd3814b55 Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Mon, 7 Nov 2022 11:26:31 +0200 Subject: [PATCH 13/14] revert to middleware for compatability --- lib/travel_time/client.rb | 7 ++--- lib/travel_time/middleware/proto.rb | 19 ++++++++++++ .../middleware/proto_middleware_spec.rb | 30 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 lib/travel_time/middleware/proto.rb create mode 100644 spec/travel_time/middleware/proto_middleware_spec.rb diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index 6de1c40..1288ad1 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -2,6 +2,7 @@ require 'faraday' require 'travel_time/middleware/authentication' +require 'travel_time/middleware/proto' module TravelTime # The Client class provides the main interface to interact with the TravelTime API @@ -23,11 +24,9 @@ def initialize init_proto_connection end - def init_proto_connection # rubocop:disable Metrics/AbcSize + def init_proto_connection @proto_connection = Faraday.new do |f| - f.headers['Content-Type'] = 'application/octet-stream' - f.headers['Accept'] = 'application/octet-stream' - f.request :authorization, :basic, TravelTime.config.application_id, TravelTime.config.api_key + f.use TravelTime::Middleware::ProtoMiddleware f.response :raise_error if TravelTime.config.raise_on_failure f.response :logger if TravelTime.config.enable_logging f.adapter TravelTime.config.http_adapter || Faraday.default_adapter diff --git a/lib/travel_time/middleware/proto.rb b/lib/travel_time/middleware/proto.rb new file mode 100644 index 0000000..bb016ad --- /dev/null +++ b/lib/travel_time/middleware/proto.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'faraday' +require 'base64' + +module TravelTime + module Middleware + # The Proto middleware is responsible for setting the basic auth headers for proto requests + # on each request. These are automatically taken from the `TravelTime.config`. + class ProtoMiddleware < Faraday::Middleware + def on_request(env) + env.request_headers['Authorization'] = + "Basic #{Base64.encode64("#{TravelTime.config.application_id}:#{TravelTime.config.api_key}")}" + env.request_headers['Content-Type'] = 'application/octet-stream' + env.request_headers['Accept'] = 'application/octet-stream' + end + end + end +end diff --git a/spec/travel_time/middleware/proto_middleware_spec.rb b/spec/travel_time/middleware/proto_middleware_spec.rb new file mode 100644 index 0000000..47e3f23 --- /dev/null +++ b/spec/travel_time/middleware/proto_middleware_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.describe TravelTime::Middleware::ProtoMiddleware do + let(:faraday_env) do + Faraday::Env.new.tap do |env| + env.request_headers = Faraday::Utils::Headers.new + end + end + let(:middleware) { described_class.new } + + it 'adds basic auth header' do + middleware.on_request(faraday_env) + value = faraday_env.request_headers['Authorization'] + expect(value).to be_truthy + end + + it 'automatically adds Accept type header' do + middleware.on_request(faraday_env) + value = faraday_env.request_headers['Accept'] + expected = 'application/octet-stream' + expect(value).to eq(expected) + end + + it 'automatically adds Content-Type type header' do + middleware.on_request(faraday_env) + value = faraday_env.request_headers['Content-Type'] + expected = 'application/octet-stream' + expect(value).to eq(expected) + end +end From 402039e850cd0484a2269339e1a822e00cc4d97b Mon Sep 17 00:00:00 2001 From: EivydasKoc Date: Mon, 7 Nov 2022 11:46:53 +0200 Subject: [PATCH 14/14] - enable proto distance - add disclaimer to readme on proto distance --- README.md | 34 +++++++++++++++++++++++++++++++++ lib/travel_time/client.rb | 9 +++++++++ spec/travel_time/client_spec.rb | 14 ++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/README.md b/README.md index 552cfa8..1648839 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,40 @@ response = client.time_filter_fast_proto( puts(response.body) ``` +### Time Filter (Fast) Proto Distance +A version of `Time Filter (Fast) Proto` endpoint that also returns distance information. Request parameters are even more limited than `Time Filter (Fast) Proto`. + +This endpoint is not enabled by default, please [contact us](https://traveltime.com/contact-us) if you wish to obtain access. + +Body attributes: +* origin: Origin point. +* destinations: Destination points. Cannot be more than 200,000. +* transport: Transportation type. +* travelTime: Time limit; +* country: Return the results that are within the specified country + +```ruby +origin = { + lat: 51.508930, + lng: -0.131387, +} + +destinations = [{ + lat: 51.508824, + lng: -0.167093, +}] + +response = client.time_filter_fast_proto_distance( + country: 'UK', + origin: origin, + destinations: destinations, + transport: 'driving+ferry', + traveltime: 7200 +) +puts(response.body) +``` + + ### [Time Filter (Postcode Districts)](https://traveltime.com/docs/api/reference/postcode-district-filter) Find districts that have a certain coverage from origin (or to destination) and get statistics about postcodes within such districts. Currently only supports United Kingdom. diff --git a/lib/travel_time/client.rb b/lib/travel_time/client.rb index 1288ad1..f80bac8 100644 --- a/lib/travel_time/client.rb +++ b/lib/travel_time/client.rb @@ -125,6 +125,15 @@ def time_filter_fast_proto(country:, origin:, destinations:, transport:, travelt end end + def time_filter_fast_proto_distance(country:, origin:, destinations:, transport:, traveltime:) + message = ProtoUtils.make_proto_message(origin, destinations, transport, traveltime, properties: [1]) + payload = ProtoUtils.encode_proto_message(message) + perform_request_proto do + proto_connection.post("https://proto-with-distance.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}", + payload) + end + end + def time_filter_postcodes(departure_searches: nil, arrival_searches: nil) payload = { departure_searches: departure_searches, diff --git a/spec/travel_time/client_spec.rb b/spec/travel_time/client_spec.rb index 5f90947..404ae97 100644 --- a/spec/travel_time/client_spec.rb +++ b/spec/travel_time/client_spec.rb @@ -142,6 +142,20 @@ it_behaves_like 'an endpoint method' end + describe '#time_filter_fast_proto_distance' do + country = 'uk' + transport = 'driving+ferry' + subject(:response) do + client.time_filter_fast_proto_distance(country: country, origin: {}, destinations: {}, transport: transport, + traveltime: 0) + end + + let(:url) { "https://proto-with-distance.api.traveltimeapp.com/api/v2/#{country}/time-filter/fast/#{transport}" } + let(:stub) { stub_request(:post, url) } + + it_behaves_like 'an endpoint method' + end + describe '#time_filter_postcodes' do subject(:response) { client.time_filter_postcodes(arrival_searches: []) }