Skip to content

Commit

Permalink
Merge pull request #3857 from 3scale/THREESCALE-1789-annotations
Browse files Browse the repository at this point in the history
THREESCALE-1786: Managed by operator
  • Loading branch information
jlledom authored Aug 30, 2024
2 parents 9afab5e + edf0378 commit dade692
Show file tree
Hide file tree
Showing 28 changed files with 621 additions and 13 deletions.
2 changes: 1 addition & 1 deletion app/controllers/admin/api/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Admin::Api::AccountsController < Admin::Api::BaseController
# Account List
# GET /admin/api/accounts.xml
def index
accounts = buyer_accounts.includes(:users, :settings, :payment_detail, :country, bought_plans: [:original]) # :issuer is polymorphic
accounts = buyer_accounts.includes(:users, :settings, :payment_detail, :country, :annotations, bought_plans: [:original]) # :issuer is polymorphic

if state = params[:state].presence
accounts = accounts.where(:state => state.to_s)
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/admin/api/backend_apis_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Admin::Api::BackendApisController < Admin::Api::BaseController
clear_respond_to
respond_to :json

wrap_parameters BackendApi
wrap_parameters BackendApi, include: BackendApi.attribute_names | %w[annotations]
representer BackendApi

paginate only: :index
Expand Down Expand Up @@ -48,7 +48,7 @@ def destroy

private

DEFAULT_PARAMS = %i[name description private_endpoint].freeze
DEFAULT_PARAMS = [:name, :description, :private_endpoint, {annotations: {}}].freeze
private_constant :DEFAULT_PARAMS

def authorize
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/admin/api/services_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class Admin::Api::ServicesController < Admin::Api::ServiceBaseController
wrap_parameters Service, include: Service.attribute_names | %w[state_event]
wrap_parameters Service, include: Service.attribute_names | %w[state_event annotations]
representer Service

before_action :deny_on_premises_for_master
Expand All @@ -12,7 +12,7 @@ class Admin::Api::ServicesController < Admin::Api::ServiceBaseController
# Service List
# GET /admin/api/services.xml
def index
services = accessible_services.includes(:proxy, :account).order(:id).paginate(pagination_params)
services = accessible_services.includes(:proxy, :account, :annotations).order(:id).paginate(pagination_params)
respond_with(services)
end

Expand Down Expand Up @@ -62,7 +62,8 @@ def service_params
:buyer_can_select_plan, :buyer_plan_change_permission, :buyers_manage_keys,
:buyer_key_regenerate_enabled, :mandatory_app_key, :custom_keys_enabled, :state_event,
:txt_support, :terms,
{notification_settings: [web_provider: [], email_provider: [], web_buyer: [], email_buyer: []]}]
{notification_settings: [web_provider: [], email_provider: [], web_buyer: [], email_buyer: []],
annotations: {}}]
params.require(:service).permit(*permitted_params)
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/master/api/providers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def plan_upgrade
end
end

UPDATE_PARAMS = %i[from_email support_email finance_support_email site_access_code state_event].freeze
UPDATE_PARAMS = [:from_email, :support_email, :finance_support_email, :site_access_code, :state_event, {annotations: {}}].freeze
private_constant :UPDATE_PARAMS

private
Expand Down
4 changes: 3 additions & 1 deletion app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class Account < ApplicationRecord

scope :searchable, -> { not_master.without_to_be_deleted.includes(:users, :bought_cinstances) }

annotated
audited

# this is done in a callback because we want to do this AFTER the account is deleted
Expand Down Expand Up @@ -323,7 +324,7 @@ def country=(country_name)
end

def special_fields
[:country]
%i[country annotations]
end

# Returns the id corresponding to an account with given api key. This function avoids
Expand Down Expand Up @@ -483,6 +484,7 @@ def to_xml(options = {})
end

xml.state state
annotations_xml(:builder => xml)
xml.deletion_date deletion_date.xmlschema if scheduled_for_deletion? && deletion_date

if provider?
Expand Down
11 changes: 11 additions & 0 deletions app/models/annotation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class Annotation < ApplicationRecord
SUPPORTED_ANNOTATIONS = %w[managed_by].freeze

