From a80ede5d540e111a6da5e037fab9cf54213232de Mon Sep 17 00:00:00 2001 From: Andrey Marchenko Date: Thu, 28 Sep 2023 17:17:45 +0200 Subject: [PATCH] migrate to Core transport WIP --- lib/datadog/ci/transport/http.rb | 122 ++------ sig/datadog/ci/test_visibility/transport.rbs | 4 +- sig/datadog/ci/transport/http.rbs | 49 +-- spec/datadog/ci/transport/http_spec.rb | 286 ++---------------- .../core/transport/http/adapters/net.rbs | 67 ++++ .../0/datadog/core/transport/http/env.rbs | 33 ++ .../0/datadog/core/transport/request.rbs | 11 + .../0/datadog/core/transport/response.rbs | 35 +++ 8 files changed, 215 insertions(+), 392 deletions(-) create mode 100644 vendor/rbs/ddtrace/0/datadog/core/transport/http/adapters/net.rbs create mode 100644 vendor/rbs/ddtrace/0/datadog/core/transport/http/env.rbs create mode 100644 vendor/rbs/ddtrace/0/datadog/core/transport/request.rbs create mode 100644 vendor/rbs/ddtrace/0/datadog/core/transport/response.rbs diff --git a/lib/datadog/ci/transport/http.rb b/lib/datadog/ci/transport/http.rb index a59ffb2f..f96a417d 100644 --- a/lib/datadog/ci/transport/http.rb +++ b/lib/datadog/ci/transport/http.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true require "net/http" +require "datadog/core/transport/http/adapters/net" +require "datadog/core/transport/http/env" +require "datadog/core/transport/request" require_relative "gzip" require_relative "../ext/transport" @@ -26,126 +29,45 @@ def initialize(host:, timeout: DEFAULT_TIMEOUT, port: nil, ssl: true, compress: @compress = compress.nil? ? false : compress end - def request(path:, payload:, headers:, method: "post") - raise "Unknown method #{method}" unless respond_to?(method, true) - + def request(path:, payload:, headers:, verb: "post") if compress headers[Ext::Transport::HEADER_CONTENT_ENCODING] = Ext::Transport::CONTENT_ENCODING_GZIP payload = Gzip.compress(payload) end Datadog.logger.debug do - "Sending #{method} request: host=#{host}; port=#{port}; ssl_enabled=#{ssl}; " \ + "Sending #{verb} request: host=#{host}; port=#{port}; ssl_enabled=#{ssl}; " \ "compression_enabled=#{compress}; path=#{path}; payload_size=#{payload.size}" end - send(method, path: path, payload: payload, headers: headers) + ResponseDecorator.new( + adapter.call( + build_env(path: path, payload: payload, headers: headers, verb: verb) + ) + ) end private - def open(&block) - req = ::Net::HTTP.new(@host, @port) - - req.use_ssl = @ssl - req.open_timeout = req.read_timeout = @timeout - - req.start(&block) + def build_env(path:, payload:, headers:, verb:) + env = Datadog::Core::Transport::HTTP::Env.new( + Datadog::Core::Transport::Request.new + ) + env.body = payload + env.path = path + env.headers = headers + env.verb = verb + env end - def post(path:, headers:, payload:) - post = ::Net::HTTP::Post.new(path, headers) - post.body = payload - - http_response = open do |http| - http.request(post) - end - - Response.new(http_response) - rescue => e - Datadog.logger.debug("Unable to send events: #{e}") - - InternalErrorResponse.new(e) + def adapter + @adapter ||= Datadog::Core::Transport::HTTP::Adapters::Net.new(host, port, timeout: timeout, ssl: ssl) end - # Data structure for an HTTP Response - class Response - attr_reader :http_response - - def initialize(http_response) - @http_response = http_response - end - - def payload - http_response.body - end - - def code - http_response.code.to_i - end - - def ok? - code.between?(200, 299) - end - - def unsupported? - code == 415 - end - - def not_found? - code == 404 - end - - def client_error? - code.between?(400, 499) - end - - def server_error? - code.between?(500, 599) - end - - def internal_error? - false - end - + class ResponseDecorator < SimpleDelegator def trace_count 0 end - - def inspect - "#{self.class} ok?:#{ok?} unsupported?:#{unsupported?}, " \ - "not_found?:#{not_found?}, client_error?:#{client_error?}, " \ - "server_error?:#{server_error?}, internal_error?:#{internal_error?}, " \ - "payload:#{payload}" - end - end - - class InternalErrorResponse < Response - class DummyNetHTTPResponse - def body - "" - end - - def code - "-1" - end - end - - attr_reader :error - - def initialize(error) - super(DummyNetHTTPResponse.new) - - @error = error - end - - def internal_error? - true - end - - def inspect - "#{super}, error_class:#{error.class}, error:#{error}" - end end end end diff --git a/sig/datadog/ci/test_visibility/transport.rbs b/sig/datadog/ci/test_visibility/transport.rbs index c1058380..774564c2 100644 --- a/sig/datadog/ci/test_visibility/transport.rbs +++ b/sig/datadog/ci/test_visibility/transport.rbs @@ -24,11 +24,11 @@ module Datadog ?max_payload_size: Integer ) -> void - def send_traces: (Array[Datadog::Tracing::TraceSegment] traces) -> ::Array[Datadog::CI::Transport::HTTP::Response] + def send_traces: (Array[Datadog::Tracing::TraceSegment] traces) -> ::Array[Datadog::CI::Transport::HTTP::ResponseDecorator] private - def send_payload: (String encoded_payload) -> Datadog::CI::Transport::HTTP::Response + def send_payload: (String encoded_payload) -> Datadog::CI::Transport::HTTP::ResponseDecorator def pack_events: (Array[String] encoded_events) -> String def encode_traces: (Array[Datadog::Tracing::TraceSegment] traces) -> ::Array[String] def encode_span: (Datadog::Tracing::TraceSegment trace, Datadog::Tracing::Span span) -> String? diff --git a/sig/datadog/ci/transport/http.rbs b/sig/datadog/ci/transport/http.rbs index bab7f203..1d0e6f0c 100644 --- a/sig/datadog/ci/transport/http.rbs +++ b/sig/datadog/ci/transport/http.rbs @@ -1,7 +1,12 @@ +class SimpleDelegator +end + module Datadog module CI module Transport class HTTP + @adapter: Datadog::Core::Transport::HTTP::Adapters::Net + attr_reader host: String attr_reader port: Integer? attr_reader ssl: bool @@ -12,50 +17,20 @@ module Datadog def initialize: (host: String, ?port: Integer?, ?ssl: bool, ?timeout: Integer, ?compress: bool) -> void - def request: (?method: String, payload: String, headers: Hash[String, String], path: String) -> Response + def request: (?verb: String, payload: String, headers: Hash[String, String], path: String) -> ResponseDecorator private - def open: () { (::Net::HTTP) -> Net::HTTPResponse } -> Net::HTTPResponse - - def post: (payload: String, headers: Hash[String, String], path: String) -> Response - - class Response - attr_reader http_response: (Net::HTTPResponse | InternalErrorResponse::DummyNetHTTPResponse) - - def initialize: ((Net::HTTPResponse | InternalErrorResponse::DummyNetHTTPResponse) http_response) -> void - - def payload: () -> String - - def code: () -> Integer - - def ok?: () -> bool + def adapter: () -> Datadog::Core::Transport::HTTP::Adapters::Net - def unsupported?: () -> bool + def build_env: (payload: String, headers: Hash[String, String], path: String, verb: String) -> Datadog::Core::Transport::HTTP::Env - def not_found?: () -> bool - - def client_error?: () -> bool - - def server_error?: () -> bool - - def internal_error?: () -> bool - - def inspect: () -> ::String - end - - class InternalErrorResponse < Response - class DummyNetHTTPResponse - def body: () -> "" - def code: () -> "-1" - end - - attr_reader error: StandardError - @error: StandardError - - def initialize: (StandardError error) -> void + class ResponseDecorator < ::SimpleDelegator + def initialize: (untyped anything) -> void + def trace_count: () -> Integer end end end end end + diff --git a/spec/datadog/ci/transport/http_spec.rb b/spec/datadog/ci/transport/http_spec.rb index 09009b2e..fc6aacba 100644 --- a/spec/datadog/ci/transport/http_spec.rb +++ b/spec/datadog/ci/transport/http_spec.rb @@ -9,21 +9,17 @@ let(:ssl) { true } let(:options) { {} } - shared_context "HTTP connection stub" do - let(:http_connection) { instance_double(::Net::HTTP) } + shared_context "HTTP adapter stub" do + let(:adapter) { instance_double(::Datadog::Core::Transport::HTTP::Adapters::Net) } before do - allow(::Net::HTTP).to receive(:new) + allow(::Datadog::Core::Transport::HTTP::Adapters::Net).to receive(:new) .with( transport.host, - transport.port + transport.port, + timeout: transport.timeout, + ssl: transport.ssl ).and_return(http_connection) - - allow(http_connection).to receive(:open_timeout=).with(transport.timeout) - allow(http_connection).to receive(:read_timeout=).with(transport.timeout) - allow(http_connection).to receive(:use_ssl=).with(transport.ssl) - - allow(http_connection).to receive(:start).and_yield(http_connection) end end @@ -99,262 +95,46 @@ subject(:request) { transport.request(path: path, payload: payload, headers: headers, **request_options) } context "when request is successful" do - before { expect(http_connection).to receive(:request).and_return(http_response) } + let(:env) do + env = Datadog::Core::Transport::HTTP::Env.new( + Datadog::Core::Transport::Request.new + ) + env.body = payload + env.path = path + env.headers = headers + env.verb = "post" + env + end + before do + expect(adapter).to receive(:call).with(env).and_return(http_response) + end it "produces a response" do - is_expected.to be_a_kind_of(described_class::Response) + is_expected.to be_a_kind_of(described_class::ResponseDecorator) expect(request.http_response).to be(http_response) end end context "when error in connecting to server" do - before { expect(http_connection).to receive(:request).and_raise(StandardError) } + before { expect(adapter).to receive(:request).and_raise(StandardError) } it { expect(request).to be_a_kind_of(described_class::InternalErrorResponse) } end - context "when method is unknown" do - let(:request_options) { {method: "delete"} } - - it { expect { request }.to raise_error("Unknown method delete") } - end - - context "when compressing payload" do - let(:headers) { {"Content-Type" => "application/json"} } - let(:expected_headers) { {"Content-Type" => "application/json", "Content-Encoding" => "gzip"} } - let(:options) { {compress: true} } - let(:post_request) { double(:post_request) } - - before do - expect(::Net::HTTP::Post).to receive(:new).with(path, expected_headers).and_return(post_request) - expect(post_request).to receive(:body=).with(Datadog::CI::Transport::Gzip.compress(payload)) - expect(http_connection).to receive(:request).with(post_request).and_return(http_response) - end - - it { expect(request.http_response).to be(http_response) } - end - end -end - -RSpec.describe Datadog::CI::Transport::HTTP::Response do - subject(:response) { described_class.new(http_response) } - - let(:http_response) { instance_double(::Net::HTTPResponse) } - - describe "#initialize" do - it { is_expected.to have_attributes(http_response: http_response) } - end - - describe "#payload" do - subject(:payload) { response.payload } - - let(:http_response) { instance_double(::Net::HTTPResponse, body: double("body")) } - - it { is_expected.to be(http_response.body) } - end - - describe "#code" do - subject(:code) { response.code } - - let(:http_response) { instance_double(::Net::HTTPResponse, code: "200") } - - it { is_expected.to eq(200) } - end - - describe "#ok?" do - subject(:ok?) { response.ok? } - - let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } - - context "when code not 2xx" do - let(:code) { 199 } - - it { is_expected.to be false } - end - - context "when code is 200" do - let(:code) { 200 } - - it { is_expected.to be true } - end - - context "when code is 299" do - let(:code) { 299 } - - it { is_expected.to be true } - end - - context "when code is greater than 299" do - let(:code) { 300 } - - it { is_expected.to be false } - end - end - - describe "#unsupported?" do - subject(:unsupported?) { response.unsupported? } - - let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } - - context "when code is 400" do - let(:code) { 400 } - - it { is_expected.to be false } - end - - context "when code is 415" do - let(:code) { 415 } - - it { is_expected.to be true } - end - end - - describe "#not_found?" do - subject(:not_found?) { response.not_found? } - - let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } - - context "when code is 400" do - let(:code) { 400 } - - it { is_expected.to be false } - end - - context "when code is 404" do - let(:code) { 404 } - - it { is_expected.to be true } - end - end - - describe "#client_error?" do - subject(:client_error?) { response.client_error? } - - let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } - - context "when code is 399" do - let(:code) { 399 } - - it { is_expected.to be false } - end - - context "when code is 400" do - let(:code) { 400 } - - it { is_expected.to be true } - end - - context "when code is 499" do - let(:code) { 499 } - - it { is_expected.to be true } - end - - context "when code is 500" do - let(:code) { 500 } - - it { is_expected.to be false } - end - end - - describe "#server_error?" do - subject(:server_error?) { response.server_error? } - - let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } - - context "when code is 499" do - let(:code) { 499 } - - it { is_expected.to be false } - end - - context "when code is 500" do - let(:code) { 500 } - - it { is_expected.to be true } - end - - context "when code is 599" do - let(:code) { 599 } - - it { is_expected.to be true } - end - - context "when code is 600" do - let(:code) { 600 } - - it { is_expected.to be false } - end - end - - describe "#internal_error?" do - subject(:internal_error?) { response.internal_error? } - - it { is_expected.to be false } - end -end - -RSpec.describe Datadog::CI::Transport::HTTP::InternalErrorResponse do - subject(:response) { described_class.new(error) } - - let(:error) { instance_double(StandardError, class: "StandardError", to_s: "error message") } - - describe "#initialize" do - it { is_expected.to have_attributes(error: error) } - end - - describe "#payload" do - subject(:payload) { response.payload } - - it { is_expected.to eq("") } - end - - describe "#code" do - subject(:code) { response.code } - - it { is_expected.to eq(-1) } - end - - describe "#ok?" do - subject(:ok?) { response.ok? } - - it { is_expected.to be false } - end - - describe "#unsupported?" do - subject(:unsupported?) { response.unsupported? } - - it { is_expected.to be false } - end - - describe "#not_found?" do - subject(:not_found?) { response.not_found? } - - it { is_expected.to be false } - end - - describe "#client_error?" do - subject(:client_error?) { response.client_error? } - - it { is_expected.to be false } - end - - describe "#server_error?" do - subject(:server_error?) { response.server_error? } - - it { is_expected.to be false } - end - - describe "#internal_error?" do - subject(:internal_error?) { response.internal_error? } - - it { is_expected.to be true } - end + # context "when compressing payload" do + # let(:headers) { {"Content-Type" => "application/json"} } + # let(:expected_headers) { {"Content-Type" => "application/json", "Content-Encoding" => "gzip"} } + # let(:options) { {compress: true} } + # let(:post_request) { double(:post_request) } - describe "#inspect" do - subject(:inspect) { response.inspect } + # before do + # expect(::Net::HTTP::Post).to receive(:new).with(path, expected_headers).and_return(post_request) + # expect(post_request).to receive(:body=).with(Datadog::CI::Transport::Gzip.compress(payload)) + # expect(http_connection).to receive(:request).with(post_request).and_return(http_response) + # end - it { is_expected.to include("error_class:StandardError, error:error message") } + # it { expect(request.http_response).to be(http_response) } + # end end end diff --git a/vendor/rbs/ddtrace/0/datadog/core/transport/http/adapters/net.rbs b/vendor/rbs/ddtrace/0/datadog/core/transport/http/adapters/net.rbs new file mode 100644 index 00000000..52968f54 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/transport/http/adapters/net.rbs @@ -0,0 +1,67 @@ +module Datadog + module Core + module Transport + module HTTP + module Adapters + class Net + attr_reader hostname: untyped + + attr_reader port: untyped + + attr_reader timeout: untyped + + attr_reader ssl: untyped + + DEFAULT_TIMEOUT: 30 + + def initialize: (?untyped? hostname, ?untyped? port, **untyped options) -> void + + def self.build: (untyped agent_settings) -> untyped + + def open: () ?{ () -> untyped } -> untyped + + def call: (untyped env) -> untyped + + def get: (untyped env) -> untyped + + def post: (untyped env) -> untyped + + def url: () -> ::String + + class UnknownHTTPMethod < StandardError + attr_reader verb: untyped + + def initialize: (untyped verb) -> void + + def message: () -> ::String + end + + class Response + include Transport::Response + + attr_reader http_response: untyped + + def initialize: (untyped http_response) -> void + + def payload: () -> untyped + + def code: () -> untyped + + def ok?: () -> untyped + + def unsupported?: () -> untyped + + def not_found?: () -> untyped + + def client_error?: () -> untyped + + def server_error?: () -> untyped + + def inspect: () -> ::String + end + end + end + end + end + end +end diff --git a/vendor/rbs/ddtrace/0/datadog/core/transport/http/env.rbs b/vendor/rbs/ddtrace/0/datadog/core/transport/http/env.rbs new file mode 100644 index 00000000..a2f9d32c --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/transport/http/env.rbs @@ -0,0 +1,33 @@ +module Datadog + module Core + module Transport + module HTTP + class Env < Hash[untyped, untyped] + attr_reader request: untyped + + def initialize: (untyped request, ?untyped? options) -> void + + def verb: () -> untyped + + def verb=: (untyped value) -> untyped + + def path: () -> untyped + + def path=: (untyped value) -> untyped + + def body: () -> untyped + + def body=: (untyped value) -> untyped + + def headers: () -> untyped + + def headers=: (untyped value) -> untyped + + def form: () -> untyped + + def form=: (untyped value) -> untyped + end + end + end + end +end diff --git a/vendor/rbs/ddtrace/0/datadog/core/transport/request.rbs b/vendor/rbs/ddtrace/0/datadog/core/transport/request.rbs new file mode 100644 index 00000000..b1043a55 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/transport/request.rbs @@ -0,0 +1,11 @@ +module Datadog + module Core + module Transport + class Request + attr_reader parcel: untyped + + def initialize: (?untyped? parcel) -> void + end + end + end +end diff --git a/vendor/rbs/ddtrace/0/datadog/core/transport/response.rbs b/vendor/rbs/ddtrace/0/datadog/core/transport/response.rbs new file mode 100644 index 00000000..888e6756 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/transport/response.rbs @@ -0,0 +1,35 @@ +module Datadog + module Core + module Transport + module Response + def payload: () -> nil + + def ok?: () -> nil + + def unsupported?: () -> nil + + def not_found?: () -> nil + + def client_error?: () -> nil + + def server_error?: () -> nil + + def internal_error?: () -> nil + + def inspect: () -> ::String + end + + class InternalErrorResponse + include Response + + attr_reader error: untyped + + def initialize: (untyped error) -> void + + def internal_error?: () -> true + + def inspect: () -> ::String + end + end + end +end