factored KaleidoscopeEvent out of UserNotification

master
rob 2 years ago
parent b9c7090dbb
commit fc7550e196

@ -0,0 +1,53 @@
// kaleidoscope-event.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const KaleidoscopeEventSchema = new Schema({
created: { type: Date, default: Date.now, required: true, index: -1, expires: '30d' },
action: { type: String, required: true, lowercase: true },
label: { type: String },
content: { type: String },
href: { type: String },
source: {
pkg: {
name: { type: String, required: true },
version: { type: String, required: true },
},
site: {
name: { type: String, required: true },
description: { type: String },
domain: { type: String, lowercase: true, required: true },
domainKey: { type: String, lowercase: true, required: true },
company: { type: String },
coreAuth: {
scopes: { type: [String] },
},
},
author: {
userId: { type: Schema.ObjectId, required: true },
displayName: { type: String },
username: { type: String },
href: { type: String },
},
},
attachmentType: { type: String },
attachment: { type: Schema.ObjectId, refPath: 'attachmentType' },
});
/*
* Compound index for site.domainKey, author.userId for when local members
* subscribe to a specific author on a domain, or the whole domain.
*/
KaleidoscopeEventSchema.index({
'source.site.domainKey': 1,
'source.author.userId': 1,
}, {
name: 'evtsrc_site_author_index',
});
module.exports = mongoose.model('KaleidoscopeEvent', KaleidoscopeEventSchema);

@ -7,38 +7,15 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
/*
* A notification is really just a user-specific bookmark to an event we know
* the user is interested in. These are the "timelines" presented to members.
*/
const UserNotificationSchema = new Schema({
created: { type: Date, default: Date.now, required: true, index: -1, expires: '7d' },
user: { type: Schema.ObjectId, required: true, index: 1, ref: 'User' },
status: { type: String, enum: ['new', 'seen'], default: 'new', required: true },
action: { type: String, required: true, lowercase: true },
label: { type: String },
content: { type: String },
href: { type: String },
source: {
pkg: {
name: { type: String, required: true },
version: { type: String, required: true },
},
site: {
name: { type: String, required: true },
description: { type: String },
domain: { type: String, lowercase: true, required: true },
domainKey: { type: String, lowercase: true, required: true },
company: { type: String },
coreAuth: {
scopes: { type: [String] },
},
},
author: {
userId: { type: Schema.ObjectId, required: true },
displayName: { type: String },
username: { type: String },
href: { type: String },
},
},
attachmentType: { type: String },
attachment: { type: Schema.ObjectId, refPath: 'attachmentType' },
event: { type: Schema.ObjectId, required: true, ref: 'KaleidoscopeEvent' },
});
module.exports = mongoose.model('UserNotification', UserNotificationSchema);