belongs_to :annotated, polymorphic: true, optional: false, inverse_of: :annotations

validates :name, presence: true, inclusion: { in: SUPPORTED_ANNOTATIONS }
validates :value, presence: true
validates :name, :value, :annotated_type, length: { maximum: 255 }
end
1 change: 1 addition & 0 deletions app/models/application_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true

include Annotating
include BackgroundDeletion

def self.user_attribute_names
Expand Down
3 changes: 2 additions & 1 deletion app/models/backend_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ class BackendApi < ApplicationRecord
include SystemName
include ProxyConfigAffectingChanges::ModelExtension

audited :allow_mass_assignment => true
annotated
audited

define_proxy_config_affecting_attributes :private_endpoint

Expand Down
26 changes: 26 additions & 0 deletions app/models/concerns/annotating.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module Annotating
extend ActiveSupport::Concern

class_methods do
def annotated
class_eval do
include Model
include ManagedBy
end
end
end

class << self
def models
return @models if @models

# This is to see all models when creating the DB trigger, otherwise the resulting trigger could be incorrect
# https://github.com/3scale/porta/pull/3857#discussion_r1707235658
Rails.autoloaders.main.eager_load_dir("#{Rails.root}/app/models")

@models = ActiveRecord::Base.descendants.select { |model| model.include?(Model) }
end
end
end
15 changes: 15 additions & 0 deletions app/models/concerns/annotating/managed_by.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Annotating
module ManagedBy
extend ActiveSupport::Concern

def managed_by
value_of_annotation("managed_by")
end

def managed_by=(value)
annotate("managed_by", value)
end
end
end
56 changes: 56 additions & 0 deletions app/models/concerns/annotating/model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module Annotating
module Model
extend ActiveSupport::Concern

included do
has_many :annotations, as: :annotated, dependent: :destroy, autosave: true, inverse_of: :annotated
end

def annotations=(hash)
hash.each do |k ,v|
annotate(k, v)
end
end

def annotations_hash
annotations.pluck(:name, :value).to_h
end

def annotations_xml(options = {})
xml = options[:builder] || ThreeScale::XML::Builder.new

xml.annotations do
annotations.each do |annotation|
xml.tag!(annotation.name, annotation.value)
end
end

xml.to_xml
end

def annotation(name)
annotations.find { _1.name == name }
end

def value_of_annotation(name)
annotation(name)&.value
end

def annotate(name, value)
return remove_annotation(name) if value.blank?

existing = annotation(name)
if existing
existing.value = value
else
annotations.build(name: name, value: value)
end
end

def remove_annotation(name)
annotation(name)&.mark_for_destruction
end
end
end
5 changes: 4 additions & 1 deletion app/models/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ def self.all

serialize :notification_settings

audited allow_mass_assignment: true
annotated
audited

state_machine initial: :incomplete do
state :incomplete
state :hidden
Expand Down Expand Up @@ -370,6 +372,7 @@ def to_xml(options = {})
xml.mandatory_app_key mandatory_app_key
xml.buyer_can_select_plan buyer_can_select_plan
xml.buyer_plan_change_permission buyer_plan_change_permission
annotations_xml(:builder => xml)

if notification_settings
xml.notification_settings do |xml|
Expand Down
1 change: 1 addition & 0 deletions app/representers/account_representer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ module AccountRepresenter
end

property :state
property :annotations_hash, as: :annotations

link :self do
admin_api_account_url(self) unless provider?
Expand Down
1 change: 1 addition & 0 deletions app/representers/backend_api_representer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module BackendApiRepresenter
property :system_name
property :description
property :private_endpoint
property :annotations_hash, as: :annotations
property :account_id
property :created_at
property :updated_at
Expand Down
1 change: 1 addition & 0 deletions app/representers/service_representer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module ServiceRepresenter
property :buyer_can_select_plan
property :buyer_plan_change_permission
property :notification_settings
property :annotations_hash, as: :annotations

property :created_at
property :updated_at
Expand Down
33 changes: 33 additions & 0 deletions db/migrate/20240726111800_create_annotation_references.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class CreateAnnotationReferences < ActiveRecord::Migration[6.1]
disable_ddl_transaction! if System::Database.postgres?

