diff --git a/Gemfile b/Gemfile index 060ea8618..53330492a 100644 --- a/Gemfile +++ b/Gemfile @@ -64,6 +64,10 @@ gem 'truemail' # This is needed to run the migrations from id to uuid for primary keys gem 'webdack-uuid_migration' +# for integrations! +gem "httparty" +gem "json-diff" + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] diff --git a/Gemfile.lock b/Gemfile.lock index 78255a94c..12f0cbf71 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -153,6 +153,9 @@ GEM guard-compat (~> 1.0) multi_json (~> 1.8) http_parser.rb (0.8.0) + httparty (0.20.0) + mime-types (~> 3.0) + multi_xml (>= 0.5.2) i18n (1.12.0) concurrent-ruby (~> 1.0) io-wait (0.2.3) @@ -160,6 +163,7 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) json (2.6.2) + json-diff (0.4.1) jsonapi-renderer (0.2.2) jsonapi-serializer (2.2.0) activesupport (>= 4.2) @@ -190,11 +194,15 @@ GEM marcel (1.0.2) matrix (0.4.2) method_source (1.0.0) + mime-types (3.4.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2022.0105) mini_mime (1.1.2) mini_portile2 (2.8.0) minitest (5.16.3) msgpack (1.5.4) multi_json (1.15.0) + multi_xml (0.6.0) nenv (0.3.0) nilify_blanks (1.4.0) activerecord (>= 4.0.0) @@ -426,8 +434,10 @@ DEPENDENCIES fast_excel guard guard-livereload + httparty io-wait jbuilder (~> 2.7) + json-diff jsonapi-serializer jsonapi.rb kaminari diff --git a/app/controllers/integrations_controller.rb b/app/controllers/integrations_controller.rb new file mode 100644 index 000000000..8bcd1bc0c --- /dev/null +++ b/app/controllers/integrations_controller.rb @@ -0,0 +1,28 @@ +class IntegrationsController < ResourceController + SERIALIZER_CLASS = 'IntegrationSerializer'.freeze + POLICY_CLASS = 'IntegrationPolicy'.freeze + + def airmeet + authorize model_class, policy_class: policy_class + + airmeet = Integration.find_by({name: 'airmeet'}) + + render json: serializer_class.new(airmeet, + { + include: serializer_includes, + params: {domain: "#{request.base_url}"} + } + ).serializable_hash(), + content_type: 'application/json' + end + + def allowed_params + %i[ + id + name + lock_version + config + ] + end + +end diff --git a/app/controllers/published_sessions_controller.rb b/app/controllers/published_sessions_controller.rb index e0bf0d590..615a3c781 100644 --- a/app/controllers/published_sessions_controller.rb +++ b/app/controllers/published_sessions_controller.rb @@ -1,3 +1,5 @@ class PublishedSessionsController < ResourceController -# TBD + SERIALIZER_CLASS = 'PublishedSessionSerializer'.freeze + POLICY_CLASS = 'PublishedSessionPolicy'.freeze +# end diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb new file mode 100644 index 000000000..e6b84107a --- /dev/null +++ b/app/helpers/integrations_helper.rb @@ -0,0 +1,2 @@ +module IntegrationsHelper +end diff --git a/app/javascript/administration/admin_component.vue b/app/javascript/administration/admin_component.vue index ca2206691..d628ac732 100644 --- a/app/javascript/administration/admin_component.vue +++ b/app/javascript/administration/admin_component.vue @@ -50,6 +50,9 @@ ref="configurations-manager" > + + + import AdminAccordion from './admin_accordion.vue' import PersonAdd from '../people/person_add.vue'; -import ChangeUserConventionRoles from './change-user-con-roles'; -import MailingsManager from '../mailings/mailings_manager'; -import ConfigurationsManager from '../configurations/configurations_manager'; +import ChangeUserConventionRoles from './change-user-con-roles.vue'; +import MailingsManager from '../mailings/mailings_manager.vue'; +import ConfigurationsManager from '../configurations/configurations_manager.vue'; import SheetImporterVue from '../components/sheet_importer_vue.vue'; -import AgreementManager from "@/agreements/agreement_manager"; +import AgreementManager from "@/agreements/agreement_manager.vue"; import ScheduleSettings from "@/schedule/schedule_settings.vue"; +import IntegrationSettings from "@/integrations/integration_settings.vue" export default { components: { @@ -79,7 +83,8 @@ export default { SheetImporterVue, MailingsManager, ConfigurationsManager, - ScheduleSettings + ScheduleSettings, + IntegrationSettings, }, name: 'AdminComponent', data: () => ({ diff --git a/app/javascript/airmeet/airmeet_settings.vue b/app/javascript/airmeet/airmeet_settings.vue new file mode 100644 index 000000000..d24dfba94 --- /dev/null +++ b/app/javascript/airmeet/airmeet_settings.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/app/javascript/integrations/integration_settings.vue b/app/javascript/integrations/integration_settings.vue new file mode 100644 index 000000000..3bb15c8cc --- /dev/null +++ b/app/javascript/integrations/integration_settings.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/app/javascript/people/person_tabs.vue b/app/javascript/people/person_tabs.vue index 90ac4467b..c902d47e5 100644 --- a/app/javascript/people/person_tabs.vue +++ b/app/javascript/people/person_tabs.vue @@ -53,6 +53,9 @@ + +
{{JSON.stringify(person.integrations, undefined, 2)}}
+
@@ -132,6 +135,9 @@ export default { baseTabs.push('email'); baseTabs.push('admin'); } + if(this.currentUserIsAdmin) { + baseTabs.push('integrations'); + } return baseTabs; }, person() { diff --git a/app/javascript/sessions/session_tabs.vue b/app/javascript/sessions/session_tabs.vue index 5b7c53d27..f1928dc5d 100644 --- a/app/javascript/sessions/session_tabs.vue +++ b/app/javascript/sessions/session_tabs.vue @@ -30,6 +30,9 @@ + +
{{JSON.stringify(published_session.integrations, undefined, 2)}}
+
@@ -48,6 +51,8 @@ import SessionSchedule from './session_schedule'; import SessionConflicts from '../conflicts/session_conflicts.vue' import { sessionConflictModel } from '@/store/session_conflict.store' import settingsMixin from "@/store/settings.mixin"; +import { personSessionMixin } from '@/mixins'; +import { publishedSessionModel } from '@/store/published_session.store'; export default { name: "SessionTabs", @@ -65,16 +70,20 @@ export default { }, mixins: [ modelUtilsMixin, - settingsMixin + settingsMixin, + personSessionMixin ], data: () => ({ sessionAssignmentModel, - sessionConflictModel + sessionConflictModel, }), computed: { session() { return this.selected_model(sessionModel); }, + published_session() { + return this.selected_model(publishedSessionModel) || {}; + }, assignmentFilter() { let filter = { "op": "all", @@ -120,6 +129,9 @@ export default { case 4: path = `notes/${this.id}`; break; + case 5: + path = `integrations/${this.id}` + break; } // console.debug("****** Path:", path) // change the router path to match the current tab @@ -138,7 +150,10 @@ export default { (obj) => { this.select_model(sessionModel, obj); } - ) + ), + this.fetch_model_by_id(publishedSessionModel, this.id).then( (obj) => { + this.select_model(publishedSessionModel, obj) + }) } } diff --git a/app/javascript/store/integration.store.js b/app/javascript/store/integration.store.js new file mode 100644 index 000000000..c00767a21 --- /dev/null +++ b/app/javascript/store/integration.store.js @@ -0,0 +1,28 @@ +import { FETCH } from "./model.store" + +export const integrationModel = 'integration' + +export const integrationEndpoints = { + [integrationModel]: 'integration' +} + +export const FETCH_AIRMEET_INTEGRATION = 'FETCH AIRMEET INTEGRATION' +export const SET_AIRMEET_INTEGRATION = 'SET AIRMEET INTEGRATION' + +export const integrationStore = { + state: { + airmeet: {} + }, + mutations: { + [SET_AIRMEET_INTEGRATION] (state, integration) { + state.airmeet = integration; + } + }, + actions: { + [FETCH_AIRMEET_INTEGRATION] ({dispatch, commit}) { + dispatch(FETCH, {url: 'integration/airmeet'}).then(data => { + commit(SET_AIRMEET_INTEGRATION, data); + }) + } + } +} diff --git a/app/javascript/store/model.store.js b/app/javascript/store/model.store.js index 677963979..3f7939da2 100644 --- a/app/javascript/store/model.store.js +++ b/app/javascript/store/model.store.js @@ -75,9 +75,11 @@ import { configurationStore, configurationEndpoints } from './configuration.stor // session add-ons import { sessionAssignmentStore, sessionAssignmentEndpoints } from './session_assignment.store'; +import { publishedSessionEndpoints, publishedSessionStore } from './published_session.store'; // global app things import { appStore } from './app.store'; +import { integrationEndpoints, integrationStore } from './integration.store'; // schedule workflow import { scheduleWorkflowStore, scheduleWorkflowEndpoints } from './schedule_workflow/schedule_workflow.store'; @@ -106,6 +108,8 @@ const endpoints = { // ...personExclusionEndpoints, ...scheduleWorkflowEndpoints, ...personScheduleApprovalEndpoints, + ...publishedSessionEndpoints, + ...integrationEndpoints, } // NOTE: this is really the store @@ -142,6 +146,7 @@ export const store = new Vuex.Store({ ...sessionConflictStore.selected, ...formatStore.selected, ...personScheduleApprovalStore.selected, + ...publishedSessionStore.selected, }, ...personSessionStore.state, ...settingsStore.state, @@ -153,6 +158,7 @@ export const store = new Vuex.Store({ ...roomStore.state, ...appStore.state, ...scheduleWorkflowStore.state, + ...integrationStore.state, // ...mailingStore.state }, getters: { @@ -211,7 +217,8 @@ export const store = new Vuex.Store({ ...surveyStore.mutations, ...searchStateStore.mutations, ...roomStore.mutations, - ...appStore.mutations + ...appStore.mutations, + ...integrationStore.mutations }, actions: { /** @@ -381,5 +388,6 @@ export const store = new Vuex.Store({ ...sessionConflictStore.actions, ...scheduleWorkflowStore.actions, ...personScheduleApprovalStore.actions, + ...integrationStore.actions, } }) diff --git a/app/javascript/store/published_session.store.js b/app/javascript/store/published_session.store.js new file mode 100644 index 000000000..2acf4d994 --- /dev/null +++ b/app/javascript/store/published_session.store.js @@ -0,0 +1,11 @@ +export const publishedSessionModel = 'published_session' + +export const publishedSessionEndpoints = { + [publishedSessionModel]: 'published_session' +} + +export const publishedSessionStore = { + selected: { + [publishedSessionModel]: null + } +} diff --git a/app/models/integration.rb b/app/models/integration.rb new file mode 100644 index 000000000..66c9978d0 --- /dev/null +++ b/app/models/integration.rb @@ -0,0 +1,2 @@ +class Integration < ApplicationRecord +end diff --git a/app/policies/integration_policy.rb b/app/policies/integration_policy.rb new file mode 100644 index 000000000..85e09549d --- /dev/null +++ b/app/policies/integration_policy.rb @@ -0,0 +1,19 @@ +class IntegrationPolicy < PlannerPolicy + def publish? + allowed?(action: :publish) + end + + def airmeet? + allowed?(action: :airmeet) + end + + def update? + allowed?(action: :update) + end + + class Scope < PlannerPolicy::Scope + def resolve + scope.all + end + end +end diff --git a/app/policies/published_session_policy.rb b/app/policies/published_session_policy.rb new file mode 100644 index 000000000..16736953f --- /dev/null +++ b/app/policies/published_session_policy.rb @@ -0,0 +1,7 @@ +class PublishedSessionPolicy < PlannerPolicy + class Scope < PlannerPolicy::Scope + def resolve + scope.all + end + end +end diff --git a/app/serializers/integration_serializer.rb b/app/serializers/integration_serializer.rb new file mode 100644 index 000000000..0120b3c9b --- /dev/null +++ b/app/serializers/integration_serializer.rb @@ -0,0 +1,15 @@ +class IntegrationSerializer + include JSONAPI::Serializer + + attributes :lock_version, :created_at, :updated_at, + :name, :id + + attribute :config do |integration| + {airmeet_id: integration.config["airmeet_id"], + airmeet_host: integration.config["airmeet_host"] + } + end + + + +end diff --git a/app/serializers/person_serializer.rb b/app/serializers/person_serializer.rb index f58785557..f757ac4c5 100644 --- a/app/serializers/person_serializer.rb +++ b/app/serializers/person_serializer.rb @@ -41,7 +41,8 @@ class PersonSerializer #< ActiveModel::Serializer :timezone, :twelve_hour, :attendance_type, - :availability_notes + :availability_notes, + :integrations # status and comments hidden except for staff protected_attributes :con_state, :comments diff --git a/app/serializers/published_session_serializer.rb b/app/serializers/published_session_serializer.rb index e7bffc08a..4150e7cb1 100644 --- a/app/serializers/published_session_serializer.rb +++ b/app/serializers/published_session_serializer.rb @@ -1,5 +1,5 @@ class PublishedSessionSerializer include JSONAPI::Serializer - attributes :id, :lock_version, :created_at, :updated_at + attributes :id, :lock_version, :created_at, :updated_at, :integrations end diff --git a/app/serializers/room_serializer.rb b/app/serializers/room_serializer.rb index a675c0b05..772c3ee93 100644 --- a/app/serializers/room_serializer.rb +++ b/app/serializers/room_serializer.rb @@ -5,7 +5,7 @@ class RoomSerializer :name, :sort_order, :purpose, :comment, :capacity, :floor, :open_for_schedule, :is_virtual, :area_of_space, :length, :width, :height, - :venue_id, :room_set_id + :venue_id, :room_set_id, :integrations end diff --git a/app/services/airmeet_api_service.rb b/app/services/airmeet_api_service.rb new file mode 100644 index 000000000..16ed3e1c8 --- /dev/null +++ b/app/services/airmeet_api_service.rb @@ -0,0 +1,176 @@ +module AirmeetApiService + def self.integration + @integration ||= Integration.find_or_create_by({name: :airmeet}) + end + + def self.config + integration.config + end + + def self.token + if !config['token'] || Time.at(config['token_exp']) < Time.now + auth + else + config['token'] + end + end + + def self.airmeet_id + config['airmeet_id'] + end + + def self.airmeet + @airmeet ||= Airmeet.new + end + + def self.get_participants + Airmeet.get("/airmeet/#{airmeet_id}/participants") + end + + def self.auth + token = airmeet.auth["token"] + integration.update({config: config.merge({ + token: token, + token_exp: (Time.now + (29 * 24 * 60 * 60)).to_i + })}) + token + end + + def self.info + Airmeet.get("/airmeet/#{airmeet_id}/info") + end + + def self.get_session(id) + info["sessions"].find { |s| s["sessionid"] == id } + end + + def self.create_session(sessionTitle:, sessionStartTime:, sessionDuration:, sessionSummary:, hostEmail:, speakerEmails: [], cohostEmails: []) + sessionStartTime = sessionStartTime.to_i * 1000 + Airmeet.post("/airmeet/#{airmeet_id}/session", { + body: { + sessionTitle: sessionTitle, + sessionStartTime: sessionStartTime, + sessionDuration: sessionDuration, + sessionSummary: sessionSummary, + hostEmail: hostEmail, + speakerEmails: speakerEmails, + cohostEmails: cohostEmails, + type: "HOSTING" + }.to_json + }) + end + + def self.create_speaker(name:, email:, organisation: "", designation: "", imageUrl: "", bio: "", city: "", country: "") + Airmeet.post("/airmeet/#{airmeet_id}/speaker", { + body: { + name: name, + email: email, + organisation: organisation, + designation: designation, + imageUrl: imageUrl, + bio: bio || " ", + city: city, + country: country, + }.to_json + }) + end + + def self.person_to_airmeet(person) + speaker_email = person.primary_email.email + country = nil + city = nil + if person.integrations["airmeet"] + speaker_email = person.integrations["airmeet"]["speaker_email"] || speaker_email + country = person.integrations["airmeet"]["country"] + city = person.integrations["airmeet"]["city"] + end + name = person.published_name + bio = person.bio + args = {name: name, email: speaker_email, bio: bio} + if country + args[:country] = country + end + if city + args[:city] = city + end + puts args + result = create_speaker(args) + puts result + person.update({integrations: person.integrations.merge({airmeet: {speaker_email: speaker_email, synced: true, data: args, synced_at: Time.now.iso8601}})}) + result["email"] + end + + def self.session_to_airmeet(session) + args = {sessionTitle: "#{session.room.name} - #{session.title} - #{session.format.name} - #{session.area_list.join(", ")}", + sessionSummary: session.description, + sessionDuration: session.duration, + sessionStartTime: session.start_time, + hostEmail: room_hosts[session.room_id] + }; + participants = session.published_session_assignments.filter { |sa| sa.session_assignment_role_type_id == moderator_id || sa.session_assignment_role_type_id == participant_id }.map { |sa| sa.person } + if session.environment == "virtual" + args[:speakerEmails] = participants.map{|p| p.integrations["airmeet"]["speaker_email"]} + args[:cohostEmails] = session.published_session_assignments.filter { |sa| sa.session_assignment_role_type_id == moderator_id }.map { |sa| sa.person.integrations["airmeet"]["speaker_email"] } + end + puts args + result = create_session(args); + puts result + session.update({integrations: session.integrations.merge({airmeet: {session_id: result["uid"], synced: true, synced_at: Time.now(), data: args}})}) + if session.environment == "virtual" + people_tokens = result["token"].inject({}) {|p,c| p[c["email"]] = c["token"]; p} + participants.each { |p| p.update({integrations: p.integrations.merge({airmeet: (p.integrations["airmeet"] || {}).merge({token: people_tokens[(p.integrations["airmeet"] || {})["speaker_email"]]})})})} + end + end + + def self.room_hosts + @room_hosts ||= Room.where.not(integrations: {}).inject({}) { |p, c| p[c.id] = (c.integrations["airmeet"] || {})["room_host_email"]; p } + end + + def self.virtual_people + Person.left_outer_joins(:published_session_assignments, :published_sessions, :primary_email) + .where(published_sessions: { environment: 'virtual' }, published_session_assignments: {session_assignment_role_type_id: [moderator_id, participant_id]}) + .distinct + end + + def self.virtual_sessions + SessionService.published_sessions.where(streamed: true) + end + + def self.moderator_id + @moderator_id = SessionAssignmentRoleType.find_by(name: 'Moderator').id + end + + def self.participant_id + @participant_id = SessionAssignmentRoleType.find_by(name: 'Participant').id + end + + def self.sync_to_airmeet + virtual_people.map { |p| person_to_airmeet(p) } + virtual_sessions.map { |s| session_to_airmeet(s) } + puts "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IT WORKED" + end + + class Airmeet + include HTTParty + base_uri "https://api-gateway.airmeet.com/prod" + + headers 'Content-Type' => 'application/json' + + # TODO fix me for not testing + default_options.update(verify: false) + + def auth + self.class.post("/auth", { + headers: { + "X-Airmeet-Access-Key": ENV["AIRMEET_ACCESS_KEY"], + "X-Airmeet-Secret-Key": ENV["AIRMEET_SECRET_KEY"], + "X-Airmeet-Access-Token": "" + }, + verify: false + }) + end + + headers 'X-Airmeet-Access-Token' => AirmeetApiService.token + end + +end diff --git a/config/routes.rb b/config/routes.rb index a0f818daf..c6d9f4522 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -94,7 +94,9 @@ get 'report/schedule_reports/schedule_diff(/:from)(/:to)', to: 'reports/schedule_reports#schedule_diff' get 'publication_date/reset', to: 'publication_dates#reset' - resources :publication_dates, path: 'publication_date', only: [:index] + resources :publication_dates, path: 'publication_date', only: [:index, :update] + resources :integrations, path: 'integration', only: [:index, :update] + get 'integration/airmeet', to: 'integrations#airmeet' resources :availabilities, path: 'availability', except: [:index] resources :person_exclusions, path: 'person_exclusion', except: [:index] diff --git a/db/integrations.md b/db/integrations.md new file mode 100644 index 000000000..5df98effa --- /dev/null +++ b/db/integrations.md @@ -0,0 +1,57 @@ +This page should document what data structrue is expected for the integration columns + +## Config + + { + "airmeet_id": "uuid", + "airmeet_host": "host email", + "active": true, + "token": "jwt set by the system here" + "token_exp": "time set by the system here" + } + +## Session + + { + "airmeet": { + "synced": true + "synced_at": "ISO8601 timestamp", + "sessionTitle": "generated string here" , + "sesionStartTime": "epoch in ms", + "sessionDuration": "number in minutes", + "sessionSummary": "generated string here" + "hostEmail": "room email", + "speakerEmails": "speaker emails", + "cohostEmails": "moderator emails", + "sessionId": "??" + } + } + +## Person + + { + "airmeet": { + "synced": true + "speaker_email": "speaker@email.com" + "bio": "bio here", + "name": "name here" + "synced_at": "ISO8601 timestamp" + } + } + +## Session Assignment + + { + "airmeet": { + "magic_link": "here" + "synced_at": "ISO8601 timestamp" + } + } + +## Room + + { + "airmeet": { + "room_host_email": "helpdesk+roomname@chicon.org" + } + } diff --git a/db/migrate/20220818200500_create_integrations.rb b/db/migrate/20220818200500_create_integrations.rb new file mode 100644 index 000000000..0f9e91fc2 --- /dev/null +++ b/db/migrate/20220818200500_create_integrations.rb @@ -0,0 +1,27 @@ +class CreateIntegrations < ActiveRecord::Migration[6.1] + def change + create_table :integrations, id: :uuid do |t| + t.string :name + t.jsonb :config, null: false, default: {} + + t.timestamps + end + + create_table :integration_publishes, id: :uuid do |t| + t.string :integration_name + t.jsonb :data, null: false, default: {} + t.datetime :started_at + t.datetime :completed_at + t.string :created_by + + t.timestamps + end + + # fields to hold the integration information for each session/person/session_assignment/room + add_column :published_sessions, :integrations, :jsonb, null: false, default: {} + add_column :people, :integrations, :jsonb, null: false, default: {} + # todo maybe i don't need this one if the tokens are always the same + add_column :published_session_assignments, :integrations, :jsonb, null: false, default: {} + add_column :rooms, :integrations, :jsonb, null: false, default: {} + end +end diff --git a/db/migrate/20220821001724_add_lock_version_to_integrations.rb b/db/migrate/20220821001724_add_lock_version_to_integrations.rb new file mode 100644 index 000000000..842c72b2f --- /dev/null +++ b/db/migrate/20220821001724_add_lock_version_to_integrations.rb @@ -0,0 +1,5 @@ +class AddLockVersionToIntegrations < ActiveRecord::Migration[6.1] + def change + add_column :integrations, :lock_version, :integer, default: 0 + end +end diff --git a/db/structure.sql b/db/structure.sql index 34656c4e2..4d3757d1c 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -616,7 +616,8 @@ END) STORED, attendance_type character varying(200) DEFAULT NULL::character varying, twelve_hour boolean DEFAULT true, timezone character varying(500) DEFAULT NULL::character varying, - availability_notes character varying + availability_notes character varying, + integrations jsonb DEFAULT '{}'::jsonb NOT NULL ); @@ -926,6 +927,36 @@ CREATE TABLE public.ignored_conflicts ( ); +-- +-- Name: integration_publishes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.integration_publishes ( + id uuid DEFAULT public.gen_random_uuid() NOT NULL, + integration_name character varying, + data jsonb DEFAULT '{}'::jsonb NOT NULL, + started_at timestamp without time zone, + completed_at timestamp without time zone, + created_by character varying, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: integrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.integrations ( + id uuid DEFAULT public.gen_random_uuid() NOT NULL, + name character varying, + config jsonb DEFAULT '{}'::jsonb NOT NULL, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + lock_version integer DEFAULT 0 +); + + -- -- Name: label_dimensions; Type: TABLE; Schema: public; Owner: - -- @@ -1351,7 +1382,9 @@ CREATE TABLE public.publication_dates ( dropped_sessions integer DEFAULT 0, new_assignments integer DEFAULT 0, updated_assignments integer DEFAULT 0, - dropped_assignments integer DEFAULT 0 + dropped_assignments integer DEFAULT 0, + sent_external boolean DEFAULT false NOT NULL, + lock_version integer DEFAULT 0 ); @@ -1396,7 +1429,8 @@ CREATE TABLE public.published_session_assignments ( session_assignment_role_type_id uuid NOT NULL, person_id uuid NOT NULL, sort_order integer, - visibility public.visibility_enum DEFAULT 'public'::public.visibility_enum + visibility public.visibility_enum DEFAULT 'public'::public.visibility_enum, + integrations jsonb DEFAULT '{}'::jsonb NOT NULL ); @@ -1425,7 +1459,8 @@ CREATE TABLE public.published_sessions ( environment public.session_environments_enum DEFAULT 'unknown'::public.session_environments_enum, minors_participation jsonb, recorded boolean DEFAULT false NOT NULL, - streamed boolean DEFAULT false NOT NULL + streamed boolean DEFAULT false NOT NULL, + integrations jsonb DEFAULT '{}'::jsonb NOT NULL ); @@ -1451,7 +1486,8 @@ CREATE TABLE public.rooms ( room_set_id uuid, length numeric, width numeric, - height numeric + height numeric, + integrations jsonb DEFAULT '{}'::jsonb NOT NULL ); @@ -2210,6 +2246,22 @@ ALTER TABLE ONLY public.ignored_conflicts ADD CONSTRAINT ignored_conflicts_pkey PRIMARY KEY (id); +-- +-- Name: integration_publishes integration_publishes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.integration_publishes + ADD CONSTRAINT integration_publishes_pkey PRIMARY KEY (id); + + +-- +-- Name: integrations integrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.integrations + ADD CONSTRAINT integrations_pkey PRIMARY KEY (id); + + -- -- Name: label_dimensions label_dimensions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3373,6 +3425,9 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220726130346'), ('20220801152151'), ('20220801173704'), -('20220801195644'); +('20220801195644'), +('20220818022629'), +('20220818200500'), +('20220821001724'); diff --git a/lib/tasks/chicon_airmeet.rake b/lib/tasks/chicon_airmeet.rake new file mode 100644 index 000000000..a9d5de663 --- /dev/null +++ b/lib/tasks/chicon_airmeet.rake @@ -0,0 +1,37 @@ +namespace :chicon do + desc "Configure airmeet for chicon" + task configure_airmeet_test: :environment do + integration = Integration.find_or_create_by({name: :airmeet}) + # this data is for testing. we are going to need the real information for the actual show here at some point. + # and/or create a ui for it but let's try this first. + # none of this gets you actual access to the airmeet so it's probably ok to actually check in??? + integration.update!({ + config: { + airmeet_id: "ac4b0bc0-1079-11ed-bed5-3112cc3e0b52", + airmeet_host: "gail.terman@chicon.org", + active: true + } + }) + + # Room.find_by({name: "Airmeet 1"}).update({integrations: {airmeet: {room_host_email: 'gail.terman@chicon.org'}}}) + end + + task configure_airmeet: :environment do + integration = Integration.find_or_create_by({name: :airmeet}) + # this data is for testing. we are going to need the real information for the actual show here at some point. + # and/or create a ui for it but let's try this first. + # none of this gets you actual access to the airmeet so it's probably ok to actually check in??? + integration.update!({ + config: { + airmeet_id: "NOT SET YET", + airmeet_host: "NOT SET YET", + active: false + } + }) + end + + task sync_airmeet: :environment do + AirmeetApiService.sync_to_airmeet + end +end + diff --git a/lib/tasks/rbac.rake b/lib/tasks/rbac.rake index 8763a1d43..4ca88238c 100644 --- a/lib/tasks/rbac.rake +++ b/lib/tasks/rbac.rake @@ -281,6 +281,9 @@ namespace :rbac do }, "publication_date": { "index": false + }, + "integration": { + "publish": false } }) end @@ -590,6 +593,9 @@ namespace :rbac do }, "publication_date": { "index": true + }, + "integration": { + "publish": false } }) end @@ -899,6 +905,9 @@ namespace :rbac do }, "publication_date": { "index": true + }, + "integration": { + "publish": true } }) end diff --git a/spec/helpers/integrations_helper_spec.rb b/spec/helpers/integrations_helper_spec.rb new file mode 100644 index 000000000..c39979c7b --- /dev/null +++ b/spec/helpers/integrations_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the IntegrationsHelper. For example: +# +# describe IntegrationsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe IntegrationsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb new file mode 100644 index 000000000..aae8d31da --- /dev/null +++ b/spec/models/integration_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Integration, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/integrations_spec.rb b/spec/requests/integrations_spec.rb new file mode 100644 index 000000000..5426a96db --- /dev/null +++ b/spec/requests/integrations_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe "Integrations", type: :request do + describe "GET /index" do + pending "add some examples (or delete) #{__FILE__}" + end +end