From 5fa9b95d729ac31652a45b5cc5142616faedf051 Mon Sep 17 00:00:00 2001 From: hsong-rh Date: Thu, 29 Apr 2021 16:03:24 -0400 Subject: [PATCH] Change to support object id with different patterns --- .../request_path.rb | 8 +- .../controllers/api/v2/tasks_controller.rb | 11 ++ spec/dummy/app/controllers/api/v2x0.rb | 1 + spec/dummy/app/models/source.rb | 1 + spec/dummy/app/models/task.rb | 4 + spec/dummy/config/routes.rb | 5 +- ...0190922115700_enable_pgcrypto_extension.rb | 6 + .../db/migrate/20210429134638_create_tasks.rb | 12 ++ spec/dummy/public/doc/openapi-3-v2.0.0.json | 172 ++++++++++++++++++ spec/requests/request_path_spec.rb | 20 ++ 10 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 spec/dummy/app/controllers/api/v2/tasks_controller.rb create mode 100644 spec/dummy/app/models/task.rb create mode 100644 spec/dummy/db/migrate/20190922115700_enable_pgcrypto_extension.rb create mode 100644 spec/dummy/db/migrate/20210429134638_create_tasks.rb diff --git a/lib/insights/api/common/application_controller_mixins/request_path.rb b/lib/insights/api/common/application_controller_mixins/request_path.rb index 36eecd6d..f6ef540a 100644 --- a/lib/insights/api/common/application_controller_mixins/request_path.rb +++ b/lib/insights/api/common/application_controller_mixins/request_path.rb @@ -44,10 +44,14 @@ module ClassMethods private def id_regexp(primary_collection_name) - @id_regexp ||= begin + @id_regexp_table ||= {} + + if @id_regexp_table.empty? || @id_regexp_table[primary_collection_name].nil? id_parameter = id_parameter_from_api_doc(primary_collection_name) - id_parameter ? id_parameter.fetch_path("schema", "pattern") : /^\d+$/ + @id_regexp_table[primary_collection_name] = id_parameter ? id_parameter.fetch_path("schema", "pattern") : /^\d+$/ end + + @id_regexp_table[primary_collection_name] end def id_parameter_from_api_doc(primary_collection_name) diff --git a/spec/dummy/app/controllers/api/v2/tasks_controller.rb b/spec/dummy/app/controllers/api/v2/tasks_controller.rb new file mode 100644 index 00000000..b6eebc8f --- /dev/null +++ b/spec/dummy/app/controllers/api/v2/tasks_controller.rb @@ -0,0 +1,11 @@ +module Api + module V2 + class TasksController < ApplicationController + include Api::V2::Mixins::IndexMixin + + def show + render :json => Task.find(params[:id]).to_json + end + end + end +end diff --git a/spec/dummy/app/controllers/api/v2x0.rb b/spec/dummy/app/controllers/api/v2x0.rb index 8ce43cd0..a4d9d6f9 100644 --- a/spec/dummy/app/controllers/api/v2x0.rb +++ b/spec/dummy/app/controllers/api/v2x0.rb @@ -16,5 +16,6 @@ class ErrorsController < Api::V1x0::ErrorsController; end class GraphqlController < Api::V2::GraphqlController; end class SourcesController < Api::V2::SourcesController; end class SourceTypesController < Api::V2::SourceTypesController; end + class TasksController < Api::V2::TasksController; end end end diff --git a/spec/dummy/app/models/source.rb b/spec/dummy/app/models/source.rb index 4c733b9d..3df3b4f1 100644 --- a/spec/dummy/app/models/source.rb +++ b/spec/dummy/app/models/source.rb @@ -5,6 +5,7 @@ class Source < ApplicationRecord has_many :applications has_many :application_types, :through => :applications has_many :endpoints, :autosave => true + has_many :tasks belongs_to :source_type diff --git a/spec/dummy/app/models/task.rb b/spec/dummy/app/models/task.rb new file mode 100644 index 00000000..04202d5f --- /dev/null +++ b/spec/dummy/app/models/task.rb @@ -0,0 +1,4 @@ +class Task < ApplicationRecord + include TenancyConcern + belongs_to :source +end diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index 002a6327..0464cc7b 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -21,8 +21,11 @@ resources :authentications, :only => [:create, :update] resources :vms, :only => [:index, :show] resources :persons, :only => [:index, :create, :show, :update] - resources :sources, :only => [:index] + resources :sources, :only => [:index] do + resources :tasks, :only => [:index] + end resources :source_types, :only => [:index] + resources :tasks, :only => [:index, :show] resources :extras, :only => [:index] end diff --git a/spec/dummy/db/migrate/20190922115700_enable_pgcrypto_extension.rb b/spec/dummy/db/migrate/20190922115700_enable_pgcrypto_extension.rb new file mode 100644 index 00000000..2400b0cf --- /dev/null +++ b/spec/dummy/db/migrate/20190922115700_enable_pgcrypto_extension.rb @@ -0,0 +1,6 @@ +class EnablePgcryptoExtension < ActiveRecord::Migration[5.2] + def change + enable_extension 'uuid-ossp' + enable_extension 'pgcrypto' + end +end diff --git a/spec/dummy/db/migrate/20210429134638_create_tasks.rb b/spec/dummy/db/migrate/20210429134638_create_tasks.rb new file mode 100644 index 00000000..859e55c7 --- /dev/null +++ b/spec/dummy/db/migrate/20210429134638_create_tasks.rb @@ -0,0 +1,12 @@ +class CreateTasks < ActiveRecord::Migration[5.2] + def change + create_table :tasks, :id => :uuid do |t| + t.references :tenant, :index => true, :null => false + t.references :source, :index => true, :null => true, :foreign_key => {:on_delete => :nullify} + t.string :name + t.string :status + t.string :state + t.string :message + end + end +end diff --git a/spec/dummy/public/doc/openapi-3-v2.0.0.json b/spec/dummy/public/doc/openapi-3-v2.0.0.json index 5c15acf1..0e5e459a 100644 --- a/spec/dummy/public/doc/openapi-3-v2.0.0.json +++ b/spec/dummy/public/doc/openapi-3-v2.0.0.json @@ -669,6 +669,105 @@ } } }, + "/sources/{id}/tasks": { + "get": { + "summary": "List Tasks for Source", + "operationId": "listSourceTasks", + "description": "Returns an array of Task objects", + "parameters": [ + { + "$ref": "#/components/parameters/QueryLimit" + }, + { + "$ref": "#/components/parameters/QueryOffset" + }, + { + "$ref": "#/components/parameters/QueryFilter" + }, + { + "$ref": "#/components/parameters/QuerySortBy" + }, + { + "$ref": "#/components/parameters/ID" + } + ], + "responses": { + "200": { + "description": "Tasks collection", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TasksCollection" + } + } + } + } + } + } + }, + "/tasks": { + "get": { + "summary": "List Tasks", + "operationId": "listTasks", + "description": "Returns an array of Task objects", + "parameters": [ + { + "$ref": "#/components/parameters/QueryLimit" + }, + { + "$ref": "#/components/parameters/QueryOffset" + }, + { + "$ref": "#/components/parameters/QueryFilter" + }, + { + "$ref": "#/components/parameters/QuerySortBy" + }, + { + "$ref": "#/components/parameters/ID" + } + ], + "responses": { + "200": { + "description": "Tasks collection", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TasksCollection" + } + } + } + } + } + } + }, + "/tasks/{id}": { + "get": { + "summary": "Show an existing Task", + "operationId": "showTask", + "description": "Returns a Task object", + "parameters": [ + { + "$ref": "#/components/parameters/UUID" + } + ], + "responses": { + "200": { + "description": "Task info", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Task" + } + } + } + }, + "404": { + "description": "Not found" + } + } + } + }, "/persons": { "get": { "summary": "List Person Objects", @@ -913,6 +1012,16 @@ "type": "string", "pattern": "^\\d+$" } + }, + "UUID": { + "name": "id", + "in": "path", + "description": "UUID of task", + "required": true, + "schema": { + "type": "string", + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + } } }, "securitySchemes": { @@ -1197,6 +1306,63 @@ } } }, + "Task": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "example": "OpenShift default", + "type": "string" + }, + "status": { + "example": "valid", + "type": "string" + }, + "state": { + "example": "complete", + "type": "string" + }, + "message": { + "example": "Ready to go", + "type": "string" + }, + "source_id": { + "$ref": "#/components/schemas/ID" + }, + "tenant": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "readOnly": true, + "type": "string" + }, + "updated_at": { + "format": "date-time", + "readOnly": true, + "type": "string" + } + } + }, + "TasksCollection": { + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/CollectionMetadata" + }, + "links": { + "$ref": "#/components/schemas/CollectionLinks" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Task" + } + } + } + }, "GraphQLRequest": { "type": "object", "properties": { @@ -1242,6 +1408,12 @@ "pattern": "^\\d+$", "readOnly": true }, + "UUID": { + "type": "string", + "description": "UUID of the resource", + "format": "uuid", + "readOnly": true + }, "OrderParameters": { "type": "object", "properties": { diff --git a/spec/requests/request_path_spec.rb b/spec/requests/request_path_spec.rb index d22c7843..f83caebc 100644 --- a/spec/requests/request_path_spec.rb +++ b/spec/requests/request_path_spec.rb @@ -30,4 +30,24 @@ expect(response.status).to eq(400) expect(response.parsed_body).to eq("errors" => [{"detail" => "Insights::API::Common::ApplicationControllerMixins::RequestPath::RequestPathError: ID is invalid", "status" => "400"}]) end + + context "when object id pattern is different" do + let(:external_tenant) { rand(1000).to_s } + let(:tenant) { Tenant.create!(:name => "default", :external_tenant => external_tenant) } + let(:source_type) { SourceType.create(:name => "AnsibleTower", :product_name => "RedHat AnsibleTower", :vendor => "redhat") } + + before do + @source = Source.create!(:tenant => tenant, :name => "source_s1", :source_type => source_type) + @task = Task.create!(:tenant => tenant, :source => @source, :name => "Task_t1", :state => "pending") + end + + it "valid ID" do + get "/api/v2.0/tasks/#{@task.id}" + get "/api/v2.0/sources/#{@source.id}/tasks" + + expect(response.status).to eq(200) + expect(response.parsed_body["data"].count).to eq(1) + expect(response.parsed_body["data"].first["id"]).to eq(@task.id) + end + end end