diff --git a/app/models/identity_tijuana/campaign.rb b/app/models/identity_tijuana/campaign.rb new file mode 100644 index 0000000..391000a --- /dev/null +++ b/app/models/identity_tijuana/campaign.rb @@ -0,0 +1,53 @@ +module IdentityTijuana + class Campaign < ReadWrite + self.table_name = 'campaigns' + + scope :deleted_campaigns, -> (last_updated_at, exclude_from) { + where('deleted_at is not null and deleted_at >= ? and deleted_at < ?', last_updated_at, exclude_from) + .order('deleted_at, id') + } + + scope :updated_campaigns, -> (last_updated_at, last_id) { + updated_campaigns_all(last_updated_at, last_id) + .order('updated_at, id') + .limit(Settings.tijuana.pull_batch_amount) + } + + scope :updated_campaigns_all, -> (last_updated_at, last_id) { + where('updated_at > ? or (updated_at = ? and id > ?)', last_updated_at, last_updated_at, last_id) + } + + def import(sync_id) + begin + # The campaigns table in TJ maps onto the issues table in ID. + issue = ::Issue.find_or_create_by(external_id: self.id.to_s, external_source: 'tijuana') + issue.name = self.name + issue.save! + # The campaigns.accounts_key column in TJ maps onto the issue_categories table in ID. + issue.issue_categories.clear + accounts_key = self.accounts_key + if accounts_key.present? + issue_category = ::IssueCategory.find_or_create_by(name: accounts_key) + issue.issue_categories << issue_category + end + rescue Exception => e + Rails.logger.error "Tijuana campaigns sync id:#{self.id}, error: #{e.message}" + raise + end + end + + def erase(sync_id) + begin + issue = ::Issue.find_by(external_id: self.id.to_s, external_source: 'tijuana') + if issue.present? + issue.campaigns.destroy_all # TODO: Will need to cascade to other tables. + issue.issue_categories.clear + issue.destroy + end + rescue Exception => e + Rails.logger.error "Tijuana campaigns delete id:#{self.id}, error: #{e.message}" + raise + end + end + end +end diff --git a/lib/identity_tijuana.rb b/lib/identity_tijuana.rb index ee14cf7..7eba2ad 100644 --- a/lib/identity_tijuana.rb +++ b/lib/identity_tijuana.rb @@ -1,13 +1,20 @@ require "identity_tijuana/engine" +require "identity_tijuana/campaign_helper" +require "identity_tijuana/mutex_helper" +require "identity_tijuana/redis_helper" module IdentityTijuana SYSTEM_NAME = 'tijuana' SYNCING = 'tag' CONTACT_TYPE = 'email' - PULL_JOBS = [[:fetch_user_updates, 10.minutes]] + PULL_JOBS = [[:fetch_campaign_updates, 10.minutes], [:fetch_user_updates, 10.minutes]] MEMBER_RECORD_DATA_TYPE='object' MUTEX_EXPIRY_DURATION = 10.minutes + include CampaignHelper + include MutexHelper + include RedisHelper + def self.push(sync_id, member_ids, external_system_params) begin members = Member.where(id: member_ids).with_email.order(:id) @@ -56,33 +63,20 @@ def self.get_push_jobs def self.pull(sync_id, external_system_params) begin pull_job = JSON.parse(external_system_params)['pull_job'].to_s - self.send(pull_job, sync_id) do |records_for_import_count, records_for_import, records_for_import_scope, pull_deferred| - yield records_for_import_count, records_for_import, records_for_import_scope, pull_deferred - end - rescue => e - raise e - end - end - - def self.fetch_user_updates(sync_id) - begin - mutex_acquired = acquire_mutex_lock(__method__.to_s, sync_id) + mutex_acquired = acquire_mutex_lock(pull_job, sync_id) unless mutex_acquired yield 0, {}, {}, true return end - need_another_batch = fetch_user_updates_impl(sync_id) do |records_for_import_count, records_for_import, records_for_import_scope, pull_deferred| + self.send(pull_job, sync_id) do |records_for_import_count, records_for_import, records_for_import_scope, pull_deferred| yield records_for_import_count, records_for_import, records_for_import_scope, pull_deferred end ensure - release_mutex_lock(__method__.to_s) if mutex_acquired + release_mutex_lock(pull_job) if mutex_acquired # Check to make sure that mutex lock is always released. end - schedule_pull_batch(:fetch_user_updates) if need_another_batch - schedule_pull_batch(:fetch_tagging_updates) - schedule_pull_batch(:fetch_donation_updates) end - def self.fetch_user_updates_impl(sync_id) + def self.fetch_user_updates(sync_id) started_at = DateTime.now last_updated_at = get_redis_date('tijuana:users:last_updated_at') last_id = (Sidekiq.redis { |r| r.get 'tijuana:users:last_id' } || 0).to_i @@ -100,29 +94,30 @@ def self.fetch_user_updates_impl(sync_id) updated_member_ids = Member.connection.execute(<<~SQL SELECT id as member_id FROM members - WHERE updated_at > '#{last_updated_at}' - AND updated_at <= '#{users_dependent_data_cutoff}' - UNION - SELECT DISTINCT member_id - FROM addresses - WHERE updated_at > '#{last_updated_at}' - AND updated_at <= '#{users_dependent_data_cutoff}' - UNION - SELECT distinct member_id - FROM custom_fields - WHERE updated_at > '#{last_updated_at}' - AND updated_at <= '#{users_dependent_data_cutoff}' - UNION - SELECT DISTINCT member_id - FROM member_subscriptions - WHERE updated_at > '#{last_updated_at}' - AND updated_at <= '#{users_dependent_data_cutoff}' - UNION - SELECT DISTINCT member_id - FROM phone_numbers - WHERE updated_at > '#{last_updated_at}' - AND updated_at <= '#{users_dependent_data_cutoff}' - ORDER BY member_id; + WHERE (updated_at > '#{last_updated_at}' + AND updated_at <= '#{users_dependent_data_cutoff}') + OR id IN ( + SELECT DISTINCT member_id + FROM addresses + WHERE updated_at > '#{last_updated_at}' + AND updated_at <= '#{users_dependent_data_cutoff}' + UNION + SELECT distinct member_id + FROM custom_fields + WHERE updated_at > '#{last_updated_at}' + AND updated_at <= '#{users_dependent_data_cutoff}' + UNION + SELECT DISTINCT member_id + FROM member_subscriptions + WHERE updated_at > '#{last_updated_at}' + AND updated_at <= '#{users_dependent_data_cutoff}' + UNION + SELECT DISTINCT member_id + FROM phone_numbers + WHERE updated_at > '#{last_updated_at}' + AND updated_at <= '#{users_dependent_data_cutoff}' + ORDER BY member_id + ); SQL ).map {|member_id_row| member_id_row['member_id']} @@ -154,7 +149,11 @@ def self.fetch_user_updates_impl(sync_id) false ) - updated_users.count < updated_users_all.count + release_mutex_lock(:fetch_user_updates) + need_another_batch = updated_users.count < updated_users_all.count + schedule_pull_batch(:fetch_user_updates) if need_another_batch + schedule_pull_batch(:fetch_tagging_updates) + schedule_pull_batch(:fetch_donation_updates) end def self.fetch_users_for_dedupe @@ -173,22 +172,6 @@ def self.fetch_users_for_dedupe end def self.fetch_donation_updates(sync_id) - begin - mutex_acquired = acquire_mutex_lock(__method__.to_s, sync_id) - unless mutex_acquired - yield 0, {}, {}, true - return - end - need_another_batch = fetch_donation_updates_impl(sync_id) do |records_for_import_count, records_for_import, records_for_import_scope, pull_deferred| - yield records_for_import_count, records_for_import, records_for_import_scope, pull_deferred - end - ensure - release_mutex_lock(__method__.to_s) if mutex_acquired - end - schedule_pull_batch(:fetch_donation_updates) if need_another_batch - end - - def self.fetch_donation_updates_impl(sync_id) started_at = DateTime.now last_updated_at = get_redis_date('tijuana:donations:last_updated_at') last_id = (Sidekiq.redis { |r| r.get 'tijuana:donations:last_id' } || 0).to_i @@ -221,26 +204,12 @@ def self.fetch_donation_updates_impl(sync_id) false ) - updated_donations.count < updated_donations_all.count + release_mutex_lock(:fetch_donation_updates) + need_another_batch = updated_donations.count < updated_donations_all.count + schedule_pull_batch(:fetch_donation_updates) if need_another_batch end def self.fetch_tagging_updates(sync_id) - begin - mutex_acquired = acquire_mutex_lock(__method__.to_s, sync_id) - unless mutex_acquired - yield 0, {}, {}, true - return - end - need_another_batch = fetch_tagging_updates_impl(sync_id) do |records_for_import_count, records_for_import, records_for_import_scope, pull_deferred| - yield records_for_import_count, records_for_import, records_for_import_scope, pull_deferred - end - ensure - release_mutex_lock(__method__.to_s) if mutex_acquired - end - schedule_pull_batch(:fetch_tagging_updates) if need_another_batch - end - - def self.fetch_tagging_updates_impl(sync_id) latest_tagging_scope_limit = 50000 started_at = DateTime.now last_id = (Sidekiq.redis { |r| r.get 'tijuana:taggings:last_id' } || 0).to_i @@ -369,50 +338,13 @@ def self.fetch_tagging_updates_impl(sync_id) false ) - results.count < tags_remaining_count + release_mutex_lock(:fetch_tagging_updates) + need_another_batch = results.count < tags_remaining_count + schedule_pull_batch(:fetch_tagging_updates) if need_another_batch end private - def self.acquire_mutex_lock(method_name, sync_id) - mutex_name = "#{SYSTEM_NAME}:mutex:#{method_name}" - new_mutex_expiry = DateTime.now + MUTEX_EXPIRY_DURATION - mutex_acquired = set_redis_date(mutex_name, new_mutex_expiry, true) - unless mutex_acquired - mutex_expiry = get_redis_date(mutex_name) - if mutex_expiry.past? - unless worker_currently_running?(method_name, sync_id) - delete_redis_date(mutex_name) - mutex_acquired = set_redis_date(mutex_name, new_mutex_expiry, true) - end - end - end - mutex_acquired - end - - def self.release_mutex_lock(method_name) - mutex_name = "#{SYSTEM_NAME}:mutex:#{method_name}" - delete_redis_date(mutex_name) - end - - def self.get_redis_date(redis_identifier, default_value=Time.at(0)) - date_str = Sidekiq.redis { |r| r.get redis_identifier } - date_str ? Time.parse(date_str) : default_value - end - - def self.set_redis_date(redis_identifier, date_time_value, as_mutex=false) - date_str = date_time_value.utc.to_fs(:inspect) # Ensures fractional seconds are retained - if as_mutex - Sidekiq.redis { |r| r.setnx redis_identifier, date_str } - else - Sidekiq.redis { |r| r.set redis_identifier, date_str } - end - end - - def self.delete_redis_date(redis_identifier) - Sidekiq.redis { |r| r.del redis_identifier } - end - def self.schedule_pull_batch(pull_job) sync = Sync.create!( external_system: SYSTEM_NAME, diff --git a/lib/identity_tijuana/campaign_helper.rb b/lib/identity_tijuana/campaign_helper.rb new file mode 100644 index 0000000..d56e2f6 --- /dev/null +++ b/lib/identity_tijuana/campaign_helper.rb @@ -0,0 +1,66 @@ +module IdentityTijuana + module CampaignHelper + module ClassMethods + def fetch_campaign_updates(sync_id) + started_at = DateTime.now + last_updated_at = get_redis_date('tijuana:campaigns:last_updated_at') + last_id = (Sidekiq.redis { |r| r.get 'tijuana:campaigns:last_id' } || 0).to_i + campaigns_dependent_data_cutoff = DateTime.now + updated_campaigns = IdentityTijuana::Campaign.updated_campaigns(last_updated_at, last_id) + updated_campaigns_all = IdentityTijuana::Campaign.updated_campaigns_all(last_updated_at, last_id) + + updated_campaigns.each do |campaign| + campaign.import(sync_id) + end + + unless updated_campaigns.empty? + campaigns_dependent_data_cutoff = updated_campaigns.last.updated_at if updated_campaigns.count < updated_campaigns_all.count + end + + # Erase any logically deleted campaigns from ID. + deleted_campaigns = IdentityTijuana::Campaign.deleted_campaigns(last_updated_at, campaigns_dependent_data_cutoff) + deleted_campaigns.each do |campaign| + campaign.erase(sync_id) + end + + unless updated_campaigns.empty? + set_redis_date('tijuana:campaigns:last_updated_at', updated_campaigns.last.updated_at) + Sidekiq.redis { |r| r.set 'tijuana:campaigns:last_id', updated_campaigns.last.id } + end + + set_redis_date('tijuana:campaigns:dependent_data_cutoff', campaigns_dependent_data_cutoff) + + execution_time_seconds = ((DateTime.now - started_at) * 24 * 60 * 60).to_i + yield( + updated_campaigns.size, + updated_campaigns.pluck(:id), + { + scope: 'tijuana:campaigns:last_updated_at', + scope_limit: Settings.tijuana.pull_batch_amount, + from: last_updated_at, + to: updated_campaigns.empty? ? nil : updated_campaigns.last.updated_at, + started_at: started_at, + completed_at: DateTime.now, + execution_time_seconds: execution_time_seconds, + remaining_behind: updated_campaigns_all.count + }, + false + ) + + release_mutex_lock(:fetch_campaign_updates) + need_another_batch = updated_campaigns.count < updated_campaigns_all.count + if need_another_batch + schedule_pull_batch(:fetch_campaign_updates) + else + schedule_pull_batch(:fetch_page_sequence_updates) + schedule_pull_batch(:fetch_push_updates) + end + end + end + + extend ClassMethods + def self.included(other) + other.extend(ClassMethods) + end + end +end diff --git a/lib/identity_tijuana/mutex_helper.rb b/lib/identity_tijuana/mutex_helper.rb new file mode 100644 index 0000000..f44e088 --- /dev/null +++ b/lib/identity_tijuana/mutex_helper.rb @@ -0,0 +1,35 @@ +require "identity_tijuana/redis_helper" + +module IdentityTijuana + module MutexHelper + include IdentityTijuana::RedisHelper + + module ClassMethods + def acquire_mutex_lock(method_name, sync_id) + mutex_name = "#{SYSTEM_NAME}:mutex:#{method_name}" + new_mutex_expiry = DateTime.now + MUTEX_EXPIRY_DURATION + mutex_acquired = set_redis_date(mutex_name, new_mutex_expiry, true) + unless mutex_acquired + mutex_expiry = get_redis_date(mutex_name) + if mutex_expiry.past? + unless worker_currently_running?(method_name, sync_id) + delete_redis_date(mutex_name) + mutex_acquired = set_redis_date(mutex_name, new_mutex_expiry, true) + end + end + end + mutex_acquired + end + + def release_mutex_lock(method_name) + mutex_name = "#{SYSTEM_NAME}:mutex:#{method_name}" + delete_redis_date(mutex_name) + end + end + + extend ClassMethods + def self.included(other) + other.extend(ClassMethods) + end + end +end \ No newline at end of file diff --git a/lib/identity_tijuana/redis_helper.rb b/lib/identity_tijuana/redis_helper.rb new file mode 100644 index 0000000..c7c9af5 --- /dev/null +++ b/lib/identity_tijuana/redis_helper.rb @@ -0,0 +1,28 @@ +module IdentityTijuana + module RedisHelper + module ClassMethods + def get_redis_date(redis_identifier, default_value=Time.at(0)) + date_str = Sidekiq.redis { |r| r.get redis_identifier } + date_str ? Time.parse(date_str) : default_value + end + + def set_redis_date(redis_identifier, date_time_value, as_mutex=false) + date_str = date_time_value.utc.strftime("%Y-%m-%d %H:%M:%S.%9N %z") # Ensures fractional seconds are retained + if as_mutex + Sidekiq.redis { |r| r.setnx redis_identifier, date_str } + else + Sidekiq.redis { |r| r.set redis_identifier, date_str } + end + end + + def delete_redis_date(redis_identifier) + Sidekiq.redis { |r| r.del redis_identifier } + end + end + + extend ClassMethods + def self.included(other) + other.extend(ClassMethods) + end + end +end diff --git a/spec/lib/identity_tijuana_pull_spec.rb b/spec/lib/identity_tijuana_pull_spec.rb index dec15ec..299d05b 100644 --- a/spec/lib/identity_tijuana_pull_spec.rb +++ b/spec/lib/identity_tijuana_pull_spec.rb @@ -27,6 +27,45 @@ def phone_numbers_are_equivalent(phone1, phone2) end end + context '#fetch_campaign_updates' do + context 'TJ campaign/ID issue handling' do + before do + @tj_campaign_1 = IdentityTijuana::Campaign.create(name: 'Campaign 1') + @tj_campaign_2 = IdentityTijuana::Campaign.create(name: 'Campaign 2') + end + + it 'creates TJ campaigns as issues in Identity' do + IdentityTijuana.fetch_campaign_updates(@sync_id) {} + expect(Issue.count).to eq(2) + end + + it 'updates changed TJ campaigns in Identity' do + IdentityTijuana.fetch_campaign_updates(@sync_id) {} + expect(Issue.find_by(external_id: @tj_campaign_1.id, external_source: 'tijuana').name).to eq('Campaign 1') + @tj_campaign_1.name = 'Campaign 1 changed' + @tj_campaign_1.save! + IdentityTijuana.fetch_campaign_updates(@sync_id) {} + expect(Issue.find_by(external_id: @tj_campaign_1.id, external_source: 'tijuana').name).to eq('Campaign 1 changed') + end + + it 'doesnt create deleted TJ campaigns as issues in Identity' do + @tj_campaign_2.deleted_at = DateTime.now.utc + @tj_campaign_2.save! + IdentityTijuana.fetch_campaign_updates(@sync_id) {} + expect(Issue.count).to eq(1) + end + + it 'removes deleted TJ campaigns from Identity' do + IdentityTijuana.fetch_campaign_updates(@sync_id) {} + expect(Issue.count).to eq(2) + @tj_campaign_2.deleted_at = DateTime.now.utc + @tj_campaign_2.save! + IdentityTijuana.fetch_campaign_updates(@sync_id) {} + expect(Issue.count).to eq(1) + end + end + end + context '#fetch_user_updates' do before(:each) do @email_sub = FactoryBot.create(:email_subscription) diff --git a/spec/support/external_database.rb b/spec/support/external_database.rb index 9460e02..a4c682a 100644 --- a/spec/support/external_database.rb +++ b/spec/support/external_database.rb @@ -37,6 +37,7 @@ def clean IdentityTijuana::Tagging.all.destroy_all IdentityTijuana::Tag.all.destroy_all IdentityTijuana::Postcode.all.destroy_all + IdentityTijuana::Campaign.all.destroy_all end end end diff --git a/spec/support/tijuana_test_schema.rb b/spec/support/tijuana_test_schema.rb index 1e36a63..4a5dee1 100644 --- a/spec/support/tijuana_test_schema.rb +++ b/spec/support/tijuana_test_schema.rb @@ -1,5 +1,29 @@ class CreateTijuanaTestDb < ActiveRecord::Migration[5.0] def up + create_table "campaigns", force: :cascade do |t| + t.string "name", limit: 64 + t.text "description", limit: 65535 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.datetime "deleted_at" + t.string "created_by", limit: 255 + t.string "updated_by", limit: 255 + t.integer "alternate_key", limit: 4 + t.boolean "opt_out", default: true + t.integer "theme_id", limit: 4, default: 1, null: false + t.string "slug", limit: 255 + t.string "accounts_key", limit: 255 + t.boolean "quarantined" + t.boolean "hidden_in_admin", default: false + t.string "default_email_name", limit: 255 + t.string "default_email_address", limit: 255 + t.string "default_email_reply", limit: 255 + end + + add_index "campaigns", ["accounts_key"], name: "index_campaigns_on_accounts_key", using: :btree + add_index "campaigns", ["slug"], name: "index_campaigns_on_slug", using: :btree + add_index "campaigns", ["updated_at"], name: "index_campaigns_on_updated_at", using: :btree + create_table 'tags', force: :cascade do |t| t.string 'name', limit: 255 t.integer 'taggings_count', limit: 4, default: 0 diff --git a/spec/test_identity_app/app/models/campaign.rb b/spec/test_identity_app/app/models/campaign.rb new file mode 100644 index 0000000..c6bcbd0 --- /dev/null +++ b/spec/test_identity_app/app/models/campaign.rb @@ -0,0 +1,6 @@ +class Campaign < ApplicationRecord + has_many :actions + has_many :mailings + belongs_to :issue, optional: true + belongs_to :author, class_name: 'Member', optional: true +end diff --git a/spec/test_identity_app/app/models/issue.rb b/spec/test_identity_app/app/models/issue.rb new file mode 100644 index 0000000..f8680c7 --- /dev/null +++ b/spec/test_identity_app/app/models/issue.rb @@ -0,0 +1,6 @@ +class Issue < ApplicationRecord + has_many :campaigns + has_and_belongs_to_many :issue_categories + + validates_uniqueness_of :name +end diff --git a/spec/test_identity_app/app/models/issue_category.rb b/spec/test_identity_app/app/models/issue_category.rb new file mode 100644 index 0000000..fa29c12 --- /dev/null +++ b/spec/test_identity_app/app/models/issue_category.rb @@ -0,0 +1,15 @@ +# == Schema Information +# +# Table name: issue_categories +# +# id :integer not null, primary key +# name :text +# created_at :datetime +# updated_at :datetime +# + +class IssueCategory < ApplicationRecord + has_and_belongs_to_many :issues + + validates_uniqueness_of :name +end diff --git a/spec/test_identity_app/db/migrate/000_initial.rb b/spec/test_identity_app/db/migrate/000_initial.rb index 67800d1..d43a1f1 100644 --- a/spec/test_identity_app/db/migrate/000_initial.rb +++ b/spec/test_identity_app/db/migrate/000_initial.rb @@ -27,7 +27,33 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["request_uuid"], name: "index_active_record_audits_on_request_uuid" t.index ["user_id", "user_type"], name: "user_index" end - + + create_table "campaigns", force: :cascade do |t| + t.text "name" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.integer "issue_id" + t.text "description" + t.integer "author_id" + t.integer "controlshift_campaign_id" + t.text "campaign_type" + t.float "latitude" + t.float "longitude" + t.text "location" + t.text "image" + t.text "url" + t.text "slug" + t.text "moderation_status" + t.datetime "finished_at", precision: nil + t.string "target_type" + t.string "outcome" + t.string "languages", default: [], array: true + t.string "external_id" + t.string "external_source" + t.index ["author_id"], name: "index_campaigns_on_author_id" + t.index ["issue_id"], name: "index_campaigns_on_issue_id" + end + create_table "contact_campaigns", id: :serial, force: :cascade do |t| t.text "name" t.integer "external_id" @@ -76,7 +102,6 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["external_id"], name: "index_contacts_on_external_id" end - #?? create_table "delayed_jobs", force: :cascade do |t| t.integer "priority", default: 0 t.integer "attempts", default: 0 @@ -91,6 +116,30 @@ class Initial < ActiveRecord::Migration[4.2] t.datetime "updated_at" end + create_table "issue_categories", id: :serial, force: :cascade do |t| + t.text "name" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + end + + create_table "issue_categories_issues", id: :serial, force: :cascade do |t| + t.integer "issue_id", null: false + t.integer "issue_category_id", null: false + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.index ["issue_category_id"], name: "index_issue_categories_issues_on_issue_category_id" + t.index ["issue_id"], name: "index_issue_categories_issues_on_issue_id" + end + + create_table "issues", id: :serial, force: :cascade do |t| + t.text "name" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.boolean "default" + t.string "external_id" + t.string "external_source" + end + create_table "list_members", id: :serial, force: :cascade do |t| t.integer "list_id", null: false t.integer "member_id", null: false @@ -113,7 +162,6 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["synced_to_redshift"], name: "index_lists_on_synced_to_redshift" end - #?? create_table "member_external_ids", id: :serial, force: :cascade do |t| t.integer "member_id", null: false t.string "system", null: false @@ -178,7 +226,6 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["role_id"], name: "index_members_on_role_id" end - #? create_table "organisation_memberships", force: :cascade do |t| t.integer "member_id", null: false t.integer "organisation_id", null: false @@ -189,7 +236,6 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["organisation_id"], name: "index_organisation_memberships_on_organisation_id" end - #? create_table "organisations", force: :cascade do |t| t.text "name" t.text "notes" @@ -197,7 +243,6 @@ class Initial < ActiveRecord::Migration[4.2] t.datetime "updated_at" end - #? create_table "permissions", id: :serial, force: :cascade do |t| t.text "permission_slug" t.datetime "created_at" @@ -214,7 +259,6 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["phone"], name: "index_phone_numbers_on_phone" end - #? create_table "role_permissions", id: :serial, force: :cascade do |t| t.integer "role_id", null: false t.integer "permission_id", null: false @@ -222,7 +266,6 @@ class Initial < ActiveRecord::Migration[4.2] t.index ["role_id"], name: "index_role_permissions_on_role_id" end - #? create_table "roles", id: :serial, force: :cascade do |t| t.integer "role_id" t.text "description" diff --git a/spec/test_identity_app/db/schema.rb b/spec/test_identity_app/db/schema.rb index dbf3f21..e6a7fc5 100644 --- a/spec/test_identity_app/db/schema.rb +++ b/spec/test_identity_app/db/schema.rb @@ -88,6 +88,32 @@ t.text "representative_gender" end + create_table "campaigns", force: :cascade do |t| + t.text "name" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.integer "issue_id" + t.text "description" + t.integer "author_id" + t.integer "controlshift_campaign_id" + t.text "campaign_type" + t.float "latitude" + t.float "longitude" + t.text "location" + t.text "image" + t.text "url" + t.text "slug" + t.text "moderation_status" + t.datetime "finished_at", precision: nil + t.string "target_type" + t.string "outcome" + t.string "languages", default: [], array: true + t.string "external_id" + t.string "external_source" + t.index ["author_id"], name: "index_campaigns_on_author_id" + t.index ["issue_id"], name: "index_campaigns_on_issue_id" + end + create_table "canonical_addresses", id: :serial, force: :cascade do |t| t.string "official_id" t.text "line1" @@ -188,6 +214,30 @@ t.datetime "updated_at" end + create_table "issue_categories", id: :serial, force: :cascade do |t| + t.text "name" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + end + + create_table "issue_categories_issues", id: :serial, force: :cascade do |t| + t.integer "issue_id", null: false + t.integer "issue_category_id", null: false + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.index ["issue_category_id"], name: "index_issue_categories_issues_on_issue_category_id" + t.index ["issue_id"], name: "index_issue_categories_issues_on_issue_id" + end + + create_table "issues", id: :serial, force: :cascade do |t| + t.text "name" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.boolean "default" + t.string "external_id" + t.string "external_source" + end + create_table "list_members", id: :serial, force: :cascade do |t| t.integer "list_id", null: false t.integer "member_id", null: false