From ff3f84e2a620346e95a52a3cab2f62bd47946d02 Mon Sep 17 00:00:00 2001 From: Sauparna Gupta Date: Wed, 3 Jul 2024 12:32:12 +0530 Subject: [PATCH] feat: add new label api (#703) * chore: wip/adding a new labels resource to list all labels * chore: wip/added pagination to labels response and refactored the response * chore: missed a file * chore: updated the spec * chore: updated the decorator and wrote a spec for repository * chore: added labels decorator spec * fix: rubocop failure * fix: fixed the failing test * fix: fixed the wrong syntax * test: fixed another failing test * chore: added test for service layer of Label service * chore: added spacing * chore: added spec for paginated response --- lib/pact_broker/api.rb | 3 ++ .../api/decorators/label_decorator.rb | 32 ++++++++------ .../api/decorators/labels_decorator.rb | 23 ++++++++++ lib/pact_broker/api/resources/labels.rb | 37 ++++++++++++++++ lib/pact_broker/labels/repository.rb | 5 +++ lib/pact_broker/labels/service.rb | 4 ++ spec/features/get_labels_spec.rb | 36 ++++++++++++++++ .../api/decorators/labels_decorator_spec.rb | 43 +++++++++++++++++++ .../lib/pact_broker/labels/repository_spec.rb | 35 +++++++++++++++ spec/lib/pact_broker/labels/service_spec.rb | 12 +++++- spec/support/all_routes_spec_support.yml | 1 + 11 files changed, 217 insertions(+), 14 deletions(-) create mode 100644 lib/pact_broker/api/decorators/labels_decorator.rb create mode 100644 lib/pact_broker/api/resources/labels.rb create mode 100644 spec/features/get_labels_spec.rb create mode 100644 spec/lib/pact_broker/api/decorators/labels_decorator_spec.rb diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index 70c1346af..26a674a00 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -85,6 +85,9 @@ def self.build_api(application_context = PactBroker::ApplicationContext.default_ add ["pacticipants", :pacticipant_name], Api::Resources::Pacticipant, {resource_name: "pacticipant"} add ["pacticipants", :pacticipant_name, "labels", :label_name], Api::Resources::Label, {resource_name: "pacticipant_label"} + # Labels + add ["labels"], Api::Resources::Labels, {resource_name: "labels"} + # Versions add ["pacticipants", :pacticipant_name, "versions"], Api::Resources::Versions, {resource_name: "pacticipant_versions"} add ["pacticipants", :pacticipant_name, "branches", :branch_name, "versions"], Api::Resources::BranchVersions, {resource_name: "pacticipant_branch_versions"} diff --git a/lib/pact_broker/api/decorators/label_decorator.rb b/lib/pact_broker/api/decorators/label_decorator.rb index 009df5d1c..098c0bcc1 100644 --- a/lib/pact_broker/api/decorators/label_decorator.rb +++ b/lib/pact_broker/api/decorators/label_decorator.rb @@ -11,20 +11,26 @@ class LabelDecorator < BaseDecorator include Timestamps - link :self do | options | - { - title: "Label", - name: represented.name, - href: label_url(represented, options[:base_url]) - } - end + # This method is overridden to conditionally render the links based on the user_options + def to_hash(options) + hash = super + + unless options.dig(:user_options, :hide_label_decorator_links) + hash[:_links] = { + self: { + title: "Label", + name: represented.name, + href: label_url(represented, options.dig(:user_options, :base_url)) + }, + pacticipant: { + title: "Pacticipant", + name: represented.pacticipant.name, + href: pacticipant_url(options.dig(:user_options, :base_url), represented.pacticipant) + } + } + end - link :pacticipant do | options | - { - title: "Pacticipant", - name: represented.pacticipant.name, - href: pacticipant_url(options.fetch(:base_url), represented.pacticipant) - } + hash end end end diff --git a/lib/pact_broker/api/decorators/labels_decorator.rb b/lib/pact_broker/api/decorators/labels_decorator.rb new file mode 100644 index 000000000..b668f1c92 --- /dev/null +++ b/lib/pact_broker/api/decorators/labels_decorator.rb @@ -0,0 +1,23 @@ +require_relative "base_decorator" +require_relative "pagination_links" +require_relative "label_decorator" +require "pact_broker/domain/label" + +module PactBroker + module Api + module Decorators + class LabelsDecorator < BaseDecorator + collection :entries, :as => :labels, :class => PactBroker::Domain::Label, :extend => PactBroker::Api::Decorators::LabelDecorator, embedded: true + + include PaginationLinks + + link :self do | options | + { + title: "Labels", + href: options.fetch(:resource_url) + } + end + end + end + end +end diff --git a/lib/pact_broker/api/resources/labels.rb b/lib/pact_broker/api/resources/labels.rb new file mode 100644 index 000000000..d6620969a --- /dev/null +++ b/lib/pact_broker/api/resources/labels.rb @@ -0,0 +1,37 @@ +require "pact_broker/api/resources/base_resource" +require "pact_broker/api/decorators/labels_decorator" +require "pact_broker/api/resources/pagination_methods" + +module PactBroker + module Api + module Resources + class Labels < BaseResource + include PaginationMethods + + def content_types_provided + [["application/hal+json", :to_json]] + end + + def allowed_methods + ["GET", "OPTIONS"] + end + + def policy_name + :'labels::labels' + end + + def to_json + decorator_class(:labels_decorator).new(labels).to_json( + **decorator_options( + hide_label_decorator_links: true, + ) + ) + end + + def labels + label_service.get_all_unique_labels(pagination_options) + end + end + end + end +end diff --git a/lib/pact_broker/labels/repository.rb b/lib/pact_broker/labels/repository.rb index 7efe0df3f..aee665261 100644 --- a/lib/pact_broker/labels/repository.rb +++ b/lib/pact_broker/labels/repository.rb @@ -3,6 +3,11 @@ module PactBroker module Labels class Repository + + def get_all_unique_labels pagination_options = {} + PactBroker::Domain::Label.distinct.select(:name).all_with_pagination_options(pagination_options) + end + def create args Domain::Label.new(name: args.fetch(:name), pacticipant: args.fetch(:pacticipant)).save end diff --git a/lib/pact_broker/labels/service.rb b/lib/pact_broker/labels/service.rb index 02ae239fa..af9bc5e6f 100644 --- a/lib/pact_broker/labels/service.rb +++ b/lib/pact_broker/labels/service.rb @@ -9,6 +9,10 @@ module Service extend PactBroker::Repositories + def get_all_unique_labels pagination_options = {} + label_repository.get_all_unique_labels(pagination_options) + end + def create args pacticipant = pacticipant_repository.find_by_name_or_create args.fetch(:pacticipant_name) label_repository.create pacticipant: pacticipant, name: args.fetch(:label_name) diff --git a/spec/features/get_labels_spec.rb b/spec/features/get_labels_spec.rb new file mode 100644 index 000000000..a63bca7b4 --- /dev/null +++ b/spec/features/get_labels_spec.rb @@ -0,0 +1,36 @@ +describe "Get labels" do + + let(:path) { "/labels" } + let(:response_body_hash) { JSON.parse(subject.body, symbolize_names: true) } + + subject { get(path) } + + context "when labels exists" do + before do + td.create_pacticipant("foo") + .create_label("ios") + .create_label("consumer") + .create_pacticipant("bar") + .create_label("ios") + .create_label("consumer") + end + + it "returns a 200 OK" do + expect(subject).to be_a_hal_json_success_response + end + + it "returns the labels in the body" do + expect(response_body_hash[:_embedded][:labels].map { |label| label[:name] }).to contain_exactly("ios", "consumer") + end + + context "with pagination options" do + subject { get(path, { "size" => "1", "page" => "1" }) } + + it "only returns the number of items specified in the page" do + expect(response_body_hash[:_embedded][:labels].size).to eq 1 + end + + it_behaves_like "a paginated response" + end + end +end diff --git a/spec/lib/pact_broker/api/decorators/labels_decorator_spec.rb b/spec/lib/pact_broker/api/decorators/labels_decorator_spec.rb new file mode 100644 index 000000000..f61302e6b --- /dev/null +++ b/spec/lib/pact_broker/api/decorators/labels_decorator_spec.rb @@ -0,0 +1,43 @@ +require "pact_broker/api/decorators/labels_decorator" +require "pact_broker/labels/service" + +module PactBroker + module Api + module Decorators + + describe LabelsDecorator do + before do + td.create_consumer("Foo") + .create_label("consumer") + .create_consumer("Bar") + .create_label("provider") + .create_consumer("Wiffle") + .create_label("provider") + end + + let(:label) do + PactBroker::Labels::Service.get_all_unique_labels + end + + let(:options) { { user_options: { resource_url: "http://example.org/labels", hide_label_decorator_links: true } } } + subject { JSON.parse LabelsDecorator.new(label).to_json(options), symbolize_names: true } + + it "includes the label names" do + expect(subject[:_embedded][:labels].map { |label| label[:name] }).to contain_exactly("provider", "consumer") + end + + it "includes the resource url" do + expect(subject[:_links][:self][:href]).to eq "http://example.org/labels" + end + + it "labels field doest not include any links" do + expect(subject[:_embedded][:labels][0][:_links]).to be_nil + end + + it "doest not include createdAt" do + expect(subject[:_embedded][:labels][0][:createdAt]).to be_nil + end + end + end + end +end diff --git a/spec/lib/pact_broker/labels/repository_spec.rb b/spec/lib/pact_broker/labels/repository_spec.rb index b3e8824e8..41a29d006 100644 --- a/spec/lib/pact_broker/labels/repository_spec.rb +++ b/spec/lib/pact_broker/labels/repository_spec.rb @@ -4,6 +4,41 @@ module PactBroker module Labels describe Repository do + describe ".get_all_unique_labels" do + before do + td.create_pacticipant("bar") + .create_label("ios") + .create_pacticipant("foo") + .create_label("android") + .create_pacticipant("wiffle") + .create_label("ios") + end + + let(:labels_repository) { Repository.new } + + context "when there are no pagination options" do + subject { labels_repository.get_all_unique_labels } + + it "returns all the unique labels" do + expect(subject.collect(&:name)).to contain_exactly("ios", "android") + end + end + + context "when there are pagination options" do + let(:pagination_options) do + { + :page_number => 1, + :page_size => 1 + } + end + subject { labels_repository.get_all_unique_labels pagination_options } + + it "returns paginated unique labels" do + expect(subject.collect(&:name)).to contain_exactly("ios") + end + end + end + describe ".find" do let(:pacticipant_name) { "foo" } diff --git a/spec/lib/pact_broker/labels/service_spec.rb b/spec/lib/pact_broker/labels/service_spec.rb index 39e242a3c..dd8f45970 100644 --- a/spec/lib/pact_broker/labels/service_spec.rb +++ b/spec/lib/pact_broker/labels/service_spec.rb @@ -5,7 +5,17 @@ module Labels describe Service do let(:pacticipant_name) { "foo" } let(:label_name) { "ios" } - let(:options) { {pacticipant_name: pacticipant_name, label_name: label_name}} + let(:options) { { pacticipant_name: pacticipant_name, label_name: label_name } } + let(:pagination_options) { { page_number: 1, page_size: 1 } } + + describe ".get_all_unique_labels" do + subject { Service.get_all_unique_labels(pagination_options) } + + it "calls the labels repository" do + expect_any_instance_of(Labels::Repository).to receive(:get_all_unique_labels).with(pagination_options) + subject + end + end describe ".create" do subject { Service.create(options) } diff --git a/spec/support/all_routes_spec_support.yml b/spec/support/all_routes_spec_support.yml index cbb370047..03e748698 100644 --- a/spec/support/all_routes_spec_support.yml +++ b/spec/support/all_routes_spec_support.yml @@ -9,6 +9,7 @@ requests_which_are_exected_to_have_no_policy_record_or_pacticipant: - environment DELETE - integrations GET - integrations DELETE + - labels GET - matrix GET - metrics GET - pacticipants GET