@ -7,8 +7,10 @@
const mongoose = require('mongoose');
const UserSubscription = mongoose.model('UserSubscription');
const KaleidoscopeEvent = mongoose.model('KaleidoscopeEvent');
const slug = require('slug');
const striptags = require('striptags');
const { SiteService, SiteError } = require('../../lib/site-lib');
@ -72,21 +74,22 @@ class HiveService extends SiteService {
await this.resolver.add('resolve-link', jobData);
}
async processKaleidoscopeEvent (event) {
async processKaleidoscopeEvent (eventDefinition) {
const {
userNotification: userNotificationService,
oauth2: oauth2Service,
} = this.dtp.services;
const client = await oauth2Service.getClientByDomainKey(event.source.site.domainKey);
const client = await oauth2Service.getClientByDomainKey(eventDefinition.source.site.domainKey);
if (!client) {
throw new SiteError(403, 'Unknown client domain key');
}
const event = await this.createKaleidoscopeEvent(eventDefinition);
await UserSubscription
.find({
'subscriptions.client': client._id,
'subscriptions.emitterId': event.source.emitter._id,
'subscriptions.emitterId': eventDefinition.source.emitter._id,
})
.select('-subscriptions')
.cursor()
@ -96,6 +99,137 @@ class HiveService extends SiteService {
this.emit('kaleidoscope:event', event, client);
}
async createKaleidoscopeEvent (eventDefinition) {
const NOW = new Date();
/*
* Validate general notification data
*/
if (!eventDefinition.action) {
throw new SiteError(406, 'Missing action');
}
if (!eventDefinition.content) {
throw new SiteError(406, 'Missing content');
}
if (!eventDefinition.href) {
throw new SiteError(406, 'Missing href');
}
/*
* Validate source data
*/
if (!eventDefinition.source) {
throw new SiteError(406, 'Missing source information');
}
/*
* Validate source site
*/
if (!eventDefinition.source.site) {
throw new SiteError(406, 'Missing source site information');
}
if (!eventDefinition.source.site.name) {
throw new SiteError(406, 'Missing source site name');
}
if (!eventDefinition.source.site.description) {
throw new SiteError(406, 'Missing source site description');
}
if (!eventDefinition.source.site.domain) {
throw new SiteError(406, 'Missing source site domain');
}
if (!eventDefinition.source.site.domainKey) {
throw new SiteError(406, 'Missing source site domain key');
}
if (!eventDefinition.source.site.company) {
throw new SiteError(406, 'Missing source site company name');
}
if (!eventDefinition.source.site.coreAuth ||
!eventDefinition.source.site.coreAuth.scopes ||
!Array.isArray(eventDefinition.source.site.coreAuth.scopes) ||
(eventDefinition.source.site.coreAuth.scopes.length === 0)) {
throw new SiteError(406, 'Missing source site Core auth or scope information');
}
/*
* Validate source package
*/
if (!eventDefinition.source.pkg) {
throw new SiteError(406, 'Missing source package information');
}
if (!eventDefinition.source.pkg.name) {
throw new SiteError(406, 'Missing source package name');
}
if (!eventDefinition.source.pkg.version) {
throw new SiteError(406, 'Missing source package version');
}
/*
* Validate source emitter
*/
if (!eventDefinition.source.emitter) {
throw new SiteError(406, 'Missing source emitter information');
}
if (!eventDefinition.source.emitter.emitterId) {
throw new SiteError(406, 'Missing source emitter ID');
}
if (!eventDefinition.source.emitter.username) {
throw new SiteError(406, 'Missing source emitter username');
}
if (!eventDefinition.source.emitter.href) {
throw new SiteError(406, 'Missing source emitter href');
}
const event = new KaleidoscopeEvent();
event.created = NOW;
event.action = striptags(eventDefinition.action.trim().toLowerCase());
if (eventDefinition.label) {
event.label = striptags(eventDefinition.label.trim());
}
event.content = striptags(eventDefinition.content.trim());
event.href = striptags(eventDefinition.href.trim());
event.source = {
pkg: {
name: striptags(eventDefinition.source.pkt.name.trim().toLowerCase()),
version: striptags(eventDefinition.source.pkt.version.trim()),
},
site: {
name: striptags(eventDefinition.source.site.name.trim()),
domain: striptags(eventDefinition.source.site.domain.trim().toLowerCase()),
domainKey: striptags(eventDefinition.source.site.domainKey.trim().toLowerCase()),
coreAuth: {
scopes: eventDefinition.source.site.coreAuth.scopes.map((scope) => scope.trim().toLowerCase()),
},
},
emitter: {
emitterId: mongoose.Types.ObjectId(eventDefinition.source.emitter.emitterId),
username: striptags(eventDefinition.source.emitter.username.trim()),
href: striptags(eventDefinition.source.emitter.href.trim()),
},
};
if (eventDefinition.source.site.company) {
event.source.site.company = striptags(eventDefinition.source.site.company.trim());
}
if (eventDefinition.source.site.description) {
event.source.site.description = striptags(eventDefinition.source.site.description.trim());
}
if (eventDefinition.source.emitter.displayName) {
event.source.emitter.displayName = striptags(eventDefinition.source.emitter.displayName);
}
event.attachmentType = eventDefinition.attachmentType;
event.attachment = eventDefinition.attachment;
}
}
module.exports = {

@ -10,9 +10,8 @@ const mongoose = require('mongoose');
const UserNotification = mongoose.model('UserNotification');
const pug = require('pug');
const striptags = require('striptags');
const { SiteService, SiteError } = require('../../lib/site-lib');
const { SiteService } = require('../../lib/site-lib');
class UserNotificationService extends SiteService {
@ -34,137 +33,12 @@ class UserNotificationService extends SiteService {
this.templates.notification = pug.compileFile(path.join(this.dtp.config.root, 'app', 'views', 'notification', 'components', 'notification-standalone.pug'));
}
async create (user, notificationDefinition) {
const NOW = new Date();
/*
* Validate general notification data
*/
if (!notificationDefinition.action) {
throw new SiteError(406, 'Missing action');
}
if (!notificationDefinition.content) {
throw new SiteError(406, 'Missing content');
}
if (!notificationDefinition.href) {
throw new SiteError(406, 'Missing href');
}
/*
* Validate source data
*/
if (!notificationDefinition.source) {
throw new SiteError(406, 'Missing source information');
}
/*
* Validate source site
*/
if (!notificationDefinition.source.site) {
throw new SiteError(406, 'Missing source site information');
}
if (!notificationDefinition.source.site.name) {
throw new SiteError(406, 'Missing source site name');
}
if (!notificationDefinition.source.site.description) {
throw new SiteError(406, 'Missing source site description');
}
if (!notificationDefinition.source.site.domain) {
throw new SiteError(406, 'Missing source site domain');
}
if (!notificationDefinition.source.site.domainKey) {
throw new SiteError(406, 'Missing source site domain key');
}
if (!notificationDefinition.source.site.company) {
throw new SiteError(406, 'Missing source site company name');
}
if (!notificationDefinition.source.site.coreAuth ||
!notificationDefinition.source.site.coreAuth.scopes ||
!Array.isArray(notificationDefinition.source.site.coreAuth.scopes) ||
(notificationDefinition.source.site.coreAuth.scopes.length === 0)) {
throw new SiteError(406, 'Missing source site Core auth or scope information');
}
/*
* Validate source package
*/
if (!notificationDefinition.source.pkg) {
throw new SiteError(406, 'Missing source package information');
}
if (!notificationDefinition.source.pkg.name) {
throw new SiteError(406, 'Missing source package name');
}
if (!notificationDefinition.source.pkg.version) {
throw new SiteError(406, 'Missing source package version');
}
/*
* Validate source emitter
*/
if (!notificationDefinition.source.emitter) {
throw new SiteError(406, 'Missing source emitter information');
}
if (!notificationDefinition.source.emitter.emitterId) {
throw new SiteError(406, 'Missing source emitter ID');
}
if (!notificationDefinition.source.emitter.username) {
throw new SiteError(406, 'Missing source emitter username');
}
if (!notificationDefinition.source.emitter.href) {
throw new SiteError(406, 'Missing source emitter href');
}
async create (user, event) {
const notification = new UserNotification();
notification.created = NOW;
notification.created = event.created;
notification.user = user._id;
notification.status = 'new';
notification.action = striptags(notificationDefinition.action.trim().toLowerCase());
if (notificationDefinition.label) {
notification.label = striptags(notificationDefinition.label.trim());
}
notification.content = striptags(notificationDefinition.content.trim());
notification.href = striptags(notificationDefinition.href.trim());
notification.source = {
pkg: {
name: striptags(notificationDefinition.source.pkt.name.trim().toLowerCase()),
version: striptags(notificationDefinition.source.pkt.version.trim()),
},
site: {
name: striptags(notificationDefinition.source.site.name.trim()),
domain: striptags(notificationDefinition.source.site.domain.trim().toLowerCase()),
domainKey: striptags(notificationDefinition.source.site.domainKey.trim().toLowerCase()),
coreAuth: {
scopes: notificationDefinition.source.site.coreAuth.scopes.map((scope) => scope.trim().toLowerCase()),
},
},
emitter: {
emitterId: mongoose.Types.ObjectId(notificationDefinition.source.emitter.emitterId),
username: striptags(notificationDefinition.source.emitter.username.trim()),
href: striptags(notificationDefinition.source.emitter.href.trim()),
},
};
if (notificationDefinition.source.site.company) {
notification.source.site.company = striptags(notificationDefinition.source.site.company.trim());
}
if (notificationDefinition.source.site.description) {
notification.source.site.description = striptags(notificationDefinition.source.site.description.trim());
}
if (notificationDefinition.source.emitter.displayName) {
notification.source.emitter.displayName = striptags(notificationDefinition.source.emitter.displayName);
}
notification.attachmentType = notificationDefinition.attachmentType;
notification.attachment = notificationDefinition.attachment;
notification.event = event._id;
await notification.save();
return notification.toObject();

@ -1,5 +1,5 @@
mixin renderUserNotification (userNotification)
div
div= moment(userNotification.created).fromNow()
div= userNotification.source
div= userNotification.message
div= userNotification.event.source
div= userNotification.event.message
Loading…
Cancel
Save