- added earliestPublicationDate for App/Models/Dataset.ts
All checks were successful
CI Pipeline / japa-tests (push) Successful in 49s
All checks were successful
CI Pipeline / japa-tests (push) Successful in 49s
- new classes TokenWorkerService.ts, TokenWorker.ts and ResumptionToken.ts for using REDIS with paging OAI results - deletd public/asstes2/langCodeMap.xml: integrated it directly in datasetxml2oai-pmh.xslt - added redis npm package - added TokenWorkerProvider.ts for using singleton of TokenWorkerService inside OaiController.ts - added config/oai.ts for oai related configs from .env-file - adapted XmlModel.ts for grting domDocument from database
This commit is contained in:
parent
2a7480d2ed
commit
7915f66dd6
16 changed files with 691 additions and 89 deletions
51
app/Library/Oai/ResumptionToken.ts
Normal file
51
app/Library/Oai/ResumptionToken.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
export default class ResumptionToken {
|
||||
private _documentIds: number[] = [];
|
||||
private _metadataPrefix = '';
|
||||
private _resumptionId = '';
|
||||
private _startPosition = 0;
|
||||
private _totalIds = 0;
|
||||
|
||||
get key(): string {
|
||||
return this.metadataPrefix + this.startPosition + this.totalIds;
|
||||
}
|
||||
|
||||
get documentIds(): number[] {
|
||||
return this._documentIds;
|
||||
}
|
||||
|
||||
set documentIds(idsToStore: number | number[]) {
|
||||
this._documentIds = Array.isArray(idsToStore) ? idsToStore : [idsToStore];
|
||||
}
|
||||
|
||||
get metadataPrefix(): string {
|
||||
return this._metadataPrefix;
|
||||
}
|
||||
|
||||
set metadataPrefix(value: string) {
|
||||
this._metadataPrefix = value;
|
||||
}
|
||||
|
||||
get resumptionId(): string {
|
||||
return this._resumptionId;
|
||||
}
|
||||
|
||||
set resumptionId(resumptionId: string) {
|
||||
this._resumptionId = resumptionId;
|
||||
}
|
||||
|
||||
get startPosition(): number {
|
||||
return this._startPosition;
|
||||
}
|
||||
|
||||
set startPosition(startPosition: number) {
|
||||
this._startPosition = startPosition;
|
||||
}
|
||||
|
||||
get totalIds(): number {
|
||||
return this._totalIds;
|
||||
}
|
||||
|
||||
set totalIds(totalIds: number) {
|
||||
this._totalIds = totalIds;
|
||||
}
|
||||
}
|
10
app/Library/Oai/TokenWorker.ts
Normal file
10
app/Library/Oai/TokenWorker.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import ResumptionToken from './ResumptionToken';
|
||||
|
||||
export default interface TokenWorkerContract {
|
||||
ttl: number;
|
||||
isConnected: boolean;
|
||||
connect();
|
||||
close();
|
||||
get(key: string): Promise<ResumptionToken | null>;
|
||||
set(token: ResumptionToken): Promise<string>;
|
||||
}
|
95
app/Library/Oai/TokenWorkerSerice.ts
Normal file
95
app/Library/Oai/TokenWorkerSerice.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
import ResumptionToken from './ResumptionToken';
|
||||
import { createClient, RedisClientType } from 'redis';
|
||||
import InternalServerErrorException from 'App/Exceptions/InternalServerException';
|
||||
import { sprintf } from 'sprintf-js';
|
||||
import dayjs from 'dayjs';
|
||||
import TokenWorkerContract from './TokenWorker';
|
||||
|
||||
export default class TokenWorkerService implements TokenWorkerContract {
|
||||
protected filePrefix = 'rs_';
|
||||
protected fileExtension = 'txt';
|
||||
|
||||
private cache: RedisClientType;
|
||||
public ttl: number;
|
||||
private url: string;
|
||||
private connected = false;
|
||||
|
||||
constructor(ttl: number) {
|
||||
this.ttl = ttl; // time to live
|
||||
this.url = process.env.REDIS_URL || 'redis://127.0.0.1:6379';
|
||||
}
|
||||
|
||||
public async connect() {
|
||||
this.cache = createClient({ url: this.url });
|
||||
this.cache.on('error', (err) => {
|
||||
this.connected = false;
|
||||
console.log('[Redis] Redis Client Error: ', err);
|
||||
});
|
||||
this.cache.on('connect', () => {
|
||||
this.connected = true;
|
||||
});
|
||||
await this.cache.connect();
|
||||
}
|
||||
|
||||
public get isConnected(): boolean {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
public async has(key: string): Promise<boolean> {
|
||||
const result = await this.cache.get(key);
|
||||
return result !== undefined && result !== null;
|
||||
}
|
||||
|
||||
public async set(token: ResumptionToken): Promise<string> {
|
||||
const uniqueName = await this.generateUniqueName();
|
||||
|
||||
const serialToken = JSON.stringify(token);
|
||||
await this.cache.setEx(uniqueName, this.ttl, serialToken);
|
||||
return uniqueName;
|
||||
}
|
||||
|
||||
private async generateUniqueName(): Promise<string> {
|
||||
let fc = 0;
|
||||
const uniqueId = dayjs().unix().toString();
|
||||
let uniqueName: string;
|
||||
let cacheKeyExists: boolean;
|
||||
do {
|
||||
// format values
|
||||
// %s - String
|
||||
// %d - Signed decimal number (negative, zero or positive)
|
||||
// [0-9] (Specifies the minimum width held of to the variable value)
|
||||
uniqueName = sprintf('%s%05d', uniqueId, fc++);
|
||||
cacheKeyExists = await this.has(uniqueName);
|
||||
} while (cacheKeyExists);
|
||||
return uniqueName;
|
||||
}
|
||||
|
||||
public async get(key: string): Promise<ResumptionToken | null> {
|
||||
if (!this.cache) {
|
||||
throw new InternalServerErrorException('Dataset is not available for OAI export!');
|
||||
}
|
||||
|
||||
const result = await this.cache.get(key);
|
||||
return result ? this.parseToken(result) : null;
|
||||
}
|
||||
|
||||
private parseToken(result: string): ResumptionToken {
|
||||
const rToken: ResumptionToken = new ResumptionToken();
|
||||
const parsed = JSON.parse(result);
|
||||
Object.assign(rToken, parsed);
|
||||
return rToken;
|
||||
}
|
||||
|
||||
public del(key: string) {
|
||||
this.cache.del(key);
|
||||
}
|
||||
|
||||
public flush() {
|
||||
this.cache.flushAll();
|
||||
}
|
||||
|
||||
public async close() {
|
||||
await this.cache.disconnect();
|
||||
this.connected = false;
|
||||
}
|
||||
}
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue