Skip to content

Commit

Permalink
Add-ons (#34)
Browse files Browse the repository at this point in the history
* Added code to save add-on actions

* Updated lesson feed to output addons

* Fixed file upload for non-bundles

* Switch to public addon feed

* Updated db table scripts
  • Loading branch information
jzongker authored Jun 15, 2024
1 parent 46eb5f9 commit 9e1a0f7
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 33 deletions.
9 changes: 8 additions & 1 deletion src/controllers/AddOnController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import { Environment } from "../helpers";
@controller("/addOns")
export class AddOnController extends LessonsBaseController {

@httpGet("/public")
public async loadPublic(req: express.Request<{}, {}, null>, res: express.Response): Promise<interfaces.IHttpActionResult> {
return this.actionWrapperAnon(req, res, async () => {
return await this.repositories.addOn.loadPublic();
});
}

@httpGet("/:id")
public async get(@requestParam("id") id: string, req: express.Request<{}, {}, null>, res: express.Response): Promise<interfaces.IHttpActionResult> {
return this.actionWrapper(req, res, async () => {
Expand Down Expand Up @@ -36,7 +43,7 @@ export class AddOnController extends LessonsBaseController {
const a = addOn;
const saveFunction = async () => {
if (a.image && a.image.startsWith("data:image/")) {
if (!a.id) await this.repositories.lesson.save(a); // save first to generate an id
if (!a.id) await this.repositories.addOn.save(a); // save first to generate an id
await this.saveImage(a);
}
return await this.repositories.addOn.save(a);
Expand Down
17 changes: 13 additions & 4 deletions src/controllers/FileController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,22 @@ export class FileController extends LessonsBaseController {


private async saveFile(file: File) {
const resource = await this.repositories.resource.load(file.churchId, file.resourceId);
const bundle = await this.repositories.bundle.load(file.churchId, resource.bundleId);
const key = "/files/" + bundle.contentType + "/" + bundle.contentId + "/" + resource.id + "/" + file.fileName;
let path = "";

if (file.resourceId)
{
const resource = await this.repositories.resource.load(file.churchId, file.resourceId);
const bundle = await this.repositories.bundle.load(file.churchId, resource.bundleId);
path = "/files/" + bundle.contentType + "/" + bundle.contentId + "/" + resource.id + "/"
} else {
path = "/files/" + file.contentType + "/" + file.contentId + "/";
}
const key = path + file.fileName;

if (file.id) // delete existing uploadFile
{
const existingFile = await this.repositories.file.load(file.churchId, file.id)
const oldKey = "/files/" + bundle.contentType + "/" + bundle.contentId + "/" + resource.id + "/" + existingFile.fileName;
const oldKey = path + existingFile.fileName;
if (oldKey !== key) await FileStorageHelper.remove(oldKey);
}

Expand Down
2 changes: 1 addition & 1 deletion src/controllers/LessonController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class LessonController extends LessonsBaseController {

const venues:FeedVenue[] = [];
data.venues.forEach(v => {
venues.push(LessonFeedHelper.convertToFeed(data.lesson, data.study, data.program, v, data.bundles, data.resources, data.externalVideos));
venues.push(LessonFeedHelper.convertToFeed(data.lesson, data.study, data.program, v, data.bundles, data.resources, data.externalVideos, data.addOns));
});
const result = { venues }
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/VenueController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class VenueController extends LessonsBaseController {

const expandedVenue = ArrayHelper.getOne(data.venues, "id", venue.id);

const result = await LessonFeedHelper.convertToFeed(data.lesson, data.study, data.program, expandedVenue, data.bundles, data.resources, data.externalVideos);
const result = await LessonFeedHelper.convertToFeed(data.lesson, data.study, data.program, expandedVenue, data.bundles, data.resources, data.externalVideos, data.addOns);
return result;
});
}
Expand Down
47 changes: 40 additions & 7 deletions src/helpers/LessonFeedHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FeedAction, FeedDownload, FeedFile, FeedSection, FeedVenue } from "../models/feed";
import { Action, Asset, Bundle, ExternalVideo, File, Lesson, Program, Resource, Role, Section, Study, Variant, Venue } from "../models";
import { Action, AddOn, Asset, Bundle, ExternalVideo, File, Lesson, Program, Resource, Role, Section, Study, Variant, Venue } from "../models";
import { ArrayHelper } from "@churchapps/apihelper";
import { Repositories } from "../repositories";

Expand All @@ -11,15 +11,17 @@ export class LessonFeedHelper {
let venues: Venue[] = null;
let bundles: Bundle[] = null;
let resources: Resource[] = null;
let addOns: AddOn[] = null;
let externalVideos: ExternalVideo[] = null;
const promises: Promise<any>[] = [];
promises.push(this.getVenues(lesson.id).then(v => venues = v));
promises.push(this.getBundles(lesson.id).then(b => bundles = b));
promises.push(this.getResources(lesson.id).then(r => resources = r));
promises.push(Repositories.getCurrent().externalVideo.loadPublicForLesson(lesson.id).then(ev => externalVideos = ev));;
promises.push(this.getAddOns(lesson.id).then(a => addOns = a));
promises.push(Repositories.getCurrent().externalVideo.loadPublicForLesson(lesson.id).then(ev => externalVideos = ev));
await Promise.all(promises);

const result = { lesson, study, program, venues, bundles, resources, externalVideos }
const result = { lesson, study, program, venues, bundles, resources, externalVideos, addOns }
return result;
}

Expand Down Expand Up @@ -60,6 +62,15 @@ export class LessonFeedHelper {
return resources;
}

private static async getAddOns(lessonId: string) {
const addOns: AddOn[] = await Repositories.getCurrent().addOn.loadPublicForLesson(lessonId);
if (addOns.length === 0) return addOns;
const fileIds = ArrayHelper.getIds(addOns, "fileId");
const files = await Repositories.getCurrent().file.loadPublicByIds(fileIds);

addOns.forEach(a => { a.file = ArrayHelper.getOne(files, "id", a.fileId); });
return addOns;
}

private static async appendSections(venue: Venue, allSections: Section[], allRoles: Role[], allActions: Action[]) {
venue.sections = ArrayHelper.getAll(allSections, "venueId", venue.id);
Expand All @@ -81,7 +92,7 @@ export class LessonFeedHelper {

}

static convertToFeed(lesson:Lesson, study:Study, program:Program, venue:Venue, bundles:Bundle[], resources:Resource[], externalVideos:ExternalVideo[]) {
static convertToFeed(lesson:Lesson, study:Study, program:Program, venue:Venue, bundles:Bundle[], resources:Resource[], externalVideos:ExternalVideo[], addOns:AddOn[]) {
const result:FeedVenue = {
name: venue.name,
id: venue.id,
Expand Down Expand Up @@ -125,7 +136,8 @@ export class LessonFeedHelper {
lastRole = role.name;
fa.role = role.name;
}
if (fa.actionType==="play") fa.files = this.convertFiles(action, bundles, resources, externalVideos, false);
if (fa.actionType==="play" || fa.actionType==="add-on") fa.files = this.convertFiles(action, bundles, resources, externalVideos, addOns, false);
if (fa.actionType==="add-on") fa.actionType = "play";
fs.actions.push(fa);
}
});
Expand Down Expand Up @@ -167,14 +179,23 @@ export class LessonFeedHelper {

}

private static convertFiles(action: Action, bundles:Bundle[], resources:Resource[], externalVideos:ExternalVideo[], download:boolean) {
private static convertFiles(action: Action, bundles:Bundle[], resources:Resource[], externalVideos:ExternalVideo[], addOns:AddOn[], download:boolean) {
const result:FeedFile[] = [];
const video: ExternalVideo = ArrayHelper.getOne(externalVideos || [], "id", action.externalVideoId);
const externalVideoId = action.externalVideoId;


const resource: Resource = ArrayHelper.getOne(resources || [], "id", action.resourceId);
const asset = (action.assetId && resource) ? ArrayHelper.getOne(resource?.assets || [], "id", action.assetId) : null;
const addOn:AddOn = ArrayHelper.getOne(addOns || [], "id", action.addOnId);
if (addOn) {
// externalVideoId = addOn.externalVideoId;
}
const video: ExternalVideo = ArrayHelper.getOne(externalVideos || [], "id", externalVideoId);

if (asset) result.push(this.convertAssetFile(asset, resource, download));
else if (resource) this.convertResourceFiles(resource, download).forEach(f => result.push(f));
else if (video) result.push(this.convertVideoFile(video, download));
else if (addOn) result.push(this.convertAddOnFile(addOn));
return result;
}

Expand Down Expand Up @@ -211,6 +232,18 @@ export class LessonFeedHelper {
return file;
}

private static convertAddOnFile(addOn:AddOn) {
const file:FeedFile = {
url: addOn?.file?.contentPath,
name: addOn?.name,
bytes: addOn?.file?.size,
fileType: addOn?.file?.fileType,
id: addOn?.file?.id
}
if (addOn?.image) file.thumbnail = addOn.image;
return file;
}

private static convertResourceFiles(resource:Resource, download:boolean) {
const result:FeedFile[] = [];
if (download)
Expand Down
1 change: 1 addition & 0 deletions src/models/Action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export class Action {
public resourceId: string;
public assetId: string;
public externalVideoId: string;
public addOnId?: string;
}
4 changes: 4 additions & 0 deletions src/models/AddOn.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { File } from "./File";

export class AddOn {
public id?: string;
public churchId?: string;
Expand All @@ -7,4 +9,6 @@ export class AddOn {
public image?: string;
public addOnType?: string;
public fileId?: string;

public file?: File;
}
2 changes: 2 additions & 0 deletions src/models/File.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class File {
public dateModified?: Date;
public seconds?: number;
public thumbPath?: string;
public contentType?: string;
public contentId?: string;

public resourceId?: string; // doesn't get saved, but determines the file path.
public fileContents?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/repositories/ActionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ export class ActionRepository {

public async create(action: Action) {
action.id = UniqueIdHelper.shortId();
const sql = "INSERT INTO actions (id, churchId, lessonId, roleId, actionType, content, sort, resourceId, assetId, externalVideoId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
const params = [action.id, action.churchId, action.lessonId, action.roleId, action.actionType, action.content, action.sort, action.resourceId, action.assetId, action.externalVideoId];
const sql = "INSERT INTO actions (id, churchId, lessonId, roleId, actionType, content, sort, resourceId, assetId, externalVideoId, addOnId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
const params = [action.id, action.churchId, action.lessonId, action.roleId, action.actionType, action.content, action.sort, action.resourceId, action.assetId, action.externalVideoId, action.addOnId];
await DB.query(sql, params);
return action;
}

public async update(action: Action) {
const sql = "UPDATE actions SET lessonId=?, roleId=?, actionType=?, content=?, sort=?, resourceId=?, assetId=?, externalVideoId=? WHERE id=? AND churchId=?";
const params = [action.lessonId, action.roleId, action.actionType, action.content, action.sort, action.resourceId, action.assetId, action.externalVideoId, action.id, action.churchId];
const sql = "UPDATE actions SET lessonId=?, roleId=?, actionType=?, content=?, sort=?, resourceId=?, assetId=?, externalVideoId=?, addOnId=? WHERE id=? AND churchId=?";
const params = [action.lessonId, action.roleId, action.actionType, action.content, action.sort, action.resourceId, action.assetId, action.externalVideoId, action.addOnId, action.id, action.churchId];
await DB.query(sql, params);
return action;
}
Expand Down
8 changes: 8 additions & 0 deletions src/repositories/AddOnRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@ export class AddOnRepository {
return addOn;
}

public loadPublic(): Promise<AddOn[]> {
return DB.query("SELECT * FROM addOns order by category, name", []);
}

public loadAll(churchId: string): Promise<AddOn[]> {
return DB.query("SELECT * FROM addOns WHERE churchId=? order by category, name", [churchId]);
}

public loadPublicForLesson(lessonId: string): Promise<AddOn[]> {
return DB.query("SELECT * FROM addOns WHERE id in (SELECT addOnId from actions WHERE lessonId=?)", [lessonId]);
}

public load(id: string): Promise<AddOn> {
return DB.queryOne("SELECT * FROM addOns WHERE id=?", [id]);
}
Expand Down
5 changes: 5 additions & 0 deletions src/repositories/FileRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export class FileRepository {
return DB.queryOne("SELECT * FROM files WHERE id=? AND churchId=?", [id, churchId]);
}

public loadPublicByIds(ids: string[]): Promise<File[]> {
const sql = "SELECT * FROM files WHERE id IN (" + ArrayHelper.fillArray("?", ids.length) + ")";
return DB.query(sql, ids);
}

public loadByIds(churchId: string, ids: string[]): Promise<File[]> {
const sql = "SELECT * FROM files WHERE churchId=? AND id IN (" + ArrayHelper.fillArray("?", ids.length) + ")";
return DB.query(sql, [churchId].concat(ids));
Expand Down
17 changes: 8 additions & 9 deletions tools/dbScripts/actions.mysql
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
DROP TABLE IF EXISTS `actions`;

CREATE TABLE `actions` (
`id` char(11) NOT NULL,
`churchId` char(11) DEFAULT NULL,
`lessonId` char(11) DEFAULT NULL,
`roleId` char(11) DEFAULT NULL,
`id` char(11) CHARACTER SET latin1 NOT NULL,
`churchId` char(11) CHARACTER SET latin1 DEFAULT NULL,
`lessonId` char(11) CHARACTER SET latin1 DEFAULT NULL,
`roleId` char(11) CHARACTER SET latin1 DEFAULT NULL,
`actionType` varchar(45) DEFAULT NULL,
`content` text,
`sort` int(11) DEFAULT NULL,
`resourceId` char(11) DEFAULT NULL,
`assetId` char(11) DEFAULT NULL,
`resourceId` char(11) CHARACTER SET latin1 DEFAULT NULL,
`assetId` char(11) CHARACTER SET latin1 DEFAULT NULL,
`externalVideoId` char(11) DEFAULT NULL,
`addOnId` char(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
);
11 changes: 11 additions & 0 deletions tools/dbScripts/addOns.mysql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE `addOns` (
`id` char(11) NOT NULL,
`churchId` char(11) DEFAULT NULL,
`providerId` char(11) DEFAULT NULL,
`category` varchar(45) DEFAULT NULL,
`name` varchar(45) DEFAULT NULL,
`image` varchar(1000) DEFAULT NULL,
`addOnType` varchar(45) DEFAULT NULL,
`fileId` char(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
11 changes: 5 additions & 6 deletions tools/dbScripts/files.mysql
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
DROP TABLE IF EXISTS `files`;

CREATE TABLE `files` (
`id` char(11) NOT NULL,
`churchId` char(11) DEFAULT NULL,
`id` char(11) CHARACTER SET latin1 NOT NULL,
`churchId` char(11) CHARACTER SET latin1 DEFAULT NULL,
`fileName` varchar(255) DEFAULT NULL,
`contentPath` varchar(1024) DEFAULT NULL,
`fileType` varchar(45) DEFAULT NULL,
`size` int(11) DEFAULT NULL,
`dateModified` datetime DEFAULT NULL,
`filePath` varchar(1024) DEFAULT NULL,
`seconds` int(11) DEFAULT NULL,
`thumbPath` varchar(1024) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
);
1 change: 1 addition & 0 deletions tools/initdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const init = async () => {

const tables: { title: string, file: string }[] = [
{ title: "Actions", file: "actions.mysql" },
{ title: "Add-ons", file: "addOns.mysql" },
{ title: "Assets", file: "assets.mysql" },
{ title: "Bundles", file: "bundles.mysql" },
{ title: "Classrooms", file: "classrooms.mysql" },
Expand Down

0 comments on commit 9e1a0f7

Please sign in to comment.