EventPublisher Support for TagMaster events

This commit is contained in:
Eric Schultz 2021-01-11 12:47:23 -06:00 committed by Eric Schultz
parent 31deb4cce8
commit 5ae4f20637
7 changed files with 189 additions and 3 deletions

View file

@ -25,7 +25,7 @@ module Nonprofits
def destroy def destroy
tag_master = current_nonprofit.tag_masters.find(params[:id]) tag_master = current_nonprofit.tag_masters.find(params[:id])
tag_master.update_attribute(:deleted, true) tag_master.discard!
tag_master.tag_joins.destroy_all tag_master.tag_joins.destroy_all
render json: {}, status: :ok render json: {}, status: :ok
end end

View file

@ -13,7 +13,7 @@ class TagJoin < ApplicationRecord
def name def name
tag_master.name tag_master.name
end end
def self.create_with_name(nonprofit, h) def self.create_with_name(nonprofit, h)
tm = nonprofit.tag_masters.find_by_name(h['name']) tm = nonprofit.tag_masters.find_by_name(h['name'])

View file

@ -3,6 +3,12 @@
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later # 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 # Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
class TagMaster < ApplicationRecord class TagMaster < ApplicationRecord
# TODO replace with Discard gem
define_model_callbacks :discard
after_discard :publish_delete
# :nonprofit, :nonprofit_id, # :nonprofit, :nonprofit_id,
# :name, # :name,
# :deleted, # :deleted,
@ -11,6 +17,7 @@ class TagMaster < ApplicationRecord
validates :name, presence: true validates :name, presence: true
validate :no_dupes, on: :create validate :no_dupes, on: :create
after_create :publish_create
belongs_to :nonprofit belongs_to :nonprofit
has_many :tag_joins, dependent: :destroy has_many :tag_joins, dependent: :destroy
has_one :email_list 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? errors.add(:base, 'Duplicate tag') if nonprofit.tag_masters.not_deleted.where(name: name).any?
end 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 end

View file

@ -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>;

View file

@ -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";
}

View file

@ -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: "<object_type>.<event_name>". 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<EventType extends string, DataObject extends HoudiniObject> {
/** 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;
}

View file

@ -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