diff --git a/src/server/service/TokenedService.js b/src/server/service/TokenedService.js index ead005e..9d805cf 100644 --- a/src/server/service/TokenedService.js +++ b/src/server/service/TokenedService.js @@ -237,20 +237,7 @@ module.exports = Trait( 'TokenedService' ) 'private _getQuoteToken': function( quote, tokid, callback ) { this._dao.getToken( quote.getId(), this._ns, tokid ) - .then( token => - { - if ( tokid && !token ) - { - callback( - Error( "Token not found: " + tokid ), - null - ); - - return; - } - - callback( null, token ); - } ) + .then( token => callback( null, token ) ) .catch( err => callback( err, null ) ); }, diff --git a/src/server/token/MongoTokenDao.ts b/src/server/token/MongoTokenDao.ts index f77a436..539f43a 100644 --- a/src/server/token/MongoTokenDao.ts +++ b/src/server/token/MongoTokenDao.ts @@ -30,8 +30,9 @@ import { TokenType, } from "./TokenDao"; -import { TokenId, TokenNamespace } from "./Token"; import { DocumentId } from "../../document/Document"; +import { TokenId, TokenNamespace } from "./Token"; +import { UnknownTokenError } from "./UnknownTokenError"; /** @@ -155,7 +156,7 @@ export class MongoTokenDao implements TokenDao * @return token data */ getToken( doc_id: DocumentId, ns: TokenNamespace, token_id: TokenId ): - Promise + Promise { const root = this._genRoot( ns ) + '.'; const fields: any = {}; @@ -187,15 +188,17 @@ export class MongoTokenDao implements TokenDao if ( !field[ ns ] ) { - resolve( null ); + reject( new UnknownTokenError( + `Unknown token namespace '${ns}' for document '${doc_id}` + ) ); return; } const ns_data = field[ ns ]; resolve( ( token_id ) - ? this._getRequestedToken( token_id, ns_data ) - : this._getLatestToken( ns_data ) + ? this._getRequestedToken( doc_id, ns, token_id, ns_data ) + : this._getLatestToken( doc_id, ns, ns_data ) ); } ); @@ -204,19 +207,30 @@ export class MongoTokenDao implements TokenDao /** - * Retrieve latest token data, or `null` if none + * Retrieve latest token data * + * @param doc_id document id + * @param ns token namespace * @param ns_data namespace data * * @return data of latest token in namespace + * + * @throws UnknownTokenError if last token data is missing */ - private _getLatestToken( ns_data: TokenNamespaceData ): TokenData | null + private _getLatestToken( + doc_id: DocumentId, + ns: TokenNamespace, + ns_data: TokenNamespaceData + ): TokenData { var last = ns_data.last; if ( !last ) { - return null; + throw new UnknownTokenError( + `Failed to locate last token for namespace '${ns}'` + + `on document '${doc_id}'` + ); } return { @@ -227,23 +241,32 @@ export class MongoTokenDao implements TokenDao /** - * Retrieve latest token data, or `null` if none + * Retrieve latest token data * + * @param doc_id document id + * @param ns token namespace * @param token_id token identifier for namespace associated with NS_DATA * @param ns_data namespace data * * @return data of requested token + * + * @throws UnknownTokenError if token data is missing */ private _getRequestedToken( + doc_id: DocumentId, + ns: TokenNamespace, token_id: TokenId, ns_data: TokenNamespaceData - ): TokenData | null + ): TokenData { const reqtok = ns_data[ token_id ]; if ( !reqtok ) { - return null; + throw new UnknownTokenError( + `Missing data for requested token '${ns}.${token_id}'` + + `for document '${doc_id}'` + ); } return { diff --git a/src/server/token/TokenDao.ts b/src/server/token/TokenDao.ts index fcb06c2..b942b2c 100644 --- a/src/server/token/TokenDao.ts +++ b/src/server/token/TokenDao.ts @@ -47,7 +47,7 @@ export interface TokenDao doc_id: DocumentId, ns: TokenNamespace, token_id: TokenId - ): Promise; + ): Promise; } diff --git a/src/server/token/UnknownTokenError.ts b/src/server/token/UnknownTokenError.ts new file mode 100644 index 0000000..c6eb671 --- /dev/null +++ b/src/server/token/UnknownTokenError.ts @@ -0,0 +1,27 @@ +/** + * Unknown token error + * + * 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 . + * + * This still uses ease.js because it does a good job of transparently + * creating Error subtypes. + */ + +const { Class } = require( 'easejs' ); + +export const UnknownTokenError = Class( 'UnknownTokenError' ).extend( Error, {} ); diff --git a/test/server/token/MongoTokenDaoTest.ts b/test/server/token/MongoTokenDaoTest.ts index 74f9c13..47b1368 100644 --- a/test/server/token/MongoTokenDaoTest.ts +++ b/test/server/token/MongoTokenDaoTest.ts @@ -33,6 +33,7 @@ import { } from "../../../src/server/token/Token"; import { DocumentId } from "../../../src/document/Document"; +import { UnknownTokenError } from "../../../src/server/token/UnknownTokenError"; import { expect, use as chai_use } from 'chai'; @@ -129,7 +130,7 @@ describe( 'server.token.TokenDao', () => data: "", }; - ( <[string, TokenId, TokenQueryResult, TokenData][]>[ + ( <[string, TokenId, TokenQueryResult, TokenData|null, any][]>[ [ 'retrieves token by id', 'tok123', @@ -150,10 +151,11 @@ describe( 'server.token.TokenDao', () => id: 'tok123', status: expected_status, }, + null, ], [ - 'returns null for namespace if token is not found', + 'rejects for namespace if token is not found', 'tok123', { [field]: { @@ -170,19 +172,21 @@ describe( 'server.token.TokenDao', () => }, }, null, + `${ns}.tok123`, ], [ - 'returns null for field if namespace is not found', + 'rejects if namespace is not found', 'tok123', { [field]: {}, }, null, + ns, ], [ - 'returns lastest modified token given no token id', + 'returns last modified token given no token id', '', { [field]: { @@ -201,23 +205,55 @@ describe( 'server.token.TokenDao', () => id: 'toklast', status: expected_status, }, + null, ], - ] ).forEach( ( [ label, tok_id, result, expected ] ) => + + [ + 'rejects unknown last modified token given no token id', + '', + { + [field]: { + [ns]: {}, + }, + }, + null, + ns, + ], + + [ + 'rejects unknown namespace token given no token id', + '', + { + [field]: {}, + }, + null, + ns, + ], + ] ).forEach( ( [ label, tok_id, dbresult, expected, failure ] ) => it( label, () => { const coll: MongoCollection = { findOne( _selector, _fields, callback ) { - callback( null, result ); + callback( null, dbresult ); }, update() {}, }; - return expect( - new Sut( coll, field, () => 0 ) - .getToken( did, ns, tok_id ) - ).to.eventually.deep.equal( expected ); + const result = new Sut( coll, field, () => 0 ) + .getToken( did, ns, tok_id ); + + return ( failure !== null ) + ? Promise.all( [ + expect( result ).to.eventually.be.rejectedWith( + UnknownTokenError, failure + ), + expect( result ).to.eventually.be.rejectedWith( + UnknownTokenError, ''+did + ), + ] ) + : expect( result ).to.eventually.deep.equal( expected ); } ) );