Commit deb841e4 authored by Bob Van Landuyt's avatar Bob Van Landuyt 💬
Browse files

Merge branch 'add-metadata-to-headers' into 'master'

Add metadata to headers in Rack middleware

Closes #18

See merge request !109
parents aee55f60 dba7776c
Loading
Loading
Loading
Loading
Loading
+0 −11
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ module Labkit
    LOG_KEY = "meta"
    CORRELATION_ID_KEY = "correlation_id"
    RAW_KEYS = [CORRELATION_ID_KEY].freeze
    HEADER_PREFIX = "X-Gitlab-"

    class << self
      def with_context(attributes = {})
@@ -71,10 +70,6 @@ module Labkit
        @known_log_keys ||= (KNOWN_KEYS.map(&method(:log_key)) + RAW_KEYS).freeze
      end

      def header_name(name)
        HEADER_PREFIX + log_key(name).titlecase(keep_id_suffix: true).gsub(/\W/, "-")
      end

      private

      def contexts
@@ -103,12 +98,6 @@ module Labkit
      data[CORRELATION_ID_KEY]
    end

    def to_headers
      to_h.except(CORRELATION_ID_KEY).transform_keys do |key|
        self.class.header_name(key)
      end
    end

    def get_attribute(attribute)
      raw = call_or_value(data[log_key(attribute)])

+17 −2
Original line number Diff line number Diff line
# frozen_string_literal: true

require "action_dispatch"
require "json"

module Labkit
  module Middleware
    HEADER = "X-Gitlab-Meta"

    # This is a rack middleware to be inserted in GitLab-rails
    # It makes sure that there's always a root context containing the correlation
    # id.
@@ -15,8 +18,12 @@ module Labkit
      end

      def call(env)
        Labkit::Context.with_context(Labkit::Context::CORRELATION_ID_KEY => correlation_id(env)) do
          @app.call(env)
        Labkit::Context.with_context(Labkit::Context::CORRELATION_ID_KEY => correlation_id(env)) do |context|
          status, headers, response = @app.call(env)

          headers[HEADER] = context_to_json(context)

          [status, headers, response]
        end
      end

@@ -29,6 +36,14 @@ module Labkit
      def request(env)
        ActionDispatch::Request.new(env)
      end

      def context_to_json(context)
        context
          .to_h
          .transform_keys { |k| k.delete_prefix("meta.") }
          .merge("version" => "1")
          .to_json
      end
    end
  end
end
+0 −47
Original line number Diff line number Diff line
@@ -107,25 +107,6 @@ describe Labkit::Context do
    end
  end

  describe ".header_name" do
    using RSpec::Parameterized::TableSyntax

    where(:value, :result) do
      :caller_id | "X-Gitlab-Meta-Caller-Id"
      :root_namespace | "X-Gitlab-Meta-Root-Namespace"
      :user | "X-Gitlab-Meta-User"
      :unknown | "X-Gitlab-Meta-Unknown"
      "meta.user" | "X-Gitlab-Meta-User"
      described_class::CORRELATION_ID_KEY | "X-Gitlab-Correlation-Id"
    end

    with_them do
      it do
        expect(described_class.header_name(value)).to eq(result)
      end
    end
  end

  describe "#to_h" do
    let(:expected_hash) do
      log_hash(user: "user",
@@ -181,34 +162,6 @@ describe Labkit::Context do
    end
  end

  describe "#to_headers" do
    it "returns a hash of header names to values, excluding correlation ID" do
      context = described_class.new(
        user: -> { "user" },
        root_namespace: -> { "namespace" },
        project: -> { "project" },
        caller_id: "ProjectsController#show",
        random_thing: "Random",
      )

      expect(context.to_headers).to include("X-Gitlab-Meta-User" => "user",
                                            "X-Gitlab-Meta-Root-Namespace" => "namespace",
                                            "X-Gitlab-Meta-Project" => "project",
                                            "X-Gitlab-Meta-Caller-Id" => "ProjectsController#show",
                                            "X-Gitlab-Meta-Random-Thing" => "Random")
    end

    it "does not include empty values" do
      context = described_class.new(
        user: -> { },
        root_namespace: nil,
        project: "",
      )

      expect(context.to_headers).to be_empty
    end
  end

  describe "#initialize" do
    it "assigns all keys as strings" do
      context = described_class.new(
+21 −6
Original line number Diff line number Diff line
@@ -2,23 +2,38 @@

describe Labkit::Middleware::Rack do
  let(:app) { double("app") }
  let(:correlation_id) { "the id" }
  let(:metadata) { { "feature_category" => "issue_tracking" } }
  let(:header_metadata) { metadata.merge({ "version" => "1", Labkit::Context::CORRELATION_ID_KEY => correlation_id }) }
  let(:env) { {} }
  let(:fake_request) { double("request") }

  before do
    allow(ActionDispatch::Request).to receive(:new).with(env).and_return(fake_request)
    allow(fake_request).to receive(:request_id).and_return(correlation_id)
  end

  describe "#call" do
    it "adds the correlation id from the request to the context" do
      fake_request = double("request")

      expect(ActionDispatch::Request).to receive(:new).with(env).and_return(fake_request)
      expect(fake_request).to receive(:request_id).and_return("the id")
      expect(Labkit::Context).to receive(:with_context).with(a_hash_including(Labkit::Context::CORRELATION_ID_KEY => "the id"))
      expect(Labkit::Context).to receive(:with_context).with(a_hash_including(Labkit::Context::CORRELATION_ID_KEY => correlation_id))

      described_class.new(app).call(env)
    end

    it "calls the app" do
      expect(app).to receive(:call).with(env)
      expect(app).to receive(:call).with(env).and_return([nil, {}, nil])

      described_class.new(app).call(env)
    end

    it "injects meta headers" do
      Labkit::Context.push(metadata)

      expect(app).to receive(:call).with(env).and_return([nil, {}, nil])

      _, headers, _ = described_class.new(app).call(env)

      expect(JSON.parse(headers["X-Gitlab-Meta"])).to eq(header_metadata)
    end
  end
end