Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/deseng751: Added external link model, service, and swagger definitions. Updated search service. #83

Merged
merged 2 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 1.11.0 Jan 9, 2025
* Added external link model, service, and swagger definitions. Modified search service. [DESENG-751](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-751)

### 1.10.0 Nov 26, 2024
* Modified project definition to accomodate shape file colours. [DESENG-743](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-743)
* Modified project definition to accomodate project type multiselect. [DESENG-745](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-745)
Expand Down
279 changes: 279 additions & 0 deletions api/controllers/externalLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
const { remove, indexOf, assignIn } = require('lodash');
const defaultLog = require('winston').loggers.get('defaultLog');
const mongoose = require('mongoose');
const Actions = require('../helpers/actions');
const Utils = require('../helpers/utils');

const getSanitizedFields = (fields) => {
return remove(fields, (f) => {
return (indexOf([
'_addedBy',
'project',
'displayName',
'externalLink',
'section',
'dateAdded',
'dateUpdated',
'description',
'projectPhase',
'checkbox',
'read',
], f) !== -1);
});
};

exports.protectedOptions = (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED OPTIONS');
res.status(200).send();
};

exports.publicGet = async (args, res) => {
defaultLog.info('EXTERNAL LINK PUBLIC GET');
// Build match query if on exLinkId route
let query = {};

if (args.swagger.params.exLinkId?.value) {
query = Utils.buildQuery("_id", args.swagger.params.exLinkId.value, query);
} else if (args.swagger.params.exLinkIds?.value?.length > 0) {
query = Utils.buildQuery("_id", args.swagger.params.exLinkIds.value, query);
}

if (args.swagger.params.project?.value) {
query = Utils.buildQuery("project", args.swagger.params.project.value, query);
}

// Set query type
assignIn(query, { "_schemaName": "ExternalLink" });

try {
const data = await Utils.runDataQuery(
'ExternalLink',
['public'],
null,
query,
getSanitizedFields(args.swagger.params.fields.value), // Fields
null, // sort warmup
null, // sort
null, // skip
null, // limit
false); // count
defaultLog.info('Got external link file(s):', data);
Utils.recordAction('Get', 'ExternalLink', 'public', args.swagger.params.exLinkId?.value || null);
return Actions.sendResponse(res, 200, data);
} catch (e) {
defaultLog.error(e);
return Actions.sendResponse(res, 400, e);
}
};

exports.protectedHead = (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED HEAD');
// Build match query if on exLinkId route
let query = {};
if (args.swagger.params.exLinkId?.value) {
query = Utils.buildQuery("_id", args.swagger.params.exLinkId.value, query);
}
if (args.swagger.params._application?.value) {
query = Utils.buildQuery('_application', args.swagger.params._application.value, query);
}
if (args.swagger.params._comment?.value) {
query = Utils.buildQuery('_comment', args.swagger.params._comment.value, query);
}
// Set query type
assignIn(query, { "_schemaName": "ExternalLink" });

Utils.runDataQuery('ExternalLink',
args.swagger.params.auth_payload.client_roles,
args.swagger.params.auth_payload.idir_user_guid,
query,
['_id',
'read'], // Fields
null, // sort warmup
null, // sort
null, // skip
null, // limit
true) // count
.then((data) => {
Utils.recordAction('Head', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, args.swagger.params.exLinkId?.value || null);
if (!(args.swagger.params.exLinkId && args.swagger.params.exLinkId.value) || (data && data.length > 0)) {
res.setHeader('x-total-count', data?.length > 0 ? data[0].total_items : 0);
return Actions.sendResponse(res, 200, data);
} else {
return Actions.sendResponse(res, 404, data);
}
});
}

exports.protectedGet = async (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED GET');
let query = {}, sort = {}, skip = null, limit = null, count = false;

// Build match query if on exLinkId route
if (args.swagger.params.exLinkId?.value) {
assignIn(query, { _id: mongoose.Types.ObjectId(args.swagger.params.exLinkId.value) });
} else if (args.swagger.params.exLinkIds?.value?.length > 0) {
query = Utils.buildQuery("_id", args.swagger.params.exLinkIds.value);
}
if (args.swagger.params.project?.value) {
query = Utils.buildQuery("project", args.swagger.params.project.value, query);
}
// Set query type
assignIn(query, { "_schemaName": "ExternalLink" });

try {
const data = await Utils.runDataQuery('ExternalLink',
args.swagger.params.auth_payload.client_roles,
args.swagger.params.auth_payload.idir_user_guid,
query,
getSanitizedFields(args.swagger.params.fields.value), // Fields
null, // sort warmup
sort, // sort
skip, // skip
limit, // limit
count); // count
Utils.recordAction('Get', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, args.swagger.params.exLinkId?.value || null);
defaultLog.info('Got external file(s):', data);
return Actions.sendResponse(res, 200, data);
} catch (e) {
defaultLog.error(e);
return Actions.sendResponse(res, 400, e);
}
};

exports.protectedPost = async (args, res, next) => {
defaultLog.info('EXTERNAL LINK PROTECTED POST');
try {
const project = args.swagger.params.project?.value;
defaultLog.info('Section value:', args.swagger.params.section?.value);
Promise.resolve()
.then(async () => {
const ExternalLink = mongoose.model('ExternalLink');
const extLink = new ExternalLink();
// Define security tag defaults
extLink.read = ['sysadmin', 'staff'];
extLink.write = ['sysadmin', 'staff'];
extLink.delete = ['sysadmin', 'staff'];

// Map the form values
extLink.project = mongoose.Types.ObjectId(project);
extLink._addedBy = args.swagger.params.auth_payload.preferred_username;
extLink._createdDate = new Date();
extLink.displayName = args.swagger.params.displayName.value;
extLink.externalLink = args.swagger.params.externalLink.value;
extLink.section = args.swagger.params.section?.value;
extLink.dateAdded = args.swagger.params.dateAdded.value;
extLink.dateUpdated = args.swagger.params.dateUpdated.value;
extLink.description = args.swagger.params.description.value;
extLink.projectPhase = args.swagger.params.projectPhase.value;
extLink.checkbox = 'true' === args.body.checkbox ? true : false;
extLink.save()
.then((exl) => {
Utils.recordAction('Post', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, exl._id);
return Actions.sendResponse(res, 200, exl);
})
.catch((error) => {
defaultLog.error(error);
return Actions.sendResponse(res, 400, error);
});
})
.catch(error => defaultLog.error(error));
} catch (e) {
defaultLog.error(e);
// Delete the path details before we return to the caller.
delete e['path'];
return Actions.sendResponse(res, 500, e);
}
};

exports.protectedPublish = async (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED PUBLISH');
const objId = args.swagger.params.exLinkId.value;
defaultLog.info("Publish External Link:", objId);

const ExternalLink = require('mongoose').model('ExternalLink');
try {
const exLink = await ExternalLink.findOne({ _id: objId });
if (exLink) {
defaultLog.info("External Link:", exLink);
const published = await Actions.publish(await exLink.save());
Utils.recordAction('Publish', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, objId);
return Actions.sendResponse(res, 200, published);
} else {
defaultLog.info("Couldn't find that external link!");
return Actions.sendResponse(res, 404, e);
}
} catch (e) {
return Actions.sendResponse(res, 400, e);
}
};

exports.protectedUnPublish = async (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED UNPUBLISH');
const objId = args.swagger.params.exLinkId.value;
defaultLog.info("Unpublish External Link:", objId);
const ExternalLink = require('mongoose').model('ExternalLink');
try {
const exLink = await ExternalLink.findOne({ _id: objId });
if (exLink) {
const unPublished = await Actions.unPublish(await exLink.save());
Utils.recordAction('Unpublish', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, objId);
defaultLog.info("Published external link:", objId);
return Actions.sendResponse(res, 200, unPublished);
} else {
defaultLog.info("Couldn't find that external link!");
return Actions.sendResponse(res, 404, e);
}
} catch (e) {
defaultLog.error(e);
return Actions.sendResponse(res, 400, e);
}
};

exports.protectedPut = async (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED PUT');
const objId = args.swagger.params.exLinkId.value;
let obj = {};
defaultLog.info('Put external link:', objId);

obj._updatedBy = args.swagger.params.auth_payload.preferred_username;
obj.displayName = args.swagger.params.displayName.value;
obj.externalLink = args.swagger.params.externalLink.value;
obj.section = args.swagger.params.section.value;
obj.projectPhase = args.swagger.params.projectPhase.value;
obj.dateAdded = args.swagger.params.dateAdded.value;
obj.dateUpdated = args.swagger.params.dateUpdated.value;
obj.description = args.swagger.params.description.value;
obj.section = "null" === args.swagger.params.section.value ? null : args.swagger.params.section.value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would do the same thing. There may be an even shorter way to do it

Suggested change
obj.section = "null" === args.swagger.params.section.value ? null : args.swagger.params.section.value;
obj.section = args.swagger.params.section.value ? args.swagger.params.section.value : null;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't think it would work in this situation, since we're looking for a string with the text "null".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right! I remember that sections can be "null" sometimes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I believe it's because everything has to be stringified for the API request and then converted back

const ExternalLink = mongoose.model('ExternalLink');

try {
const exLink = await ExternalLink.findOneAndUpdate({ _id: objId }, obj, { upsert: false, new: true });
if (exLink) {
Utils.recordAction('put', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, objId);
defaultLog.info('External link updated:', objId);
return Actions.sendResponse(res, 200, exLink);
} else {
defaultLog.info("Couldn't find that external link!");
return Actions.sendResponse(res, 404, {});
}
} catch (e) {
defaultLog.error(e);
return Actions.sendResponse(res, 400, e);
}
}

exports.protectedDelete = async (args, res) => {
defaultLog.info('EXTERNAL LINK PROTECTED DELETE');
const objId = args.swagger.params.exLinkId.value;
defaultLog.info("Delete External Link:", objId);
const ExternalLink = require('mongoose').model('ExternalLink');

try {
await ExternalLink.findOneAndRemove({ _id: objId });
Utils.recordAction('Delete', 'ExternalLink', args.swagger.params.auth_payload.preferred_username, objId);
return Actions.sendResponse(res, 200, {});
} catch (e) {
defaultLog.error("Error:", e);
return Actions.sendResponse(res, 400, e);
}
};
17 changes: 7 additions & 10 deletions api/controllers/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,17 +411,14 @@ var executeQuery = async function (args, res, next) {
});

if (dataset !== 'Item') {
var data = await searchCollection(roles, userProjectPermissions, keywords, dataset, pageNum, pageSize, project, sortField, sortDirection, caseSensitive, populate, and, or)
if (dataset === 'Comment') {
// Filter
each(data[0].searchResults, function (item) {
if (item.isAnonymous === true) {
delete item.author;
}
});
}
var data = await searchCollection(roles, userProjectPermissions, keywords, dataset, pageNum, pageSize, project, sortField, sortDirection, caseSensitive, populate, and, or);
// Filter
each(data[0].searchResults, function (item) {
if (item.isAnonymous === true) {
delete item.author;
}
});
return Actions.sendResponse(res, 200, data);

} else if (dataset === 'Item') {

var collectionObj = mongoose.model(args.swagger.params._schemaName.value);
Expand Down
22 changes: 22 additions & 0 deletions api/helpers/models/externalLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = require('../models')('ExternalLink', {

_createdDate: { type: Date, default: Date.now() },
_updatedDate: { type: Date, default: Date.now() },
_addedBy: { type: String, default: 'system' },
_updatedBy: { type: String, default: 'system' },
_deletedBy: { type: String, default: 'system' },

read: [{ type: String, trim: true, default: 'sysadmin' }],
write: [{ type: String, trim: true, default: 'sysadmin' }],
delete: [{ type: String, trim: true, default: 'sysadmin' }],

project: { type: 'ObjectId', ref: 'Project', default: null },
section: { type: 'ObjectId', ref: 'DocumentSection', default: null } | null,
displayName: { type: String, default:'' },
externalLink: { type: String, default: '' },
dateAdded: { type: Date, default: Date.now() },
dateUpdated: { type: Date, default: Date.now() },
description: { type: String, default: '' },
projectPhase: { type: String, default: '' },
checkbox: { type: Boolean, default: false },
}, 'lup');
Loading
Loading