Page MenuHomeSealhub

No OneTemporary

diff --git a/src/subject/attachments/attachment-assigner.ts b/src/subject/attachments/attachment-assigner.ts
index a06c5dc6..35dfb015 100644
--- a/src/subject/attachments/attachment-assigner.ts
+++ b/src/subject/attachments/attachment-assigner.ts
@@ -1,45 +1,40 @@
import App from "../../app/app";
import Context from "../../context";
import * as Errors from "../../response/errors";
import Collection from "../../chip-types/collection";
-import { LooseObject } from "../types";
-
-export type AssignAttachmentsResult = {
- attachments: LooseObject;
- fieldsWithAttachments: LooseObject;
-};
+import { AttachmentParams, AssignAttachmentsResult } from "../types";
export default async function(
app: App,
context: Context,
- params: any,
+ params: AttachmentParams,
root_collection: Collection,
documents: Array<any>
): Promise<AssignAttachmentsResult> {
const attachments_query = params.attachments;
const attachments = {};
const fieldsWithAttachments = {};
for (let field_name of Object.keys(attachments_query)) {
const field = root_collection.fields[field_name];
if (!field) {
throw new Errors.NotFound(
`Given field ${field_name} is not declared in collection!`
);
}
const loader = await field.get_attachment_loader(context, field_name, {
...field.params,
attachments_query: attachments_query[field_name],
documents,
});
if (!loader) {
throw new Errors.FieldDoesNotSupportAttachments(
`Given field ${field_name} does not support attachments!`
);
}
await loader.loadTo(app, attachments, fieldsWithAttachments);
}
return { attachments, fieldsWithAttachments };
}
diff --git a/src/subject/attachments/index.js b/src/subject/attachments/index.js
deleted file mode 100644
index 69e7119c..00000000
--- a/src/subject/attachments/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- ReferenceToCollection: require("./reference-to-collection.js").default,
-};
diff --git a/src/subject/predefined-subjects/users-subject.ts b/src/subject/predefined-subjects/users-subject.ts
index db266d2e..5f6ae45d 100644
--- a/src/subject/predefined-subjects/users-subject.ts
+++ b/src/subject/predefined-subjects/users-subject.ts
@@ -1,67 +1,65 @@
import Subject from "../subject.js";
import * as Errors from "../../response/errors";
import me_synonyms from "../../misc/me-synonyms";
import MeSubject from "./me-subject";
import SuperContext from "../../super-context.js";
import App from "../../app/app.js";
import Context from "../../context.js";
import { CreateActionName, ShowActionName } from "../../action.js";
export default class UsersSubject extends Subject {
getName = () => "users";
- performAction(
+ async performAction(
context: Context,
action_name: CreateActionName | ShowActionName,
params: any
) {
params = params || {};
switch (action_name) {
case "create":
return this.app.run_action(
context,
["collections", "users"],
"create",
params
);
case "show":
return this.app.run_action(
context,
["collections", "users"],
"show",
params
);
default:
- return Promise.reject(
- new Errors.BadSubjectAction(
- `Unknown/unsupported action for UsersSubject: '${action_name}'`
- )
+ throw new Errors.BadSubjectAction(
+ `Unknown/unsupported action for UsersSubject: '${action_name}'`
);
}
}
async getChildSubject(path_element: string) {
if (me_synonyms.indexOf(path_element) !== -1) {
return new MeSubject(this.app);
}
const username = path_element;
const response = await this.app.run_action(
new SuperContext(),
["collections", "users"],
"show",
{
filter: { username: username },
}
);
if (response.empty) {
throw new Errors.BadSubjectPath(`Unknown username: '${username}'`);
}
return this.app.RootSubject.getSubject([
"collections",
"users",
response.id,
]);
}
}
diff --git a/src/subject/subject-types/_batch_action.ts b/src/subject/subject-types/_batch_action.ts
new file mode 100644
index 00000000..95549f45
--- /dev/null
+++ b/src/subject/subject-types/_batch_action.ts
@@ -0,0 +1,78 @@
+import App from "../../app/app";
+import Context from "../../context";
+import ArrayCartesian from "../../utils/array-cartesian.js";
+import PromiseIterateGenerator from "../../utils/promise-iterate-generator.js";
+import { LooseObject } from "../types";
+
+type BatchActionBatchType = "batch";
+type BatchActionCartesianType = "cartesian";
+
+export type BatchActionParams = {
+ __multiple: boolean;
+ mode: BatchActionBatchType | BatchActionCartesianType;
+ sources: Array<Array<any>>;
+};
+
+export default async function batchAction(
+ app: App,
+ context: Context,
+ params: BatchActionParams,
+ callback: Function
+) {
+ // callback is a function that will be called for each instance infered from the description, taking context and inferred body as arguments
+ const mode = params.mode || "batch"; // can be: "cartesian" or "batch";
+ if (mode === "batch") {
+ throw new Error("BATCH mode not implemented yet");
+ }
+ if (mode !== "cartesian") {
+ throw new Error(`Incorrect mode: ${mode}`);
+ }
+
+ const field_names: Array<string> = [];
+ const possible_field_values: Array<any> = [];
+ const to_await = [];
+ for (const source of params.sources) {
+ if (source[0] === "literal") {
+ for (const field_name in source[1]) {
+ field_names.push(field_name);
+ possible_field_values.push([source[1][field_name]]);
+ }
+ } else if (source[0] === "collection_fields") {
+ const collection_name = source[1].collection;
+ const filter = source[1].filter || {};
+ const fields = source[1].fields || [];
+ const map_to = source[1].map_to;
+ const promise = app
+ .run_action(context, ["collections", collection_name], "show", {
+ filter,
+ })
+ .then(function(sealious_response) {
+ for (const i in map_to) {
+ const field_in_collection = fields[i];
+ const field_name = map_to[i];
+ field_names.push(field_name);
+ possible_field_values.push(
+ sealious_response.items.map(
+ (resource: LooseObject) => {
+ return resource[field_in_collection];
+ }
+ )
+ );
+ }
+ });
+ to_await.push(promise);
+ }
+ }
+ await Promise.all(to_await);
+
+ return PromiseIterateGenerator(
+ new ArrayCartesian(possible_field_values),
+ function(values: Array<any>) {
+ const body: LooseObject = {};
+ for (let i = 0; i < field_names.length; ++i) {
+ body[field_names[i]] = values[i];
+ }
+ return callback(context, body);
+ }
+ );
+}
diff --git a/src/subject/subject-types/collection-field-subject.ts b/src/subject/subject-types/collection-field-subject.ts
index 9fb4e8cf..ec42ab0b 100644
--- a/src/subject/subject-types/collection-field-subject.ts
+++ b/src/subject/subject-types/collection-field-subject.ts
@@ -1,52 +1,59 @@
-import Promise from "bluebird";
-
-import Subject from "../subject.js";
-import Errors from "../../response/error.js";
-import Collection from "../../chip-types/collection.js";
-import { ActionName } from "../../action.js";
-import Context from "../../context.js";
+import App from "../../app/app";
+import Subject from "../subject";
+import * as Errors from "../../response/errors";
+import Collection from "../../chip-types/collection";
+import { ActionName } from "../../action";
+import Context from "../../context";
+import { LooseObject } from "../types";
export default class CollectionFieldSubject extends Subject {
name: string;
collection: Collection;
field_name: string;
- field_type: string;
+ field_type: LooseObject;
resource_id: string;
+
constructor(
+ app: App,
collection: Collection,
resource_id: string,
field_name: string
) {
- super();
+ super(app);
this.name = "CollectionFieldSubject";
this.collection = collection;
this.resource_id = resource_id;
this.field_name = field_name;
this.field_type = collection[field_name].type;
}
- perform_action(context: Context, action_name: ActionName, params: any) {
+ async performAction(
+ context: Context,
+ action_name: ActionName,
+ params: any
+ ) {
params = params || {};
params = {
resource_id: this.resource_id,
field_name: this.field_name,
collection: this.collection,
...params,
};
if (this.field_type.actions[action_name]) {
return Promise.resolve(
this.field_type.actions[action_name](context, params)
);
} else {
throw new Errors.DeveloperError(`Unknown action: '${action_name}'`);
}
}
- get_child_subject(key) {
- const self = this;
- return Promise.try(function () {
- return self.field_type.get_child_subject(key);
- });
+ async getChildSubject(path_element: string) {
+ return this.field_type.get_child_subject(path_element);
+ }
+
+ getName() {
+ return "CollectionFieldSubject";
}
}
diff --git a/src/subject/subject-types/collection-subject.ts b/src/subject/subject-types/collection-subject.ts
index e963f1b0..5a8f9581 100644
--- a/src/subject/subject-types/collection-subject.ts
+++ b/src/subject/subject-types/collection-subject.ts
@@ -1,381 +1,372 @@
import assert from "assert";
import clone from "clone";
import shortid from "shortid";
-import SingleItemResponse from "../../../common_lib/response/single-item-response.js";
+import SingleItemResponse from "../../../common_lib/response/single-item-response";
import Collection from "../../chip-types/collection";
import * as Errors from "../../response/errors";
import assignAttachments from "../attachments/attachment-assigner";
-import Subject from "../subject.js";
-import SingleResource from "./single-resource-subject.js";
-import batch_action from "./_batch_action.js";
+import Subject from "../subject";
+import Context from "../../context";
+import SingleResource from "./single-resource-subject";
+import batchAction, { BatchActionParams } from "./_batch_action";
+import { ResourceCreated } from "../../../common_lib/response/responses";
+import {
+ LooseObject,
+ AttachmentParams,
+ AssignAttachmentsResult,
+} from "../types";
+import Item from "../../../common_lib/response/item";
+import CollectionResponse from "../../../common_lib/response/collection-response";
+import {
+ CreateActionName,
+ ShowActionName,
+ DeleteActionName,
+} from "../../action";
//
//
//
//
// !!! many of these methods should be moved to the collection
-// !!! class, this should be a lightweigt controller
+// !!! class, this should be a lightweight controller
//
//
//
//
export default class CollectionSubject extends Subject {
collection: Collection;
- getName = () => "Collection";
- constructor(collection: Collection, ids = []) {
+ named_filters: Array<string>;
+ ids: Array<string>;
+
+ getName() {
+ return "Collection";
+ }
+ constructor(
+ collection: Collection,
+ named_filters: Array<string> = [],
+ ids: Array<string> = []
+ ) {
super(collection.app);
this.collection = collection;
+ this.named_filters = named_filters;
+ this.ids = ids;
+ }
- this.list_resources = async function(context, params) {
- const result = await CollectionSubject.prototype.__list_resources(
- app.Datastore,
- this.collection,
- context,
- params,
- named_filters,
- ids
- );
-
- if (params.attachments && typeof params.attachments === "object") {
- Object.assign(
- result,
- await assignAttachments(
- app,
+ performAction(
+ context: Context,
+ action_name: CreateActionName | ShowActionName | DeleteActionName,
+ params: LooseObject
+ ) {
+ switch (action_name) {
+ case "create":
+ if (params.__multiple) {
+ return this.createMany(
context,
- params,
- this.collection,
- result.items
- )
+ params as BatchActionParams
+ );
+ } else {
+ return this.createResource(context, params);
+ }
+ case "show":
+ return this.listResources(context, params);
+ case "delete":
+ return this.delete(context, params as BatchActionParams);
+ default:
+ throw new Errors.DeveloperError(
+ `Unknown action for '${this.collection.name}' collection: '${action_name}'`
);
- }
- await CollectionSubject.prototype.__exclude_items_not_passing_check(
- context,
- this.collection,
- result
- );
- return new app.Sealious.Responses.CollectionResponse(result);
- };
+ }
+ }
- this.create_many = function(context, params) {
- // test with: http -f POST localhost:8081/api/v1/collections/animals __multiple=true mode=cartesian sources[0][0]=literal sources[0][1][pole]=wartosc sources[0][1][bark]=bork sources[1][0]=collection_fields sources[1][1][collection]=shelters sources[1][1][filter][city]=Poznań sources[1][1][fields][0]=id
+ createMany(context: Context, params: BatchActionParams) {
+ // test with: http -f POST localhost:8081/api/v1/collections/animals __multiple=true mode=cartesian sources[0][0]=literal sources[0][1][pole]=wartosc sources[0][1][bark]=bork sources[1][0]=collection_fields sources[1][1][collection]=shelters sources[1][1][filter][city]=Poznań sources[1][1][fields][0]=id
- /*
+ /*
params.mode = "batch" | "cartesian"
for "cartesian" mode:
* 'sources' - a list of value sources to generate the cartesian product
Each source is a 2-element array, where the first element is the source type, and the second one is the params
source types:
* 'literal': second element needs to be a key->value map
* `collection_fields`: values from a collection
* collection
* filter
* fields[]
* map_to[]
*/
- const self = this;
- return batch_action(app, context, params, function(context, body) {
- return app.run_action(
+ return batchAction(
+ this.app,
+ context,
+ params,
+ (context: Context, body: Item) =>
+ this.app.run_action(
context,
- ["collections", self.collection.name],
+ ["collections", this.collection.name],
"create",
body
- );
- });
- };
+ )
+ );
+ }
- this.delete_many = function(context, params) {
- const self = this;
- return batch_action(app, context, params, function(context, body) {
- return app
+ createResource(context: Context, body: Item) {
+ return this.__createResource(context, body);
+ }
+
+ deleteMany(context: Context, params: BatchActionParams) {
+ return batchAction(
+ this.app,
+ context,
+ params,
+ async (context: Context, body: Item) => {
+ const items = await this.app
.run_action(
context,
- ["collections", self.collection.name],
+ ["collections", this.collection.name],
"show",
{
filter: body,
}
)
- .then(sealious_response => sealious_response.items)
- .each(function(resource) {
- return app.run_action(
+ .then(sealious_response => sealious_response.items);
+
+ const results = [];
+ for (let item of items) {
+ results.push(
+ await this.app.run_action(
context,
- ["collections", self.collection.name, resource.id],
+ ["collections", this.collection.name, item.id],
"delete"
- );
- });
- });
- };
-
- this.delete = function(context, params) {
- if (params.__multiple) {
- return this.delete_many(context, params);
- } else {
- throw new app.Sealious.Errors.NotFound(
- "Cannot delete a collection. Try using the '__multiple: true' mode"
- );
+ )
+ );
+ }
+ return results;
}
- };
+ );
+ }
- this.get_child_subject = async function(key) {
- if (key[0] === "@") {
- return new CollectionSubject(app, collection, [
- ...named_filters,
- key.slice(1),
- ]);
- } else if (key instanceof Array) {
- const ids = key;
- return new CollectionSubject(app, collection, [], ids);
- }
- const resource_id = key;
- return new SingleResource(app, this.collection, resource_id);
- };
+ delete(context: Context, params: BatchActionParams) {
+ if (params.__multiple) {
+ return this.deleteMany(context, params);
+ }
+
+ throw new Errors.NotFound(
+ "Cannot delete a collection. Try using the '__multiple: true' mode"
+ );
}
-}
-CollectionSubject.prototype = Object.create(Subject.prototype);
-
-CollectionSubject.prototype.__create_resource = function(
- datastore,
- collection,
- context,
- body
-) {
- return collection
- .check_if_action_is_allowed(context, "create", body)
- .then(function() {
- return collection.validate_field_values(context, true, body);
- })
- .then(function() {
- return collection.encode_field_values(context, body);
- })
- .then(function(encoded_body) {
- const newID = shortid();
- const resource_data = {
- _metadata: {
- collection: collection.name,
- created_context: context,
- last_modified_context: context,
- },
- sealious_id: newID,
- ...encoded_body,
- };
- return datastore.insert(collection.name, resource_data, {});
- })
- .then(function(database_entry) {
- return collection.get_resource_representation(
- context,
- database_entry
+ async getChildSubject(path_element: string | Array<string>) {
+ if (path_element[0] === "@") {
+ return new CollectionSubject(this.collection, [
+ ...this.named_filters,
+ path_element.slice(1) as string,
+ ]);
+ } else if (path_element instanceof Array) {
+ const ids = path_element;
+ return new CollectionSubject(this.collection, [], ids);
+ }
+ const resource_id = path_element;
+ return new SingleResource(this.app, this.collection, resource_id);
+ }
+
+ async listResources(context: Context, params: LooseObject) {
+ const result = await this.__listResources(context, params);
+
+ if (params.attachments && typeof params.attachments === "object") {
+ Object.assign(
+ result,
+ await assignAttachments(
+ this.app,
+ context,
+ params as AttachmentParams,
+ this.collection,
+ result.items
+ )
);
- })
- .then(function(representation) {
- return new Sealious.Responses.ResourceCreated(representation);
- });
-};
+ }
+ await this.__excludeItemsNotPassingCheck(
+ context,
+ this.collection,
+ result
+ );
+ return new CollectionResponse(result);
+ }
-CollectionSubject.prototype.__preprocess_resource_filter = function(
- collection,
- context,
- filter
-) {
- filter = clone(filter) || {};
- const expanded_filter = expandHash(filter);
- const processed_filter = {};
- for (const field_name in expanded_filter) {
- if (!collection.fields[field_name]) {
- continue;
+ async __listResources(context: Context, params: LooseObject) {
+ if (params === undefined || params === null) {
+ params = {};
}
- const field = collection.fields[field_name];
- const field_filter = expanded_filter[field_name];
- if (field_filter instanceof Array) {
- processed_filter[field_name] = Promise.all(
- field_filter.map(field.encode.bind(field, context))
- ).then(filters => {
- return { $in: filters };
- });
- } else {
- processed_filter[field_name] = field.filter_to_query(
- context,
- field_filter
- );
+
+ if (params.calculate === "false" || params.calculate === false) {
+ params.calculate = false;
+ } else if (typeof params.calculate !== "object") {
+ params.calculate = true;
}
+
+ await this.collection.checkIfActionIsAllowed(context, "show", {});
+ const aggregation_stages = await this.collection.getAggregationStages(
+ context,
+ "show",
+ params,
+ this.named_filters,
+ this.ids
+ );
+
+ const output_options = getOutputOptions(this.collection, params);
+
+ const documents = await this.app.Datastore.aggregate(
+ this.collection.name,
+ aggregation_stages,
+ {},
+ output_options
+ );
+
+ const decoded_items = [];
+
+ for (let document of documents) {
+ try {
+ let item = await this.collection.getResourceRepresentation(
+ context,
+ document,
+ params.format,
+ params.calculate
+ );
+ decoded_items.push(item);
+ } catch (e) {}
+ }
+
+ return {
+ items: decoded_items,
+ attachments: {},
+ fieldsWithAttachments: {},
+ };
}
- return Promise.props(processed_filter);
-};
+
+ async __createResource(context: Context, body: Item) {
+ await this.collection.checkIfActionIsAllowed(context, "create", body);
+ await this.collection.validateFieldValues(context, true, body);
+ const encoded_body = await this.collection.encodeFieldValues(
+ context,
+ body
+ );
+
+ const newID = shortid();
+ const resource_data = {
+ _metadata: {
+ collection: this.collection.name,
+ created_context: context,
+ last_modified_context: context,
+ },
+ sealious_id: newID,
+ ...encoded_body,
+ };
+ const database_entry = await this.app.Datastore.insert(
+ this.collection.name,
+ resource_data,
+ {}
+ );
+ const representation = await this.collection.getResourceRepresentation(
+ this.context,
+ database_entry
+ );
+ return new ResourceCreated(representation);
+ }
+
+ async __excludeItemsNotPassingCheck(
+ context: Context,
+ collection: Collection,
+ result: AssignAttachmentsResult
+ ) {
+ const access_strategy = collection.getAccessStrategy("show");
+ const is_item_sensitive = await access_strategy.isItemSensitive();
+ if (!is_item_sensitive) {
+ return;
+ }
+ result.items = await Promise.filter(result.items, async item => {
+ try {
+ await access_strategy.check(
+ context,
+ new SingleItemResponse({
+ item,
+ attachments: result.attachments,
+ fieldsWithAttachments: result.fieldsWithAttachments,
+ })
+ );
+ return true;
+ } catch (e) {
+ console.error(e);
+ return false;
+ }
+ });
+ }
+}
const sealious_to_mongo_sort_param = {
desc: -1,
descending: -1,
asc: 1,
ascending: 1,
};
-const get_output_options = function(collection, params) {
+const getOutputOptions = function(collection: Collection, params: LooseObject) {
const output_options = {};
if (params.pagination) {
const default_pagination_params = {
page: 1,
items: 10,
};
- const full_pagination_params = merge(
+ const full_pagination_params = Object.assign(
+ {},
default_pagination_params,
params.pagination
);
const must_be_int = ["items", "page"];
must_be_int.forEach(function(attribute_name) {
- if (isNaN(parseInt(full_pagination_params[attribute_name]))) {
+ if (isNaN(parseInt(full_pagination_params[attribute_name], 10))) {
full_pagination_params[attribute_name] =
default_pagination_params[attribute_name];
} else {
full_pagination_params[attribute_name] = parseInt(
full_pagination_params[attribute_name]
);
}
});
output_options.skip =
(full_pagination_params.page - 1) * full_pagination_params.items;
output_options.amount =
- parseInt(full_pagination_params.items) +
- (parseInt(full_pagination_params.forward_buffer) || 0);
+ parseInt(full_pagination_params.items, 10) +
+ (parseInt(full_pagination_params.forward_buffer, 10) || 0);
} else {
if (params.skip) {
output_options.skip = parseInt(params.skip);
}
if (params.amount) {
output_options.amount = parseInt(params.count);
}
}
if (params.sort) {
const full_sort_params = clone(params.sort);
for (const field_name in full_sort_params) {
const mongo_sort_param =
sealious_to_mongo_sort_param[full_sort_params[field_name]];
if (!mongo_sort_param) {
const available_sort_keys = Object.keys(
sealious_to_mongo_sort_param
).join(", ");
throw new Errors.BadSubjectAction(
`Unknown sort key: ${full_sort_params[field_name]}. Available sort keys are: ${available_sort_keys}.`
);
}
full_sort_params[field_name] = mongo_sort_param;
}
output_options.sort = full_sort_params;
}
return output_options;
};
-
-CollectionSubject.prototype.__list_resources = async function(
- datastore,
- collection,
- context,
- params,
- named_filters,
- ids
-) {
- if (params === undefined || params === null) {
- params = {};
- }
-
- if (params.calculate === "false" || params.calculate === false) {
- params.calculate = false;
- } else if (typeof params.calculate !== "object") {
- params.calculate = true;
- }
-
- await collection.check_if_action_is_allowed(context, "show");
- const aggregation_stages = await collection.get_aggregation_stages(
- context,
- "show",
- params,
- named_filters,
- ids
- );
-
- const output_options = get_output_options(this.collection, params);
-
- const documents = await datastore.aggregate(
- collection.name,
- aggregation_stages,
- {},
- output_options
- );
-
- const decoded_items = [];
-
- for (let document of documents) {
- try {
- let item = await collection.get_resource_representation(
- context,
- document,
- params.format,
- params.calculate
- );
- decoded_items.push(item);
- } catch (e) {}
- }
-
- return { items: decoded_items, attachments: {} };
-};
-
-CollectionSubject.prototype.__exclude_items_not_passing_check = async function(
- context,
- collection,
- result
-) {
- const access_strategy = collection.get_access_strategy("show");
- const is_item_sensitive = await access_strategy.is_item_sensitive();
- if (!is_item_sensitive) {
- return;
- }
- result.items = await Promise.filter(result.items, async item => {
- try {
- await access_strategy.check(
- context,
- new SingleItemResponse({
- item,
- attachments: result.attachments,
- fieldsWithAttachments: result.fieldsWithAttachments,
- })
- );
- return true;
- } catch (e) {
- console.error(e);
- return false;
- }
- });
-};
-
-CollectionSubject.prototype.perform_action = function(
- context,
- action_name,
- args
-) {
- switch (action_name) {
- case "create":
- if (args.__multiple) {
- return this.create_many(context, args);
- } else {
- return this.create_resource(context, args);
- }
- case "show":
- return this.list_resources(context, args);
- case "delete":
- return this.delete(context, args);
- default:
- throw new Errors.DeveloperError(
- `Unknown action for '${this.collection.name}' collection: '${action_name}'`
- );
- }
-};
-
-CollectionSubject.subject_name = "collection";
-
-module.exports = CollectionSubject;
diff --git a/src/subject/subject-types/current-session-subject.js b/src/subject/subject-types/current-session-subject.js
deleted file mode 100644
index 86daa1a2..00000000
--- a/src/subject/subject-types/current-session-subject.js
+++ /dev/null
@@ -1,69 +0,0 @@
-const Promise = require("bluebird");
-
-const Response = require("../../response/response.js");
-const Errors = require("../../response/error.js");
-const Subject = require("../subject.js");
-const SuperContext = require("../../super-context.js");
-
-const CurrentSession = function (app) {
- this.app = app;
-};
-
-CurrentSession.prototype = Object.create(Subject.prototype);
-
-CurrentSession.prototype.perform_action = async function (
- context,
- action_name,
- args
-) {
- if (action_name !== "delete") {
- throw new Errors.DeveloperError(
- `Unknown action ${action_name} for CurrentSession subject.`
- );
- }
- try {
- const session_sealious_response = await this.app.run_action(
- new SuperContext(),
- ["collections", "sessions"],
- "show",
- {
- filter: { "session-id": context.session_id },
- }
- );
-
- await Promise.map(session_sealious_response.items, (session) =>
- this.app.run_action(
- new SuperContext(),
- ["collections", "sessions", session.id],
- "delete"
- )
- );
-
- const anonymous_session_sealious_response = await this.app.run_action(
- new SuperContext(),
- ["collections", "anonymous-sessions"],
- "show",
- {
- filter: {
- "anonymous-session-id": context.anonymous_session_id,
- },
- }
- );
-
- await Promise.map(
- anonymous_session_sealious_response.items,
- (session) =>
- this.app.run_action(
- new SuperContext(),
- ["collections", "anonymous-sessions", session.id],
- "delete"
- )
- );
-
- return new Response({}, false, "logged_out", "You've been logged out");
- } catch (e) {
- return Promise.reject(new Errors.BadContext("Invalid session id!"));
- }
-};
-
-module.exports = CurrentSession;
diff --git a/src/subject/subject-types/current-session-subject.ts b/src/subject/subject-types/current-session-subject.ts
new file mode 100644
index 00000000..f632e246
--- /dev/null
+++ b/src/subject/subject-types/current-session-subject.ts
@@ -0,0 +1,75 @@
+import App from "../../app/app";
+import { LeafSubject } from "../subject.js";
+import * as Errors from "../../response/errors";
+import Context from "../../context";
+import { DeleteActionName } from "../../action";
+import Response from "../../response/response";
+import SuperContext from "../../super-context";
+import Item from "../../../common_lib/response/item";
+
+export default class CurrentSession extends LeafSubject {
+ async performAction(
+ context: Context,
+ action_name: DeleteActionName,
+ params: any
+ ) {
+ if (action_name !== "delete") {
+ throw new Errors.DeveloperError(
+ `Unknown action ${action_name} for CurrentSession subject.`
+ );
+ }
+ try {
+ const session_sealious_response = await this.app.run_action(
+ new SuperContext(),
+ ["collections", "sessions"],
+ "show",
+ {
+ filter: { "session-id": context.session_id },
+ }
+ );
+
+ await Promise.all(
+ session_sealious_response.items.map((session: Item) =>
+ this.app.run_action(
+ new SuperContext(),
+ ["collections", "sessions", session.id],
+ "delete"
+ )
+ )
+ );
+
+ const anonymous_session_sealious_response = await this.app.run_action(
+ new SuperContext(),
+ ["collections", "anonymous-sessions"],
+ "show",
+ {
+ filter: {
+ "anonymous-session-id": context.anonymous_session_id,
+ },
+ }
+ );
+
+ await Promise.all(
+ anonymous_session_sealious_response.items.map((session: Item) =>
+ this.app.run_action(
+ new SuperContext(),
+ ["collections", "anonymous-sessions", session.id],
+ "delete"
+ )
+ )
+ );
+
+ return new Response(
+ {},
+ false,
+ "logged_out",
+ "You've been logged out"
+ );
+ } catch (e) {
+ return Promise.reject(new Errors.BadContext("Invalid session id!"));
+ }
+ }
+ getName() {
+ return "CurrentSession";
+ }
+}
diff --git a/src/subject/subject-types/image-format/image-format.js b/src/subject/subject-types/image-format/image-format.js
deleted file mode 100644
index 4ae6ee96..00000000
--- a/src/subject/subject-types/image-format/image-format.js
+++ /dev/null
@@ -1,179 +0,0 @@
-const locreq = require("locreq")(__dirname);
-const Subject = require("../../subject.js");
-const Errors = require("../../../response/error.js");
-const { promisify } = require("util");
-const sharp = require("sharp");
-const fs = require("fs");
-
-const QUALITY = 80;
-
-function format_hash(format_obj) {
- return format_obj.size[0] + ":" + format_obj.size[1] + "(" + QUALITY + ")";
-}
-
-function format_filename(original_filename, format_name) {
- return (
- original_filename.split(".").slice(0, -1).join(".") +
- "-" +
- format_name +
- ".jpg"
- );
-}
-
-const ImageFormat = function (app, file_id, format_name) {
- this.name = "ImageFormats";
- this.file_id = file_id;
-
- function get_hdd_path(file_id) {
- return locreq.resolve(app.FileManager.upload_path + "/" + file_id);
- }
-
- function create_formatted_version(file_id, format_name) {
- const format_obj = app.ConfigManager.get("image_formats")[format_name];
-
- return app.Datastore.find("files", { id: file_id }).then(function (
- matches
- ) {
- const original_file = matches[0];
- const file_path = get_hdd_path(original_file.id);
- const width = format_obj.size[0];
- const height = format_obj.size[1];
- const filename = format_filename(
- original_file.original_name,
- format_name
- );
-
- const temp_file_path =
- "/tmp/" + Math.floor(Math.random() * Math.pow(10, 7)) + ".jpg";
-
- return sharp(file_path)
- .resize(width, height)
- .toFile(temp_file_path)
- .then(function () {
- return promisify(fs.readFile)(temp_file_path);
- })
- .then(function (buffer) {
- return app.FileManager.save_file(
- new app.Sealious.File(
- new app.Sealious.SuperContext(),
- filename,
- buffer
- )
- );
- })
- .then(function (sealious_file) {
- return app
- .run_action(
- new app.Sealious.SuperContext(),
- ["collections", "formatted-images"],
- "create",
- {
- original_photo_file: original_file.id,
- formatted_photo_file: sealious_file.id,
- format: format_hash(format_obj),
- }
- )
- .then(() => sealious_file);
- })
- .then(function (file) {
- return promisify(fs.unlink)(temp_file_path).then(
- () => file
- );
- });
- });
- }
-
- function get_formatted_version(
- file_id,
- file_name,
- format_name,
- format_obj
- ) {
- const random = Math.random();
- const hash = format_hash(format_obj);
- return app.Datastore.aggregate("formatted-images", [
- {
- $match: {
- original_photo_file: { $eq: file_id },
- },
- },
- {
- $match: {
- $or: [{ "format.original": hash }, { "format.safe": hash }],
- },
- },
- ]).then(function (results) {
- return (
- results[0] &&
- results[0] && {
- id: results[0].formatted_photo_file,
- original_name: format_filename(file_name, format_name),
- }
- );
- });
- }
-
- const ImageFormatFile = function (file_id, format_name, file_name) {
- this.name = "SingleFile";
- this.file_id = file_id;
- this.file_name = file_name;
-
- ImageFormatFile.prototype.perform_action = function (
- context,
- action_name,
- args
- ) {
- switch (action_name) {
- case "show":
- const format_obj =
- app.ConfigManager.get("image_formats")[format_name] ||
- null;
-
- if (!format_obj) {
- throw new Errors.BadSubjectPath(
- "Unknown image format: " + format_name
- );
- }
-
- return get_formatted_version(
- file_id,
- file_name,
- format_name,
- format_obj
- )
- .then(function (result) {
- if (result !== undefined) {
- return result;
- } else {
- return create_formatted_version(
- file_id,
- format_name
- );
- }
- })
- .then((file_description) => {
- let ret = new app.Sealious.File.from_db_entry(
- file_description
- );
- ret.path_on_hdd = get_hdd_path(file_description.id);
- ret.mime = "image/jpeg";
- return ret;
- });
- default:
- throw new Errors.DeveloperError(
- `Unknown action for '${this.collection.name}' subject: '${action_name}'`
- );
- }
- };
- };
-
- ImageFormatFile.prototype = Object.create(Subject.prototype);
-
- this.get_child_subject = function (file_name) {
- return new ImageFormatFile(file_id, format_name, file_name);
- };
-};
-
-ImageFormat.prototype = Object.create(Subject.prototype);
-
-module.exports = ImageFormat;
diff --git a/src/subject/subject-types/image-format/image-format.ts b/src/subject/subject-types/image-format/image-format.ts
new file mode 100644
index 00000000..a3104650
--- /dev/null
+++ b/src/subject/subject-types/image-format/image-format.ts
@@ -0,0 +1,209 @@
+const locreq = require("locreq")(__dirname);
+import sharp from "sharp";
+import path from "path";
+import { promises as fs } from "fs";
+import crypto from "crypto";
+import Subject, { NoActionSubject, LeafSubject } from "../../subject";
+import * as Errors from "../../../response/errors";
+import App from "../../../app/app";
+import Context from "../../../context";
+import { ShowActionName } from "../../../action";
+
+const QUALITY = 80;
+
+type FormattedFileVersion = {
+ id: string;
+ original_name?: string;
+ file_name?: string;
+};
+
+type FormatObject = {
+ size: number[];
+};
+
+function formatHash(format_obj: FormatObject) {
+ return format_obj.size[0] + ":" + format_obj.size[1] + "(" + QUALITY + ")";
+}
+
+function formatFilename(
+ original_filename: string,
+ format_name: string
+): string {
+ return (
+ path.basename(original_filename, path.extname(original_filename)) +
+ "-" +
+ format_name +
+ ".jpg"
+ );
+}
+
+class ImageFormatFile extends LeafSubject {
+ file_id: string;
+ file_name: string;
+ format_name: string;
+
+ constructor(
+ app: App,
+ file_id: string,
+ file_name: string,
+ format_name: string
+ ) {
+ super(app);
+ this.file_id = file_id;
+ this.file_name = file_name;
+ this.format_name = format_name;
+ }
+
+ async performAction(
+ context: Context,
+ action_name: ShowActionName,
+ params: any
+ ) {
+ if (action_name !== "show") {
+ throw new Errors.DeveloperError(
+ `Unknown action for '${this.getName()}' subject: '${action_name}'`
+ );
+ }
+
+ const format_obj =
+ this.app.ConfigManager.get("image_formats")[this.format_name] ||
+ null;
+
+ if (!format_obj) {
+ throw new Errors.BadSubjectPath(
+ "Unknown image format: " + this.format_name
+ );
+ }
+
+ let formattedVersion = await this.getFormattedVersion(
+ this.format_name,
+ format_obj
+ );
+
+ if (!formattedVersion) {
+ formattedVersion = await this.createFormattedVersion(
+ this.file_id,
+ this.format_name
+ );
+ }
+
+ const result = new this.app.Sealious.File.from_db_entry(
+ formattedVersion
+ );
+ result.path_on_hdd = this.getHddPath(formattedVersion.id);
+ result.mime = "image/jpeg";
+ return result;
+ }
+
+ async getFormattedVersion(
+ format_name: string,
+ format_obj: FormatObject
+ ): Promise<FormattedFileVersion> {
+ const random = Math.random();
+ const hash = formatHash(format_obj);
+ const results = await this.app.Datastore.aggregate("formatted-images", [
+ {
+ $match: {
+ original_photo_file: { $eq: this.file_id },
+ },
+ },
+ {
+ $match: {
+ $or: [{ "format.original": hash }, { "format.safe": hash }],
+ },
+ },
+ ]);
+
+ return (
+ results[0] && {
+ id: results[0].formatted_photo_file,
+ original_name: formatFilename(this.file_name, format_name),
+ }
+ );
+ }
+
+ getHddPath(file_id: string): string {
+ const { upload_path } = this.app.FileManager;
+ return locreq.resolve(`${upload_path}/${file_id}`);
+ }
+
+ async createFormattedVersion(
+ file_id: string,
+ format_name: string
+ ): Promise<FormattedFileVersion> {
+ const format_obj: FormatObject = this.app.ConfigManager.get(
+ "image_formats"
+ )[format_name];
+
+ const matches = await this.app.Datastore.find("files", { id: file_id });
+
+ const original_file = matches[0];
+ const file_path = this.getHddPath(original_file.id);
+ const [width, height] = format_obj.size;
+ const filename = formatFilename(
+ original_file.original_name,
+ format_name
+ );
+
+ const temp_file_name = crypto.randomBytes(10).toString("hex") + ".jpg";
+ const temp_file_path = `/tmp/${temp_file_name}`;
+
+ const buffer = await sharp(file_path)
+ .resize(width, height)
+ .toFile(temp_file_path)
+ .then(function() {
+ return fs.readFile(temp_file_path);
+ });
+
+ const sealious_file: FormattedFileVersion = await this.app.FileManager.save_file(
+ new this.app.Sealious.File(
+ new this.app.Sealious.SuperContext(),
+ filename,
+ buffer
+ )
+ );
+
+ await this.app.run_action(
+ new this.app.Sealious.SuperContext(),
+ ["collections", "formatted-images"],
+ "create",
+ {
+ original_photo_file: original_file.id,
+ formatted_photo_file: sealious_file.id,
+ format: formatHash(format_obj),
+ }
+ );
+
+ await fs.unlink(temp_file_path);
+ return sealious_file;
+ }
+
+ getName() {
+ return "SingleFile";
+ }
+}
+
+export class ImageFormat extends NoActionSubject {
+ file_id: string;
+ format_name: string;
+
+ constructor(app: App, file_id: string, format_name: string) {
+ super(app);
+ this.file_id = file_id;
+ this.format_name = format_name;
+ }
+
+ async getChildSubject(path_element: string) {
+ const file_name = path_element;
+ return new ImageFormatFile(
+ this.app,
+ this.file_id,
+ file_name,
+ this.format_name
+ );
+ }
+
+ getName() {
+ return "ImageFormats";
+ }
+}
diff --git a/src/subject/subject-types/image-formats.ts b/src/subject/subject-types/image-formats.ts
index 5925e1e8..e257f404 100644
--- a/src/subject/subject-types/image-formats.ts
+++ b/src/subject/subject-types/image-formats.ts
@@ -1,20 +1,19 @@
import Subject, { NoActionSubject } from "../subject";
-
import App from "../../app/app.js";
-const ImageFormat = require("./image-format/image-format.js");
+import { ImageFormat } from "./image-format/image-format.js";
export default class ImageFormats extends NoActionSubject {
file_id: string;
constructor(app: App, file_id: string) {
super(app);
this.file_id = file_id;
}
getName() {
return "ImageFormats";
}
- getChildSubject(path_element: string) {
+ async getChildSubject(path_element: string) {
return new ImageFormat(this.app, this.file_id, path_element);
}
}
diff --git a/src/subject/subject-types/single-file-subject.js b/src/subject/subject-types/single-file-subject.js
deleted file mode 100644
index cc8801f7..00000000
--- a/src/subject/subject-types/single-file-subject.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const Subject = require("../subject.js");
-const Errors = require("../../response/error.js");
-
-const FileHash = function (app, file_id) {
- this.name = "FileHash";
- this.file_id = file_id;
-
- const SingleFileSubject = function (file_id, file_name) {
- this.name = "SingleFile";
- this.file_id = file_id;
- this.file_name = file_name;
-
- SingleFileSubject.prototype.perform_action = function (
- context,
- action_name,
- args
- ) {
- switch (action_name) {
- case "show":
- return app.FileManager.find(context, {
- id: this.file_id,
- }).then(function (results) {
- return results[0];
- });
- default:
- throw new Errors.DeveloperError(
- `Unknown action for '${this.collection.name}' subject: '${action_name}'`
- );
- }
- };
- };
-
- SingleFileSubject.prototype = Object.create(Subject.prototype);
-
- this.get_child_subject = function (file_name) {
- return new SingleFileSubject(this.file_id, file_name);
- };
-};
-
-FileHash.prototype = Object.create(Subject.prototype);
-
-module.exports = FileHash;
diff --git a/src/subject/subject-types/single-file-subject.ts b/src/subject/subject-types/single-file-subject.ts
new file mode 100644
index 00000000..48565beb
--- /dev/null
+++ b/src/subject/subject-types/single-file-subject.ts
@@ -0,0 +1,55 @@
+import App from "../../app/app.js";
+import * as Errors from "../../response/errors";
+import { NoActionSubject, LeafSubject } from "../subject";
+import Context from "../../context";
+import {
+ CreateActionName,
+ ShowActionName,
+ DeleteActionName,
+} from "../../action";
+
+export default class FileHash extends NoActionSubject {
+ file_id: string;
+
+ constructor(app: App, file_id: string) {
+ super(app);
+ this.file_id = file_id;
+ }
+ getName() {
+ return "FileHash";
+ }
+ async getChildSubject(path_element: string) {
+ return new SingleFileSubject(this.app, this.file_id, path_element);
+ }
+}
+
+class SingleFileSubject extends LeafSubject {
+ file_id: string;
+ file_name: string;
+
+ constructor(app: App, file_id: string, file_name: string) {
+ super(app);
+ this.file_id = file_id;
+ this.file_name = file_name;
+ }
+ getName() {
+ return "SingleFile";
+ }
+ async performAction(
+ context: Context,
+ action_name: ShowActionName,
+ params?: any
+ ) {
+ if (action_name !== "show") {
+ throw new Errors.DeveloperError(
+ `Unknown action for '${this.name}' subject: '${action_name}'`
+ );
+ }
+
+ const [result] = await this.app.FileManager.find(context, {
+ id: this.file_id,
+ });
+
+ return result;
+ }
+}
diff --git a/src/subject/subject-types/single-resource-subject.ts b/src/subject/subject-types/single-resource-subject.ts
index 2a494742..e81a0d08 100644
--- a/src/subject/subject-types/single-resource-subject.ts
+++ b/src/subject/subject-types/single-resource-subject.ts
@@ -1,173 +1,165 @@
-import Subject from "../subject.js";
-import * as Errors from "../../response/errors.js";
-import SingleItemResponse from "../../../common_lib/response/single-item-response.js";
+import Subject from "../subject";
+import * as Errors from "../../response/errors";
+import SingleItemResponse from "../../../common_lib/response/single-item-response";
import assignAttachments from "../attachments/attachment-assigner";
-import CollectionFieldSubject from "./collection-field-subject.js";
-import App from "../../app/app.js";
-import Collection from "../../chip-types/collection.js";
-import Context from "../../context.js";
+import CollectionFieldSubject from "./collection-field-subject";
+import App from "../../app/app";
+import Collection from "../../chip-types/collection";
+import Context from "../../context";
+import Item from "../../../common_lib/response/item";
+import { LooseObject, AttachmentParams } from "../types";
+import { ActionName } from "../../action";
export default class SingleResource extends Subject {
collection: Collection;
resource_id: string;
- name = "SingleResource";
- app: App;
+
constructor(app: App, collection: Collection, resource_id: string) {
- super();
+ super(app);
this.collection = collection;
this.resource_id = resource_id;
- this.app = app;
}
+ getName() {
+ return "SingleResource";
+ }
+ async performAction(context: Context, action_name: ActionName, args?: any) {
+ switch (action_name) {
+ case "show":
+ return this.getResource(context, args);
+ case "edit":
+ return this.editResource(context, args, false);
+ case "replace":
+ return this.editResource(context, args, true);
+ case "delete":
+ return this.deleteResource(context, args);
+ default:
+ throw new Errors.DeveloperError(
+ `Unknown action for '${this.collection.name}' resource: '${action_name}'`
+ );
+ }
+ }
+ async getResource(
+ context: Context,
+ args: { format?: {}; attachments?: LooseObject }
+ ) {
+ const item = await this.__getResource(context, args);
- async get_resource(context: Context, { format }: { format?: {} }) {
+ let attachments, fieldsWithAttachments;
+ if (args.attachments && typeof args.attachments === "object") {
+ ({ attachments, fieldsWithAttachments } = await assignAttachments(
+ this.app,
+ context,
+ args as AttachmentParams,
+ this.collection,
+ [item]
+ ));
+ } else {
+ attachments = {};
+ fieldsWithAttachments = {};
+ }
+ return new SingleItemResponse({
+ item,
+ attachments,
+ fieldsWithAttachments,
+ });
+ }
+ async __getResource(context: Context, { format }: { format?: {} }) {
const db_entries = await this.app.Datastore.find(
this.collection.name,
{ sealious_id: this.resource_id },
{}
);
if (db_entries[0] === undefined) {
throw new Errors.NotFound(
`${this.collection.name}: id ${this.resource_id} not found`
);
}
- const resource_representation = await this.collection.get_resource_representation(
+ const resource_representation = await this.collection.getResourceRepresentation(
context,
db_entries[0],
format
);
- await this.collection.check_if_action_is_allowed(
+ await this.collection.checkIfActionIsAllowed(
context,
"show",
- resource_representation
+ resource_representation as Item
);
- let attachments, fieldsWithAttachments;
- if (args.attachments && typeof args.attachments === "object") {
- ({ attachments, fieldsWithAttachments } = await assignAttachments(
- app,
- context,
- args,
- this.collection,
- [item]
- ));
- } else {
- attachments = {};
- fieldsWithAttachments = {};
- }
- return new SingleItemResponse({
- item,
- attachments,
- fieldsWithAttachments,
- });
+ return resource_representation as Item;
}
async getChildSubject(key: string) {
if (this.collection.fields[key].type.is_subject) {
return new CollectionFieldSubject(
+ this.app,
this.collection,
this.resource_id,
key
);
}
return null;
}
- async edit_resource(
+ async editResource(
context: Context,
values_to_patch: any,
delete_empty_values: boolean
) {
// replaces just the provided values. Equivalent of PATCH request
+ const resource_representation = await this.__getResource(context, {});
- delete_empty_values =
- delete_empty_values === undefined ? false : delete_empty_values;
-
- let resource_representation: {};
-
- const resource_representation = await this.get_resource(context, {});
- await this.collection.check_if_action_is_allowed(
+ await this.collection.checkIfActionIsAllowed(
context,
"edit",
resource_representation
);
- await this.collection.validate_field_values(
+ await this.collection.validateFieldValues(
context,
delete_empty_values,
values_to_patch,
resource_representation
);
- const encoded_values = await this.collection.encode_field_values(
+ const encoded_values = await this.collection.encodeFieldValues(
context,
values_to_patch,
resource_representation
);
- const query = { _metadata: resource_representation._metadata };
+ const query: LooseObject = {
+ _metadata: resource_representation._metadata,
+ };
query._metadata.last_modified_context = context;
for (const field_name in encoded_values) {
query[field_name] = encoded_values[field_name];
}
const patch_result = await this.app.Datastore.update(
this.collection.name,
- { sealious_id: resource_id },
+ { sealious_id: this.resource_id },
{ $set: query }
);
if (patch_result.result.n !== 1) {
throw new Error("Wrong amount of resources (!=1) modified");
}
- return this.get_resource(context, {});
+ return this.getResource(context, {});
}
-}
-SingleResource.prototype.SingleResource.prototype.__delete_resource = function(
- datastore,
- collection,
- resource_id,
- context,
- args
-) {
- // abstraction seems to be leaking here: should we use context or SuperContext here?
+ async deleteResource(context: Context, args: LooseObject) {
+ const resource_representation = await this.__getResource(context, {});
- return SingleResource.prototype
- .__get_resource(datastore, collection, resource_id, context, {})
- .then(function(resource_representation) {
- return collection.check_if_action_is_allowed(
- context,
- "delete",
- resource_representation
- );
- })
- .then(function() {
- return datastore.remove(
- collection.name,
- {
- sealious_id: resource_id,
- },
- {}
- );
- })
- .then(function(data) {
- return Promise.resolve();
- });
-};
+ await this.collection.checkIfActionIsAllowed(
+ context,
+ "delete",
+ resource_representation
+ );
-SingleResource.prototype.perform_action = function(context, action_name, args) {
- switch (action_name) {
- case "show":
- return this.get_resource(context, args);
- case "edit":
- return this.edit_resource(context, args, false);
- case "replace":
- return this.edit_resource(context, args, true);
- case "delete":
- return this.delete_resource(context, args);
- default:
- throw new Errors.DeveloperError(
- `Unknown action for '${this.collection.name}' resource: '${action_name}'`
- );
+ await this.app.Datastore.remove(
+ this.collection.name,
+ { sealious_id: this.resource_id },
+ true
+ );
}
-};
+}
diff --git a/src/subject/subject-types/single-specification-subject.js b/src/subject/subject-types/single-specification-subject.js
deleted file mode 100644
index 49ee94fe..00000000
--- a/src/subject/subject-types/single-specification-subject.js
+++ /dev/null
@@ -1,32 +0,0 @@
-const Subject = require("../subject.js");
-const Errors = require("../../response/error.js");
-
-const SingleSpecificationsSubject = function (app, collection_name) {
- const actions = {
- show: function (params) {
- const collection = app.ChipManager.get_chip(
- "collection",
- collection_name
- );
- return collection.get_specification(false);
- },
- };
-
- this.perform_action = function (context, action_name, params) {
- try {
- return actions[action_name](params);
- } catch (e) {
- return Promise.reject(
- new Errors.BadSubjectAction(
- `Unknown action for SingleSpecificationsSubject: '${action_name}'`
- )
- );
- }
- };
-};
-
-SingleSpecificationsSubject.prototype = Object.create(Subject.prototype);
-
-SingleSpecificationsSubject.subject_name = "specifications";
-
-module.exports = SingleSpecificationsSubject;
diff --git a/src/subject/subject-types/single-specification-subject.ts b/src/subject/subject-types/single-specification-subject.ts
new file mode 100644
index 00000000..8ba0caf0
--- /dev/null
+++ b/src/subject/subject-types/single-specification-subject.ts
@@ -0,0 +1,35 @@
+import App from "../../app/app";
+import { LeafSubject } from "../subject";
+import * as Errors from "../../response/errors";
+import Context from "../../context";
+import Collection from "../../chip-types/collection";
+import { ShowActionName } from "../../action";
+
+export default class SingleSpecificationsSubject extends LeafSubject {
+ collection_name: string;
+
+ constructor(app: App, collection_name: string) {
+ super(app);
+ this.collection_name = collection_name;
+ }
+ getName() {
+ return "specifications";
+ }
+ async performAction(
+ context: Context,
+ action_name: ShowActionName,
+ params: any
+ ) {
+ if (action_name !== "show") {
+ throw new Errors.BadSubjectAction(
+ `Unknown action for SingleSpecificationsSubject: '${action_name}'`
+ );
+ }
+
+ const collection = this.app.ChipManager.getChip(
+ "collection",
+ this.collection_name
+ ) as Collection;
+ return collection.getSpecification(false);
+ }
+}
diff --git a/src/subject/types.ts b/src/subject/types.ts
index 0c592aad..e635ece6 100644
--- a/src/subject/types.ts
+++ b/src/subject/types.ts
@@ -1,3 +1,13 @@
export type LooseObject = {
[key: string]: any;
};
+
+export type AttachmentParams = {
+ attachments: LooseObject;
+};
+
+export type AssignAttachmentsResult = {
+ attachments: LooseObject;
+ fieldsWithAttachments: LooseObject;
+ items: Array<LooseObject>;
+};

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 24, 14:02 (21 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
557121
Default Alt Text
(53 KB)

Event Timeline