mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-20 00:23:03 +00:00
200 lines
5.3 KiB
TypeScript
200 lines
5.3 KiB
TypeScript
/*
|
|
* Copyright (C) 2019 StApps
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
import {SCBulkRequest, SCThingType} from '@openstapps/core';
|
|
import {Logger} from '@openstapps/logger';
|
|
import moment from 'moment';
|
|
import NodeCache from 'node-cache';
|
|
import {v4} from 'uuid';
|
|
import {Database} from './database';
|
|
|
|
/**
|
|
* Possible operations with a bulk
|
|
*/
|
|
export type BulkOperation = 'create' | 'expired' | 'update';
|
|
|
|
/**
|
|
* Describes an indexing process
|
|
*/
|
|
export class Bulk implements SCBulkRequest {
|
|
/**
|
|
* Expiration of the bulk
|
|
*
|
|
* If the bulk is not finished before the expiration date is hit, the bulk
|
|
* and all data associated with it will be deleted
|
|
*/
|
|
expiration: string;
|
|
|
|
/**
|
|
* The data source of the bulk
|
|
*
|
|
* Bulks with same type and source will be replaced and the data will be
|
|
* updated when the bulk is marked as done
|
|
*/
|
|
source: string;
|
|
|
|
/**
|
|
* State of the bulk
|
|
*
|
|
* Data can be indexed for this bulk as long as the state is `in progress`
|
|
* and the bulk is not expired
|
|
*
|
|
* When the bulk is marked as `done` it replaces the previous bulk with
|
|
* the same source and type. The data will be availabe to the user when
|
|
* the bulk switches to done
|
|
*/
|
|
state: 'in progress' | 'done';
|
|
|
|
/**
|
|
* Type of data in the bulk
|
|
*/
|
|
type: SCThingType;
|
|
|
|
/**
|
|
* Unique identifier of the bulk
|
|
*/
|
|
uid: string;
|
|
|
|
/**
|
|
* Creates a new bulk process
|
|
*
|
|
* @param request Data needed for requesting a bulk
|
|
*/
|
|
constructor(request: SCBulkRequest) {
|
|
this.uid = v4();
|
|
this.state = 'in progress';
|
|
|
|
this.expiration =
|
|
typeof request.expiration === 'string' ? request.expiration : moment().add(1, 'hour').toISOString();
|
|
// when should this process be finished
|
|
// where does the process come from
|
|
this.source = request.source;
|
|
// which type of data is this process about to index
|
|
this.type = request.type;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cache for bulk-processes
|
|
*/
|
|
export class BulkStorage {
|
|
/**
|
|
* Cache for temporary storage
|
|
*/
|
|
private readonly cache: NodeCache;
|
|
|
|
/**
|
|
* Creates a new BulkStorage
|
|
*
|
|
* @param database the database that is controlled by this bulk storage
|
|
*/
|
|
constructor(public database: Database) {
|
|
// a bulk lives 60 minutes if no expiration is given
|
|
// the cache is checked every 60 seconds
|
|
this.cache = new NodeCache({stdTTL: 3600, checkperiod: 60});
|
|
|
|
this.cache.on('expired', async (_key, bulk: Bulk) => {
|
|
// if the bulk is not done
|
|
if (bulk.state !== 'done') {
|
|
// the database can delete the data associated with this bulk
|
|
await this.database.bulkExpired(bulk);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Saves a bulk process and assigns to it a user-defined ttl (time-to-live)
|
|
*
|
|
* @param bulk the bulk process to save
|
|
* @returns the bulk process that was saved
|
|
*/
|
|
private save(bulk: Bulk): Bulk {
|
|
const expirationInSeconds = moment(bulk.expiration).diff(moment.now()) / 1000;
|
|
Logger.info('Bulk expires in ', expirationInSeconds, 'seconds');
|
|
|
|
// save the item in the cache with it's expected expiration
|
|
this.cache.set(bulk.uid, bulk, expirationInSeconds);
|
|
|
|
return bulk;
|
|
}
|
|
|
|
/**
|
|
* Create and save a new bulk process
|
|
*
|
|
* @param bulkRequest a request for a new bulk process
|
|
* @returns a promise that contains the new bulk process
|
|
*/
|
|
public async create(bulkRequest: SCBulkRequest): Promise<Bulk> {
|
|
const bulk = new Bulk(bulkRequest);
|
|
bulk.source = bulkRequest.source;
|
|
bulk.type = bulkRequest.type;
|
|
|
|
this.save(bulk);
|
|
|
|
// tell the database that the bulk was created
|
|
await this.database.bulkCreated(bulk);
|
|
|
|
return bulk;
|
|
}
|
|
|
|
/**
|
|
* Delete a bulk process
|
|
*
|
|
* @param uid uid of the bulk process
|
|
* @returns a promise that contains the deleted bulk process
|
|
*/
|
|
public async delete(uid: string): Promise<Bulk> {
|
|
const bulk = this.read(uid);
|
|
|
|
if (typeof bulk === 'undefined') {
|
|
throw new TypeError(`Bulk that should be deleted was not found. UID was "${uid}"`);
|
|
}
|
|
|
|
// delete the bulk process from the cache
|
|
this.cache.del(uid);
|
|
|
|
// tell the database to handle the expiration of the bulk
|
|
await this.database.bulkExpired(bulk);
|
|
|
|
return bulk;
|
|
}
|
|
|
|
/**
|
|
* Update an old bulk process (replace it with the new one)
|
|
*
|
|
* @param bulk new bulk process
|
|
* @returns an empty promise
|
|
*/
|
|
public async markAsDone(bulk: Bulk): Promise<void> {
|
|
bulk.state = 'done';
|
|
this.save(bulk);
|
|
|
|
// tell the database that this is the new bulk
|
|
await this.database.bulkUpdated(bulk);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Read an existing bulk process
|
|
*
|
|
* @param uid uid of the bulk process
|
|
* @returns a promise that contains a bulk
|
|
*/
|
|
public read(uid: string): Bulk | undefined {
|
|
return this.cache.get(uid);
|
|
}
|
|
}
|