Add support for 'event_discount.*' events

This commit is contained in:
Eric Schultz 2021-01-13 16:59:04 -06:00 committed by Eric Schultz
parent 2893a2bf47
commit e7c482bf3a
6 changed files with 315 additions and 72 deletions

View file

@ -8,14 +8,7 @@ class EventDiscountsController < ApplicationController
before_action :authenticate_event_editor!, except: [:index]
def create
event_discount_params[:event_id] = current_event.id
render JsonResp.new(event_discount_params) do |_data|
requires(:code, :name).as_string
requires(:event_id, :percent).as_int
end.when_valid do |data|
{ status: 200, json: { event_discount: current_event.event_discounts.create(data) } }
end
render json: { data: {event_discount: current_event.event_discounts.create(event_discount_params[:event_discount]) } }
end
def index
@ -23,27 +16,22 @@ class EventDiscountsController < ApplicationController
end
def update
discount = Hamster.to_ruby(
Psql.execute(
Qexpr.new.update(:event_discounts, event_discount_params)
.where('id=$id', id: params[:id])
.returning('*')
).first
)
render json: { status: 200, data: discount }
current_event_discount.update event_discount_params[:event_discount]
render json: { status: 200, data: current_event_discount }
end
def destroy
Psql.execute(
Qexpr.new.delete_from('event_discounts')
.where('event_discounts.event_id=$id', id: params['event_id'])
.where('event_discounts.id=$id', id: params['id'])
)
current_event_discount.destroy
end
private
def current_event_discount
current_event.event_discounts.find(params[:id])
end
def event_discount_params
params.required(:event_discount).permit(:code, :event_id, :name, :percent)
params.required(:event_discount).permit(:code, :name, :percent)
end
end

View file

@ -7,19 +7,77 @@ class EventDiscount < ApplicationRecord
# :event_id,
# :name,
# :percent
validates :name, presence: true
validates :code, presence: true
validates :event, presence: true
validates :percent, presence: true, numericality: {only_integer: true, greater_than: 0, less_than_or_equal_to: 100}
# we use after_create_commit because the db could be in an inconsistent state and the messages will be slightly wrong
# we use after commit on the rest for consistency
after_create_commit :publish_create
after_destroy_commit :publish_delete
after_update_commit :publish_updated
belongs_to :event
has_many :tickets
def to_builder(*expand)
Jbuilder.new do |json|
json.(self, :id, :name)
json.(self, :id, :name, :code)
json.deleted !persisted?
json.object 'event_discount'
json.discount do
json.percent percent
end
if event
if expand.include? :event
json.event event.to_builder
else
json.event event.id
end
if event.nonprofit
if expand.include? :nonprofit
json.nonprofit event.nonprofit.to_builder
else
json.nonprofit event.nonprofit.id
end
else
json.nonprofit nil
end
if expand.include? :ticket_levels
json.ticket_levels event.ticket_levels do |tl|
json.merge! tl.to_builder.attributes!
end
else
json.ticket_levels event.ticket_levels.pluck(:id)
end
end
end
end
private
def publish_create
Houdini.event_publisher.announce(:event_discount_created, to_event('event_discount.created', :event, :nonprofit, :ticket_levels).attributes!)
end
def publish_updated
Houdini.event_publisher.announce(:event_discount_updated, to_event('event_discount.updated', :event, :nonprofit, :ticket_levels).attributes!)
end
def publish_delete
Houdini.event_publisher.announce(:event_discount_deleted, to_event('event_discount.deleted', :event, :nonprofit, :ticket_levels).attributes!)
end
def to_event(event_type, *expand)
Jbuilder.new do |event|
event.id SecureRandom.uuid
event.object 'object_event'
event.type event_type
event.data do
event.object to_builder(*expand)
end
end
end

View file

@ -73,7 +73,7 @@ class TicketLevel < ApplicationRecord
if expand.include? :event_discounts
json.event_discounts event.event_discounts do |disc|
disc.to_builder
json.merge! disc.to_builder.attributes!
end
else
json.event_discounts event.event_discounts.pluck(:id)
@ -84,18 +84,18 @@ class TicketLevel < ApplicationRecord
private
def publish_create
Houdini.event_publisher.announce(:ticket_level_created, to_event('ticket_level.created', :event, :nonprofit).attributes!)
Houdini.event_publisher.announce(:ticket_level_created, to_event('ticket_level.created', :event, :nonprofit, :event_discounts).attributes!)
end
def publish_updated
# we don't run update when we've really just discarded
unless deleted
Houdini.event_publisher.announce(:ticket_level_updated, to_event('ticket_level.updated', :event, :nonprofit).attributes!)
Houdini.event_publisher.announce(:ticket_level_updated, to_event('ticket_level.updated', :event, :nonprofit, :event_discounts).attributes!)
end
end
def publish_delete
Houdini.event_publisher.announce(:ticket_level_deleted, to_event('ticket_level.deleted', :event, :nonprofit).attributes!)
Houdini.event_publisher.announce(:ticket_level_deleted, to_event('ticket_level.deleted', :event, :nonprofit, :event_discounts).attributes!)
end
def to_event(event_type, *expand)

View file

@ -1,15 +1,23 @@
// License: LGPL-3.0-or-later
import type { IdType, HoudiniObject } from '../../common';
import type { IdType, HoudiniObject, Amount, HoudiniEvent } from '../../common';
import type Nonprofit from '..';
import type Event from '.';
import type { TicketLevel } from './TicketLevel';
type DiscountType = { percent: number } | { amount: Amount };
/**
* Describes an EventDiscount (shell)
*/
export interface EventDiscount extends HoudiniObject {
code: string;
discount: DiscountType;
event: IdType | Event;
nonprofit: IdType | Nonprofit;
object: "event_discount";
ticket_levels: IdType[] | TicketLevel[];
}
}
export type EventDiscountCreated = HoudiniEvent<'event_discount.created', EventDiscount>;
export type EventDiscountlUpdated = HoudiniEvent<'event_discount.updated', EventDiscount>;
export type EventDiscountDeleted = HoudiniEvent<'event_discount.deleted', EventDiscount>;

View file

@ -0,0 +1,232 @@
# 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 EventDiscount, type: :model do
include_context :shared_donation_charge_context
let(:name) {"CUSTOM EVENT DISCOUNT"}
let(:percent) { 55}
let(:code) { "fewet"}
let(:event_discount) {
ticket_level
event.event_discounts.create(name: name, percent: percent, code: code)
}
describe 'validate' do
let(:event_discount) { ed = EventDiscount.new; ed.save; ed}
let(:ed_percent_at_0) { ed = EventDiscount.new(percent: 0); ed.save; ed}
let(:ed_percent_at_101) { ed = EventDiscount.new(percent: 101); ed.save; ed}
it('has errors on name') do
expect(event_discount.errors.details[:name].length).to be(1)
end
it('has errors on code') do
expect(event_discount.errors.details[:code].length).to be(1)
end
it('has errors on event') do
expect(event_discount.errors.details[:event].length).to be(1)
end
it('has errors on percent') do
expect(event_discount.errors.details[:percent].length).to be(2)
end
it('has errors on percents at 0') do
expect(ed_percent_at_0.errors.details[:percent].length).to be(1)
end
it('has errors on percents at 101') do
expect(ed_percent_at_101.errors.details[:percent].length).to be(1)
end
end
describe 'create' do
it 'is without error' do
expect(event_discount.errors).to be_empty
end
it 'announces create' do
expect(Houdini.event_publisher).to receive(:announce).with(:ticket_level_created, anything).ordered
expect(Houdini.event_publisher).to receive(:announce).with(:event_discount_created, {
'id' => kind_of(String),
'object' => 'object_event',
'type' => 'event_discount.created',
'data' => {
'object' => {
'code' => code,
'deleted' => false,
'discount' => {
'percent' => percent
},
'event' => {
'id' => event.id,
'name' => event.name,
'object' => 'event',
'nonprofit' => nonprofit.id
},
'id'=> kind_of(Numeric),
'name' => name,
'nonprofit'=> {
'id' => nonprofit.id,
'name' => nonprofit.name,
'object' => 'nonprofit'
},
'object' => 'event_discount',
'ticket_levels' => [
{
'id' => ticket_level.id,
'name' => ticket_level.name,
'deleted' => ticket_level.deleted,
'order' => ticket_level.order,
'limit' => ticket_level.limit,
'object' => 'ticket_level',
'description' => ticket_level.description,
'amount' => {
'value_in_cents' => ticket_level.amount,
'currency' => 'usd'
},
'available_to' => 'everyone',
'nonprofit' => nonprofit.id,
'event' => event.id,
'event_discounts' => [kind_of(Numeric)]
}
]
}
}
})
event_discount
end
end
describe 'update' do
it 'is without error' do
event_discount.code = 'code'
event_discount.save
expect(event_discount.errors).to be_empty
end
it 'announces updated' do
expect(Houdini.event_publisher).to receive(:announce).with(:ticket_level_created, anything).ordered
expect(Houdini.event_publisher).to receive(:announce).with(:event_discount_created, anything).ordered
expect(Houdini.event_publisher).to receive(:announce).with(:event_discount_updated, {
'id' => kind_of(String),
'object' => 'object_event',
'type' => 'event_discount.updated',
'data' => {
'object' => {
'code' => 'code',
'deleted' => false,
'discount' => {
'percent' => percent
},
'event' => {
'id' => event.id,
'name' => event.name,
'object' => 'event',
'nonprofit' => nonprofit.id
},
'id'=> kind_of(Numeric),
'name' => name,
'nonprofit'=> {
'id' => nonprofit.id,
'name' => nonprofit.name,
'object' => 'nonprofit'
},
'object' => 'event_discount',
'ticket_levels' => [
{
'id' => ticket_level.id,
'name' => ticket_level.name,
'deleted' => ticket_level.deleted,
'order' => ticket_level.order,
'limit' => ticket_level.limit,
'object' => 'ticket_level',
'description' => ticket_level.description,
'amount' => {
'value_in_cents' => ticket_level.amount,
'currency' => 'usd'
},
'available_to' => 'everyone',
'nonprofit' => nonprofit.id,
'event' => event.id,
'event_discounts' => [kind_of(Numeric)]
}
]
}
}
}).ordered
event_discount.code = 'code'
event_discount.save!
end
end
describe 'deleted' do
it 'is without error' do
event_discount.destroy
expect(event_discount).to_not be_persisted
end
it 'announces deleted' do
expect(Houdini.event_publisher).to receive(:announce).with(:ticket_level_created, anything).ordered
expect(Houdini.event_publisher).to receive(:announce).with(:event_discount_created, anything).ordered
expect(Houdini.event_publisher).to receive(:announce).with(:event_discount_deleted, {
'id' => kind_of(String),
'object' => 'object_event',
'type' => 'event_discount.deleted',
'data' => {
'object' => {
'code' => code,
'deleted' => true,
'discount' => {
'percent' => percent
},
'event' => {
'id' => event.id,
'name' => event.name,
'object' => 'event',
'nonprofit' => nonprofit.id
},
'id'=> kind_of(Numeric),
'name' => name,
'nonprofit'=> {
'id' => nonprofit.id,
'name' => nonprofit.name,
'object' => 'nonprofit'
},
'object' => 'event_discount',
'ticket_levels' => [
{
'id' => ticket_level.id,
'name' => ticket_level.name,
'deleted' => ticket_level.deleted,
'order' => ticket_level.order,
'limit' => ticket_level.limit,
'object' => 'ticket_level',
'description' => ticket_level.description,
'amount' => {
'value_in_cents' => ticket_level.amount,
'currency' => 'usd'
},
'available_to' => 'everyone',
'nonprofit' => nonprofit.id,
'event' => event.id,
'event_discounts' => [kind_of(Numeric)]
}
]
}
}
}).ordered
event_discount.destroy
end
end
end

View file

@ -302,47 +302,4 @@ RSpec.describe TicketLevel, type: :model do
end
end
end
# 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