def change
options = "CHARSET=utf8mb4 COLLATE=utf8mb4_bin" if System::Database.mysql?

create_table :annotations, options: options do |t|
t.string :name, null: false
t.string :value
t.references :annotated, polymorphic: true, index: false, null: false
t.integer :tenant_id
t.timestamps

t.index %i[annotated_type annotated_id name], unique: true
end

reversible do |direction|
direction.up do
self.class.execute_trigger_action(:recreate)
end
direction.down do
self.class.execute_trigger_action(:drop)
end
end
end

def self.execute_trigger_action(action)
trigger = System::Database.triggers.detect { |trigger| trigger.name == "annotations_tenant_id" }

expressions = [trigger.public_send(action)].flatten
expressions.each(&ActiveRecord::Base.connection.method(:execute))
end
end
13 changes: 12 additions & 1 deletion db/oracle_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_07_19_174715) do
ActiveRecord::Schema.define(version: 2024_07_26_111800) do

create_table "access_tokens", force: :cascade do |t|
t.integer "owner_id", precision: 38, null: false
Expand Down Expand Up @@ -119,6 +119,17 @@
t.index ["timestamp"], name: "index_alerts_on_timestamp"
end

create_table "annotations", force: :cascade do |t|
t.string "name", null: false
t.string "value"
t.string "annotated_type", null: false
t.integer "annotated_id", precision: 38, null: false
t.integer "tenant_id", precision: 38
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["annotated_type", "annotated_id", "name"], name: "index_annotations_on_annotated_type_and_annotated_id_and_name", unique: true
end

create_table "api_docs_services", force: :cascade do |t|
t.integer "account_id", precision: 38
t.integer "tenant_id", precision: 38
Expand Down
13 changes: 12 additions & 1 deletion db/postgres_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_07_19_174715) do
ActiveRecord::Schema.define(version: 2024_07_26_111800) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -122,6 +122,17 @@
t.index ["timestamp"], name: "index_alerts_on_timestamp"
end

create_table "annotations", force: :cascade do |t|
t.string "name", null: false
t.string "value"
t.string "annotated_type", null: false
t.bigint "annotated_id", null: false
t.integer "tenant_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["annotated_type", "annotated_id", "name"], name: "index_annotations_on_annotated_type_and_annotated_id_and_name", unique: true
end

create_table "api_docs_services", force: :cascade do |t|
t.bigint "account_id"
t.bigint "tenant_id"
Expand Down
13 changes: 12 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_07_19_174715) do
ActiveRecord::Schema.define(version: 2024_07_26_111800) do

create_table "access_tokens", charset: "utf8mb3", collation: "utf8mb3_bin", force: :cascade do |t|
t.bigint "owner_id", null: false
Expand Down Expand Up @@ -121,6 +121,17 @@
t.index ["timestamp"], name: "index_alerts_on_timestamp"
end

create_table "annotations", charset: "utf8mb4", collation: "utf8mb4_bin", force: :cascade do |t|
t.string "name", null: false
t.string "value"
t.string "annotated_type", null: false
t.bigint "annotated_id", null: false
t.integer "tenant_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["annotated_type", "annotated_id", "name"], name: "index_annotations_on_annotated_type_and_annotated_id_and_name", unique: true
end

create_table "api_docs_services", charset: "utf8mb3", collation: "utf8mb3_bin", force: :cascade do |t|
t.bigint "account_id"
t.bigint "tenant_id"
Expand Down
14 changes: 14 additions & 0 deletions lib/system/database/definitions/mysql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,20 @@
SQL
end

trigger 'annotations' do
definitions = Annotating.models.map do |model|
[
"NEW.annotated_type = '#{model}'",
"SET NEW.tenant_id = (SELECT tenant_id FROM #{model.table_name} WHERE id = NEW.annotated_id AND tenant_id <> master_id);"
]
end

<<~SQL
IF #{definitions.map{ _1.join(" THEN\n") }.join("\nELSEIF ")}
END IF;
SQL
end

procedure 'sp_invoices_friendly_id', invoice_id: 'bigint' do
<<~SQL
BEGIN
Expand Down
Loading

0 comments on commit dade692

Please sign in to comment.