[DEV-5312] Add dao for deltas
parent
d0b2a4ce73
commit
91a7cf94b2
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* Delta data access
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2019 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework.
|
||||||
|
*
|
||||||
|
* liza 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 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* These types are used to describe the structure of the token data as it
|
||||||
|
* is stored in Mongo. It has a number of undesirable properties and
|
||||||
|
* duplicates data---this was intended to make querying easier and work
|
||||||
|
* around Mongo limitations.
|
||||||
|
*
|
||||||
|
* This structure can be changed in the future, but we'll need to maintain
|
||||||
|
* compatibility with the existing data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DocumentId } from "../../document/Document";
|
||||||
|
import { PositiveInteger } from "../../numeric";
|
||||||
|
|
||||||
|
|
||||||
|
/** Manage deltas */
|
||||||
|
export interface DeltaDao
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get documents in need of processing
|
||||||
|
*
|
||||||
|
* @return documents in need of processing
|
||||||
|
*/
|
||||||
|
getUnprocessedDocuments(
|
||||||
|
callback: ( data: Record<string, any> | null ) => void
|
||||||
|
): this;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the document's processed index
|
||||||
|
*
|
||||||
|
* @param doc_id - The document whose index will be set
|
||||||
|
* @param index - The index to set
|
||||||
|
*/
|
||||||
|
advanceDeltaIndexByType(
|
||||||
|
doc_id: DocumentId,
|
||||||
|
type: string,
|
||||||
|
index: PositiveInteger,
|
||||||
|
callback: ( err: NullableError, indexHasAdvanced: boolean ) => void,
|
||||||
|
): this;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a given document as processed. First does a check to make sure that
|
||||||
|
* the document does not have a newer update timestamp than the provided one
|
||||||
|
*
|
||||||
|
* @param doc_id - The document to mark
|
||||||
|
* @param last_update_ts - The last time this document was updated
|
||||||
|
*
|
||||||
|
* @return true if the document was successfully marked as processed
|
||||||
|
*/
|
||||||
|
markDocumentAsProcessed(
|
||||||
|
doc_id: DocumentId,
|
||||||
|
last_update_ts: UnixTimestamp,
|
||||||
|
callback: ( err: NullableError, markedSuccessfully: boolean ) => void,
|
||||||
|
): this;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
/**
|
||||||
|
* Delta data access
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2019 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework.
|
||||||
|
*
|
||||||
|
* liza 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 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Get deltas from the mongo document in order to process and publish them
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DocumentId } from "../../document/Document";
|
||||||
|
import { PositiveInteger } from "../../numeric";
|
||||||
|
import { MongoCollection } from "mongodb";
|
||||||
|
import { DeltaDao } from "./DeltaDao";
|
||||||
|
|
||||||
|
|
||||||
|
export type MongoDeltaType = 'ratedata' | 'data';
|
||||||
|
|
||||||
|
|
||||||
|
/** Manage deltas */
|
||||||
|
export class MongoDeltaDao implements DeltaDao
|
||||||
|
{
|
||||||
|
/** The ratedata delta type */
|
||||||
|
static readonly DELTA_RATEDATA: string = 'ratedata';
|
||||||
|
|
||||||
|
/** The data delta type */
|
||||||
|
static readonly DELTA_DATA: string = 'data';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize connection
|
||||||
|
*
|
||||||
|
* @param _collection Mongo collection
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly _collection: MongoCollection,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get documents in need of processing
|
||||||
|
*
|
||||||
|
* @return documents in need of processing
|
||||||
|
*/
|
||||||
|
getUnprocessedDocuments(
|
||||||
|
callback: ( data: Record<string, any> | null ) => void,
|
||||||
|
): this
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this._collection.find(
|
||||||
|
{ published: false },
|
||||||
|
{},
|
||||||
|
function( _err, cursor )
|
||||||
|
{
|
||||||
|
cursor.toArray( function( _err: NullableError, data: any[] )
|
||||||
|
{
|
||||||
|
// was the quote found?
|
||||||
|
if ( data.length == 0 )
|
||||||
|
{
|
||||||
|
callback.call( self, null );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the quote data
|
||||||
|
callback.call( self, data );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the document's processed index
|
||||||
|
*
|
||||||
|
* @param doc_id - Document whose index will be set
|
||||||
|
* @param type - Delta type
|
||||||
|
* @param index - Index to set
|
||||||
|
* @param callback - Callback function
|
||||||
|
*/
|
||||||
|
advanceDeltaIndexByType(
|
||||||
|
doc_id: DocumentId,
|
||||||
|
type: MongoDeltaType,
|
||||||
|
index: PositiveInteger,
|
||||||
|
callback: ( err: NullableError, indexAdvanced: boolean ) => void,
|
||||||
|
): this
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
const set_data: Record<string, any> = {};
|
||||||
|
|
||||||
|
set_data[ 'lastPublishDelta.' + type ] = index;
|
||||||
|
|
||||||
|
this._collection.update(
|
||||||
|
{ id: doc_id },
|
||||||
|
{ $set: set_data },
|
||||||
|
{ upsert: true },
|
||||||
|
function( err )
|
||||||
|
{
|
||||||
|
if ( err )
|
||||||
|
{
|
||||||
|
callback.call( self, err, false );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.call( self, null, true );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a given document as processed. First does a check to make sure that
|
||||||
|
* the document does not have a newer update timestamp than the provided one
|
||||||
|
*
|
||||||
|
* @param doc_id - The document to mark
|
||||||
|
* @param last_update_ts - The last time this document was updated
|
||||||
|
*
|
||||||
|
* @return true if the document was successfully marked as processed
|
||||||
|
*/
|
||||||
|
markDocumentAsProcessed(
|
||||||
|
doc_id: DocumentId,
|
||||||
|
last_update_ts: UnixTimestamp,
|
||||||
|
callback: ( err: NullableError, indexAdvanced: boolean ) => void,
|
||||||
|
): this
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this._collection.update(
|
||||||
|
{ id: doc_id, lastUpdate: { $gt: last_update_ts } },
|
||||||
|
{ $set: { processed: true } },
|
||||||
|
{ upsert: false },
|
||||||
|
function( err, result )
|
||||||
|
{
|
||||||
|
if ( err )
|
||||||
|
{
|
||||||
|
callback.call( self, err, false );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log( '-------', result );
|
||||||
|
|
||||||
|
callback.call( self, null, true );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -69,6 +69,9 @@ interface MongoFindOptions
|
||||||
{
|
{
|
||||||
/** Limit results returned */
|
/** Limit results returned */
|
||||||
limit?: PositiveInteger,
|
limit?: PositiveInteger,
|
||||||
|
|
||||||
|
/** Whether to project only id's */
|
||||||
|
id?: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,7 +151,7 @@ declare interface MongoCollection
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a query and return the first result
|
* Execute a query and return the results
|
||||||
*
|
*
|
||||||
* Unlike `update`, the callback return value is not propagated, and so
|
* Unlike `update`, the callback return value is not propagated, and so
|
||||||
* the callback ought not return anything.
|
* the callback ought not return anything.
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* Token state management test
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2019 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework.
|
||||||
|
*
|
||||||
|
* liza 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 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MongoDeltaDao as Sut } from "../../../src/server/db/MongoDeltaDao";
|
||||||
|
import { MongoCollection } from "mongodb";
|
||||||
|
import { PositiveInteger } from "../../../src/numeric";
|
||||||
|
import { DocumentId } from "../../../src/document/Document";
|
||||||
|
|
||||||
|
import { expect, use as chai_use } from 'chai';
|
||||||
|
chai_use( require( 'chai-as-promised' ) );
|
||||||
|
|
||||||
|
|
||||||
|
describe( 'server.db.MongoDeltaDao', () =>
|
||||||
|
{
|
||||||
|
describe( '#getUnprocessedDocuments', () =>
|
||||||
|
{
|
||||||
|
it( 'gets documents', () =>
|
||||||
|
{
|
||||||
|
let returned_data = { foo: 'bar' };
|
||||||
|
let callback_called = false;
|
||||||
|
|
||||||
|
const collection = _createMockMongoCollection();
|
||||||
|
collection.find = ( _: any, __: any, c: any ) =>
|
||||||
|
{
|
||||||
|
c( null, {
|
||||||
|
toArray: ( c: any ) => c( null, returned_data ),
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
const callback = ( data: Record<string, any> | null ) =>
|
||||||
|
{
|
||||||
|
expect( returned_data ).to.deep.equal( data );
|
||||||
|
|
||||||
|
callback_called = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
new Sut( collection ).getUnprocessedDocuments( callback );
|
||||||
|
|
||||||
|
expect( callback_called ).to.equal( true );
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe( '#advanceDeltaIndexByType', () =>
|
||||||
|
{
|
||||||
|
it( 'advances specified index', () =>
|
||||||
|
{
|
||||||
|
const quote_id = <DocumentId>123,
|
||||||
|
delta_type = 'ratedata',
|
||||||
|
expected_field = 'lastPublishDelta.ratedata',
|
||||||
|
index = <PositiveInteger>1;
|
||||||
|
|
||||||
|
let callback_called = false;
|
||||||
|
|
||||||
|
const collection = _createMockMongoCollection();
|
||||||
|
collection.update = (
|
||||||
|
given_quote_id: any,
|
||||||
|
given_delta_type: any,
|
||||||
|
_given_index: any,
|
||||||
|
given_callback: any
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
const expected_set: Record<string, any> = {};
|
||||||
|
|
||||||
|
expected_set[ expected_field ] = index
|
||||||
|
|
||||||
|
expect( given_quote_id ).to.deep.equal( { id: quote_id } );
|
||||||
|
expect( given_delta_type )
|
||||||
|
.to.deep.equal( { $set: expected_set } );
|
||||||
|
|
||||||
|
given_callback( null );
|
||||||
|
};
|
||||||
|
|
||||||
|
const callback = ( _err: NullableError, _indexAdvanced: boolean ) =>
|
||||||
|
{
|
||||||
|
callback_called = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
new Sut( collection ).advanceDeltaIndexByType(
|
||||||
|
quote_id,
|
||||||
|
delta_type,
|
||||||
|
index,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( callback_called ).to.equal( true );
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe( '#markDocumentAsProcessed', () =>
|
||||||
|
{
|
||||||
|
it( 'doc marked if provided timestamp <= doc timestamp', () =>
|
||||||
|
{
|
||||||
|
const quote_id = <DocumentId>123,
|
||||||
|
last_update_ts = <UnixTimestamp>1573582767;
|
||||||
|
|
||||||
|
let callback_called = false;
|
||||||
|
|
||||||
|
const collection = _createMockMongoCollection();
|
||||||
|
collection.update = (
|
||||||
|
given_filter: any,
|
||||||
|
_: any,
|
||||||
|
__: any,
|
||||||
|
given_callback: any
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
const expected_filter: Record<string, any> = {
|
||||||
|
id: quote_id,
|
||||||
|
lastUpdate: { $gt: last_update_ts }
|
||||||
|
};
|
||||||
|
|
||||||
|
expect( given_filter ).to.deep.equal( expected_filter );
|
||||||
|
|
||||||
|
given_callback( null );
|
||||||
|
};
|
||||||
|
|
||||||
|
const callback = ( _err: NullableError, _indexAdvanced: boolean ) =>
|
||||||
|
{
|
||||||
|
callback_called = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
new Sut( collection ).markDocumentAsProcessed(
|
||||||
|
quote_id,
|
||||||
|
last_update_ts,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( callback_called ).to.equal( true );
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
function _createMockMongoCollection(): MongoCollection
|
||||||
|
{
|
||||||
|
return <MongoCollection> {
|
||||||
|
findOne() {},
|
||||||
|
update() {},
|
||||||
|
findAndModify() {},
|
||||||
|
find() {},
|
||||||
|
createIndex() {},
|
||||||
|
insert() {},
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue