diff --git a/app/controllers/nonprofits/tag_masters_controller.rb b/app/controllers/nonprofits/tag_masters_controller.rb index ecc46943..35e68b8e 100644 --- a/app/controllers/nonprofits/tag_masters_controller.rb +++ b/app/controllers/nonprofits/tag_masters_controller.rb @@ -5,7 +5,7 @@ module Nonprofits class TagMastersController < ApplicationController include Controllers::Nonprofit::Current - include Controllers::Nonprofit::Authorization + include Controllers::Nonprofit::Authorization before_action :authenticate_nonprofit_user! def index @@ -25,7 +25,7 @@ module Nonprofits def destroy tag_master = current_nonprofit.tag_masters.find(params[:id]) - tag_master.update_attribute(:deleted, true) + tag_master.discard! tag_master.tag_joins.destroy_all render json: {}, status: :ok end diff --git a/app/models/tag_join.rb b/app/models/tag_join.rb index 79138bb9..ddd98bca 100644 --- a/app/models/tag_join.rb +++ b/app/models/tag_join.rb @@ -13,7 +13,7 @@ class TagJoin < ApplicationRecord def name tag_master.name -end + end def self.create_with_name(nonprofit, h) tm = nonprofit.tag_masters.find_by_name(h['name']) diff --git a/app/models/tag_master.rb b/app/models/tag_master.rb index 63a76af7..d83c9203 100644 --- a/app/models/tag_master.rb +++ b/app/models/tag_master.rb @@ -3,6 +3,12 @@ # License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later # Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE class TagMaster < ApplicationRecord + + # TODO replace with Discard gem + define_model_callbacks :discard + + after_discard :publish_delete + # :nonprofit, :nonprofit_id, # :name, # :deleted, @@ -11,6 +17,7 @@ class TagMaster < ApplicationRecord validates :name, presence: true validate :no_dupes, on: :create + after_create :publish_create belongs_to :nonprofit has_many :tag_joins, dependent: :destroy has_one :email_list @@ -22,4 +29,50 @@ class TagMaster < ApplicationRecord errors.add(:base, 'Duplicate tag') if nonprofit.tag_masters.not_deleted.where(name: name).any? end + + + # TODO replace with discard gem + def discard! + run_callbacks(:discard) do + self.deleted = true + save! + end + end + + def to_builder(*expand) + Jbuilder.new do |tag| + tag.(self, :id, :name, :deleted) + tag.object 'tag_master' + if expand.include? :nonprofit && nonprofit + tag.nonprofit do + tag.id nonprofit.id + tag.name nonprofit.name + end + else + tag.nonprofit nonprofit && nonprofit.id + end + end + end + + +private + def publish_create + Houdini.event_publisher.announce(:tag_master_created, to_event('tag_master.created', :nonprofit).attributes!) + end + + def publish_delete + Houdini.event_publisher.announce(:tag_master_deleted, to_event('tag_master.deleted', :nonprofit).attributes!) + end + + + def to_event(event_type, *expand) + Jbuilder.new do |event| + event.id SecureRandom.uuid + event.object 'event' + event.type event_type + event.data do + event.object to_builder(*expand) + end + end + end end diff --git a/docs/event_definitions/Nonprofit/TagMaster.ts b/docs/event_definitions/Nonprofit/TagMaster.ts new file mode 100644 index 00000000..66ffc33e --- /dev/null +++ b/docs/event_definitions/Nonprofit/TagMaster.ts @@ -0,0 +1,20 @@ +// License: LGPL-3.0-or-later + +import type { HoudiniEvent, HoudiniObject, IdType } from "../common"; +import type Nonprofit from './'; + +export interface TagMaster extends HoudiniObject { + deleted: boolean; + name: string; + nonprofit: IdType | Nonprofit; + object: 'tag_master'; +} + +/** POST /nonprofits/:id/tag_masters */ +export interface CreateTagMaster { + name: string; +} + +export type TagMasterCreated = HoudiniEvent<'tag_master.created', TagMaster>; + +export type TagMasterDeleted = HoudiniEvent<'tag_master.deleted', TagMaster>; \ No newline at end of file diff --git a/docs/event_definitions/Nonprofit/index.ts b/docs/event_definitions/Nonprofit/index.ts new file mode 100644 index 00000000..387e6f96 --- /dev/null +++ b/docs/event_definitions/Nonprofit/index.ts @@ -0,0 +1,10 @@ +// License: LGPL-3.0-or-later +import type { HoudiniObject } from '../common'; + +/** + * A single nonprofit organization on Houdini. + */ +export default interface Nonprofit extends HoudiniObject { + name: string; + object: "nonprofit"; +} diff --git a/docs/event_definitions/common.ts b/docs/event_definitions/common.ts new file mode 100644 index 00000000..51808402 --- /dev/null +++ b/docs/event_definitions/common.ts @@ -0,0 +1,48 @@ +// License: LGPL-3.0-or-later + +/** + * the main identifier for HoudiniObjects which is unique between all other HoudiniObjects with the same object value. + * Currently just an integer but we could reevaluate later; + */ +export type IdType = number; + +/** + * Every object controlled by the Houdini event publisher must meet this standard interface + * and will inherit from it. + */ +export interface HoudiniObject { + /** + * An IdType which unique which uniquely identifies this object + * from all other similar objects + */ + id: IdType; + /** + * the type of object. Roughly corresponds to the object's class in Rails + */ + object: string; +} + +/** + * An event published by Houdini + * + * Generics: + * * EventType a snake-cased string of the format: ".". As an example + * tag_master.created means the event fired by when a tag_master was created + * * DataObject: the interface representing the actual object which the event occurred on. An object of that type is + * on the 'data' attribute + */ +export interface HoudiniEvent { + /** data for the event. We wrap the object inside becuase we might want to provide some sort of */ + data: { + /** the object after the event has occurred */ + object: DataObject; + }; + /** + * A UUID uniquely representing the event + */ + id: string; + object: 'event'; + /** The type of event that this is */ + type: EventType; + +} \ No newline at end of file diff --git a/spec/models/tag_master_spec.rb b/spec/models/tag_master_spec.rb new file mode 100644 index 00000000..b3afe628 --- /dev/null +++ b/spec/models/tag_master_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later +# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE +require 'rails_helper' + +RSpec.describe TagMaster, type: :model do + include_context :shared_donation_charge_context + let(:name) { "TAGNAME"} + + let(:tag_master) { nonprofit.tag_masters.create(name: name) } + it 'creates' do + expect(tag_master.errors).to be_empty + end + + it 'announces create' do + expect(Houdini.event_publisher).to receive(:announce).with(:tag_master_created, { + 'id' => kind_of(String), + 'object' => 'event', + 'type' => 'tag_master.created', + 'data' => { + 'object' => { + 'id'=> kind_of(Numeric), + 'deleted' => false, + 'name' => name, + 'nonprofit'=> nonprofit.id, + 'object' => 'tag_master' + } + } + }) + + tag_master + end + + it 'announces deleted' do + expect(Houdini.event_publisher).to receive(:announce).with(:tag_master_created, anything).ordered + expect(Houdini.event_publisher).to receive(:announce).with(:tag_master_deleted, { + 'id' => kind_of(String), + 'object' => 'event', + 'type' => 'tag_master.deleted', + 'data' => { + 'object' => { + 'id'=> kind_of(Numeric), + 'deleted' => true, + 'name' => name, + 'nonprofit'=> nonprofit.id, + 'object' => 'tag_master' + } + } + }).ordered + + tag_master.discard! + + end +end