rets-client
Advanced tools
Comparing version 4.6.11 to 5.0.0
@@ -0,4 +1,31 @@ | ||
#### 5.0 | ||
A significant amount of internal cleanup, resulting in more consistent code and API. There are some minor breaking | ||
changes, but they're small enough that migrating to 5.0 shouldn't be much effort. The | ||
[Example Usage](https://github.com/sbruno81/rets-client#example-usage) has been updated to show 5.x patterns. | ||
- object stream queries now have events with a `type` field to make discrimination easier, with the following | ||
possibilities: | ||
- `dataStream`, for a stream containing an object's raw data | ||
- `location`, when no stream is available but a URL is available in the `headerInfo` (as per the `Location: 1` option) | ||
- `headerInfo`, with the headers for the outer multipart response | ||
- `error`, for an error corresponding to a single object rather than the stream as a whole | ||
- search stream queries now return an object with a `retsStream` field rather than the bare stream | ||
- the `searchRets` method now returns an object with a `rawStream` field rather than the bare stream | ||
- headerInfo is now available on every query made | ||
- login and logout set `client.loginHeaderInfo` and `client.logoutHeaderInfo` | ||
- every streaming query will include an event with `type: 'headerInfo'` | ||
- every buffered query (and most streaming queries) will return an object including a `headerInfo` field | ||
- errors now consistently include response headers as well as the request options | ||
- all calls made to the RETS server now obey `settings.method` for POST vs GET | ||
#### 4.6.0 | ||
Added support for `Location: 1` option on `getObject` calls. | ||
#### 4.5.0 | ||
Added a new error class for RETS permissions problems on login. | ||
#### 4.4.0 | ||
Added a new `parserEncoding` param for `client.search.query()` and `client.search.stream.query()` calls, defaulting to | ||
UTF-8. | ||
Added a new `parserEncoding` param for `client.search.query()` and `client.search.stream.query()` calls. UTF-8 is the | ||
default value, so this is to support RETS servers using ISO-8859-1 or some other encoding. | ||
@@ -12,5 +39,4 @@ #### 4.3.0 | ||
#### 4.1.0 | ||
Added client configuration option to use POST instead of GET for all requests, as this seems to work better for some | ||
RETS servers. | ||
RETS servers. See the [Example Usage](https://github.com/sbruno81/rets-client#client-configuration). | ||
@@ -17,0 +43,0 @@ #### 4.0.0 |
@@ -89,13 +89,13 @@ // Generated by CoffeeScript 1.10.0 | ||
this.baseRetsSession = request.defaults(defaults); | ||
this.loginRequest = Promise.promisify(this.baseRetsSession.defaults({ | ||
uri: this.settings.loginUrl | ||
})); | ||
} | ||
Client.prototype.login = function() { | ||
var options; | ||
options = { | ||
uri: this.settings.loginUrl | ||
}; | ||
return auth.login(this.baseRetsSession.defaults(options), this).then((function(_this) { | ||
return function(systemData) { | ||
return auth.login(this.loginRequest, this).then((function(_this) { | ||
return function(retsContext) { | ||
var hasPermissions, key, missingPermissions, val; | ||
_this.systemData = systemData; | ||
_this.systemData = retsContext.systemData; | ||
_this.loginHeaderInfo = retsContext.headerInfo; | ||
_this.urls = {}; | ||
@@ -134,5 +134,5 @@ for (key in URL_KEYS) { | ||
} | ||
_this.logoutRequest = _this.baseRetsSession.defaults({ | ||
_this.logoutRequest = Promise.promisify(_this.baseRetsSession.defaults({ | ||
uri: _this.urls[URL_KEYS.LOGOUT] | ||
}); | ||
})); | ||
if (!hasPermissions) { | ||
@@ -147,3 +147,7 @@ throw new errors.RetsPermissionError(missingPermissions); | ||
Client.prototype.logout = function() { | ||
return auth.logout(this.logoutRequest); | ||
return auth.logout(this.logoutRequest, this).then((function(_this) { | ||
return function(retsContext) { | ||
return _this.logoutHeaderInfo = retsContext.headerInfo; | ||
}; | ||
})(this)); | ||
}; | ||
@@ -150,0 +154,0 @@ |
@@ -25,7 +25,10 @@ // Generated by CoffeeScript 1.10.0 | ||
_getMetadataImpl = function(retsSession, type, options, client) { | ||
_getMetadataImpl = function(retsSession, type, queryOptions, client) { | ||
return new Promise(function(resolve, reject) { | ||
var context, currEntry, result; | ||
context = retsParsing.getStreamParser('getMetadata', type); | ||
retsHttp.streamRetsMethod('getMetadata', retsSession, options, context.fail, context.response, client).pipe(context.parser); | ||
var currEntry, result, retsContext; | ||
retsContext = retsParsing.getStreamParser({ | ||
retsMethod: 'getMetadata', | ||
queryOptions: queryOptions | ||
}, type); | ||
retsHttp.streamRetsMethod(retsContext, retsSession, client); | ||
result = { | ||
@@ -36,3 +39,3 @@ results: [], | ||
currEntry = null; | ||
return context.retsStream.pipe(through2.obj(function(event, encoding, callback) { | ||
return retsContext.retsStream.pipe(through2.obj(function(event, encoding, callback) { | ||
var key, ref, ref1, value; | ||
@@ -96,3 +99,3 @@ switch (event.type) { | ||
return function() { | ||
var options; | ||
var queryOptions; | ||
if (!type) { | ||
@@ -104,3 +107,3 @@ throw new errors.RetsParamError('Metadata type is required'); | ||
} | ||
options = { | ||
queryOptions = { | ||
Type: type, | ||
@@ -110,3 +113,6 @@ ID: id, | ||
}; | ||
return retsHttp.callRetsMethod('getMetadata', _this.retsSession, options); | ||
return retsHttp.callRetsMethod({ | ||
retsMethod: 'getMetadata', | ||
queryOptions: queryOptions | ||
}, _this.retsSession, _this.client); | ||
}; | ||
@@ -122,7 +128,7 @@ })(this)); | ||
getSystem = function() { | ||
return this.getMetadata('METADATA-SYSTEM', '0').then(function(xmlResponse) { | ||
return this.getMetadata('METADATA-SYSTEM', '0').then(function(retsContext) { | ||
return new Promise(function(resolve, reject) { | ||
var comment, comments, gotMetaDataInfo, gotSystemInfo, result, retsParser; | ||
result = {}; | ||
retsParser = retsParsing.getSimpleParser('getMetadata', reject, xmlResponse.headerInfo); | ||
retsParser = retsParsing.getSimpleParser(retsContext, reject); | ||
gotMetaDataInfo = false; | ||
@@ -164,3 +170,3 @@ gotSystemInfo = false; | ||
if (!gotSystemInfo || !gotMetaDataInfo) { | ||
return reject(new errors.RetsProcessingError('getSystem', 'Failed to parse data')); | ||
return reject(new errors.RetsProcessingError(retsContext, 'Failed to parse data')); | ||
} else { | ||
@@ -170,6 +176,7 @@ if (comments.length > 0) { | ||
} | ||
result.headerInfo = retsContext.headerInfo; | ||
return resolve(result); | ||
} | ||
}); | ||
retsParser.parser.write(xmlResponse.body); | ||
retsParser.parser.write(retsContext.body); | ||
return retsParser.parser.end(); | ||
@@ -176,0 +183,0 @@ }); |
@@ -170,4 +170,2 @@ // Generated by CoffeeScript 1.10.0 | ||
return { | ||
retsSession: _retsSession, | ||
client: _client, | ||
getObjects: getObjects, | ||
@@ -174,0 +172,0 @@ getAllObjects: getAllObjects, |
@@ -34,20 +34,21 @@ // Generated by CoffeeScript 1.10.0 | ||
_processBody = function(headers, bodyStream, preDecoded, options) { | ||
_processBody = function(retsContext, preDecoded) { | ||
return new Promise(function(resolve, reject) { | ||
var b64, headerInfo, onError, ref, retsParser; | ||
headerInfo = headersHelper.processHeaders(headers); | ||
var b64, onError, ref, retsParser; | ||
onError = function(error) { | ||
return reject(errors.ensureRetsError('getObject', error, headerInfo)); | ||
return reject(errors.ensureRetsError(retsContext, error)); | ||
}; | ||
if (headerInfo.location && options.Location === 1) { | ||
if (retsContext.headerInfo.location && retsContext.queryOptions.Location === 1) { | ||
return resolve({ | ||
headerInfo: headerInfo | ||
type: 'location', | ||
headerInfo: retsContext.headerInfo | ||
}); | ||
} else if (_insensitiveStartsWith(headerInfo.contentType, 'text/xml')) { | ||
retsParser = retsParsing.getSimpleParser('getObject', onError, headerInfo); | ||
return bodyStream.pipe(retsParser.parser); | ||
} else if (_insensitiveStartsWith(headerInfo.contentType, 'multipart')) { | ||
return multipart.getObjectStream(headerInfo, bodyStream, _processBody, options).then(function(objectStream) { | ||
} else if (_insensitiveStartsWith(retsContext.headerInfo.contentType, 'text/xml')) { | ||
retsParser = retsParsing.getSimpleParser(retsContext, onError); | ||
return retsContext.parser.pipe(retsParser.parser); | ||
} else if (_insensitiveStartsWith(retsContext.headerInfo.contentType, 'multipart')) { | ||
return multipart.getObjectStream(retsContext, _processBody).then(function(objectStream) { | ||
return resolve({ | ||
headerInfo: headerInfo, | ||
type: 'objectStream', | ||
headerInfo: retsContext.headerInfo, | ||
objectStream: objectStream | ||
@@ -59,20 +60,23 @@ }); | ||
} else { | ||
if (preDecoded || ((ref = headerInfo.transferEncoding) === 'binary' || ref === '7bit' || ref === '8bit' || ref === (void 0))) { | ||
if (preDecoded || ((ref = retsContext.headerInfo.transferEncoding) === 'binary' || ref === '7bit' || ref === '8bit' || ref === (void 0))) { | ||
return resolve({ | ||
headerInfo: headerInfo, | ||
dataStream: bodyStream | ||
type: 'dataStream', | ||
headerInfo: retsContext.headerInfo, | ||
dataStream: retsContext.parser | ||
}); | ||
} else if (headerInfo.transferEncoding === 'base64') { | ||
} else if (retsContext.headerInfo.transferEncoding === 'base64') { | ||
b64 = base64.decode(); | ||
bodyStream.on('error', function(err) { | ||
return b64.emit('error', errors.ensureRetsError('getObject', err, headerInfo)); | ||
retsContext.parser.on('error', function(err) { | ||
return b64.emit('error', errors.ensureRetsError(retsContext, err)); | ||
}); | ||
return resolve({ | ||
headerInfo: headerInfo, | ||
dataStream: bodyStream.pipe(b64) | ||
type: 'dataStream', | ||
headerInfo: retsContext.headerInfo, | ||
dataStream: retsContext.parser.pipe(b64) | ||
}); | ||
} else { | ||
return resolve({ | ||
headerInfo: headerInfo, | ||
error: new errors.RetsProcessingError('getObject', "unknown transfer encoding: " + (JSON.stringify(headerInfo.transferEncoding)), headerInfo) | ||
type: 'error', | ||
headerInfo: retsContext.headerInfo, | ||
error: new errors.RetsProcessingError(retsContext, "unknown transfer encoding: " + (JSON.stringify(retsContext.headerInfo.transferEncoding))) | ||
}); | ||
@@ -152,3 +156,3 @@ } | ||
return function() { | ||
var alwaysGroupObjects, idArray, idString, mainOptions, objectIds, options, resourceId; | ||
var alwaysGroupObjects, idArray, idString, mainOptions, objectIds, queryOptions, resourceId; | ||
if (!resourceType) { | ||
@@ -188,31 +192,30 @@ throw new errors.RetsParamError('Resource type id is required'); | ||
}; | ||
options = queryOptionHelpers.mergeOptions(mainOptions, _options, { | ||
queryOptions = queryOptionHelpers.mergeOptions(mainOptions, _options, { | ||
Location: 0 | ||
}); | ||
if (Array.isArray(options.ObjectData)) { | ||
options.ObjectData = options.ObjectData.join(','); | ||
if (Array.isArray(queryOptions.ObjectData)) { | ||
queryOptions.ObjectData = queryOptions.ObjectData.join(','); | ||
} | ||
alwaysGroupObjects = !!options.alwaysGroupObjects; | ||
delete options.alwaysGroupObjects; | ||
alwaysGroupObjects = !!queryOptions.alwaysGroupObjects; | ||
delete queryOptions.alwaysGroupObjects; | ||
return new Promise(function(resolve, reject) { | ||
var bodyStream, done, fail, req; | ||
done = false; | ||
fail = function(error) { | ||
done = true; | ||
return reject(errors.ensureRetsError('getObject', error)); | ||
var errorHandler, responseHandler, retsContext; | ||
errorHandler = function(error) { | ||
return reject(errors.ensureRetsError(retsContext, error)); | ||
}; | ||
req = retsHttp.streamRetsMethod('getObject', _this.retsSession, options, fail, null, _this.client); | ||
req.on('error', fail); | ||
req.on('response', function(response) { | ||
if (done) { | ||
return; | ||
} | ||
done = true; | ||
return _processBody(response.rawHeaders, bodyStream, true, options).then(function(result) { | ||
responseHandler = function(response) { | ||
return _processBody(retsContext, true).then(function(result) { | ||
return resolve(result); | ||
})["catch"](function(error) { | ||
return fail(error); | ||
return errorHandler(error); | ||
}); | ||
}); | ||
return bodyStream = req.pipe(through2()); | ||
}; | ||
retsContext = { | ||
retsMethod: 'getObject', | ||
queryOptions: queryOptions, | ||
errorHandler: errorHandler, | ||
responseHandler: responseHandler, | ||
parser: through2() | ||
}; | ||
return retsHttp.streamRetsMethod(retsContext, _this.retsSession, _this.client); | ||
}).then(function(result) { | ||
@@ -224,5 +227,10 @@ var wrappedResult; | ||
wrappedResult = { | ||
type: 'objectStream', | ||
headerInfo: result.headerInfo, | ||
objectStream: through2.obj() | ||
}; | ||
wrappedResult.objectStream.write({ | ||
type: 'headerInfo', | ||
headerInfo: result.headerInfo | ||
}); | ||
wrappedResult.objectStream.write(result); | ||
@@ -242,4 +250,4 @@ wrappedResult.objectStream.end(); | ||
getAllObjects = function(resourceType, objectType, ids, options) { | ||
return this.getObjects(resourceType, objectType, _annotateIds(ids, '*'), options); | ||
getAllObjects = function(resourceType, objectType, ids, queryOptions) { | ||
return this.getObjects(resourceType, objectType, _annotateIds(ids, '*'), queryOptions); | ||
}; | ||
@@ -253,4 +261,4 @@ | ||
getPreferredObjects = function(resourceType, objectType, ids, options) { | ||
return this.getObjects(resourceType, objectType, _annotateIds(ids, '0'), options); | ||
getPreferredObjects = function(resourceType, objectType, ids, queryOptions) { | ||
return this.getObjects(resourceType, objectType, _annotateIds(ids, '0'), queryOptions); | ||
}; | ||
@@ -257,0 +265,0 @@ |
@@ -48,9 +48,15 @@ // Generated by CoffeeScript 1.10.0 | ||
searchRets = function(queryOptions) { | ||
searchRets = function(_queryOptions) { | ||
return Promise["try"]((function(_this) { | ||
return function() { | ||
var finalQueryOptions; | ||
finalQueryOptions = queryOptionHelpers.normalizeOptions(queryOptions); | ||
return retsHttp.callRetsMethod('search', _this.retsSession, finalQueryOptions).then(function(result) { | ||
return result.body; | ||
var queryOptions; | ||
queryOptions = queryOptionHelpers.normalizeOptions(_queryOptions); | ||
return retsHttp.callRetsMethod({ | ||
retsMethod: 'search', | ||
queryOptions: queryOptions | ||
}, _this.retsSession, _this.client).then(function(retsContext) { | ||
return { | ||
text: retsContext.body, | ||
headerInfo: retsContext.headerInfo | ||
}; | ||
}); | ||
@@ -93,3 +99,3 @@ }; | ||
return function(resolve, reject) { | ||
var currEntry, result; | ||
var currEntry, result, retsContext; | ||
result = { | ||
@@ -100,3 +106,4 @@ results: [], | ||
currEntry = null; | ||
return _this.stream.query(resourceType, classType, queryString, options, null, parserEncoding).pipe(through2.obj(function(event, encoding, callback) { | ||
retsContext = _this.stream.query(resourceType, classType, queryString, options, null, parserEncoding); | ||
return retsContext.retsStream.pipe(through2.obj(function(event, encoding, callback) { | ||
var key, ref, ref1, value; | ||
@@ -125,2 +132,3 @@ switch (event.type) { | ||
} | ||
result.headerInfo = retsContext.headerInfo; | ||
resolve(result); | ||
@@ -127,0 +135,0 @@ break; |
@@ -41,17 +41,18 @@ // Generated by CoffeeScript 1.10.0 | ||
searchRets = function(queryOptions, headerInfoCallback) { | ||
searchRets = function(_options, responseHandler) { | ||
return Promise["try"]((function(_this) { | ||
return function() { | ||
var finalQueryOptions, httpStream, onError, resultStream; | ||
finalQueryOptions = queryOptionHelpers.normalizeOptions(queryOptions); | ||
resultStream = through2(); | ||
onError = function(err) { | ||
httpStream.unpipe(resultStream); | ||
return resultStream.emit('error', err); | ||
}; | ||
if (!headerInfoCallback) { | ||
headerInfoCallback = function() {}; | ||
} | ||
httpStream = retsHttp.streamRetsMethod('search', _this.retsSession, finalQueryOptions, onError, headerInfoCallback, _this.client); | ||
return httpStream.pipe(resultStream); | ||
var queryOptions; | ||
queryOptions = queryOptionHelpers.normalizeOptions(_options); | ||
return retsHttp.streamRetsMethod({ | ||
retsMethod: 'search', | ||
queryOptions: queryOptions, | ||
responseHandler: responseHandler, | ||
parser: through2() | ||
}, _this.retsSession, _this.client).then(function(retsContext) { | ||
return { | ||
headerInfo: retsContext.headerInfo, | ||
rawStream: retsContext.parser | ||
}; | ||
}); | ||
}; | ||
@@ -85,6 +86,6 @@ })(this)); | ||
query = function(resourceType, classType, queryString, options, rawData, parserEncoding) { | ||
var baseOpts, context, finalQueryOptions, queryOptions; | ||
if (options == null) { | ||
options = {}; | ||
query = function(resourceType, classType, queryString, _options, rawData, parserEncoding) { | ||
var baseOpts, mainOptions, queryOptions, retsContext; | ||
if (_options == null) { | ||
_options = {}; | ||
} | ||
@@ -102,9 +103,15 @@ if (rawData == null) { | ||
}; | ||
queryOptions = queryOptionHelpers.mergeOptions(baseOpts, options); | ||
delete queryOptions.queryType; | ||
delete queryOptions.format; | ||
finalQueryOptions = queryOptionHelpers.normalizeOptions(queryOptions); | ||
context = retsParsing.getStreamParser('search', null, rawData, parserEncoding); | ||
retsHttp.streamRetsMethod('search', this.retsSession, finalQueryOptions, context.fail, context.response, this.client).pipe(context.parser); | ||
return context.retsStream; | ||
mainOptions = queryOptionHelpers.mergeOptions(baseOpts, _options); | ||
delete mainOptions.queryType; | ||
delete mainOptions.format; | ||
queryOptions = queryOptionHelpers.normalizeOptions(mainOptions); | ||
retsContext = retsParsing.getStreamParser({ | ||
retsMethod: 'search', | ||
queryOptions: queryOptions | ||
}, null, rawData, parserEncoding); | ||
retsHttp.streamRetsMethod(retsContext, this.retsSession, this.client); | ||
return { | ||
headerInfo: retsContext.headerInfo, | ||
retsStream: retsContext.retsStream | ||
}; | ||
}; | ||
@@ -111,0 +118,0 @@ |
@@ -28,14 +28,16 @@ // Generated by CoffeeScript 1.10.0 | ||
login = function(retsSession, client) { | ||
return retsHttp.callRetsMethod('login', Promise.promisify(retsSession), {}).then(function(retsResponse) { | ||
return retsHttp.callRetsMethod({ | ||
retsMethod: 'login', | ||
queryOptions: {} | ||
}, retsSession, client).then(function(retsContext) { | ||
return new Promise(function(resolve, reject) { | ||
var gotData, headerCookie, headerCookies, headers, i, len, matches, retsParser, systemData, typeIsArray; | ||
headers = headersHelper.processHeaders(retsResponse.response.rawHeaders); | ||
if (client.settings.userAgentPassword && headers.setCookie) { | ||
var gotData, headerCookie, headerCookies, i, len, matches, retsParser, systemData, typeIsArray; | ||
if (client.settings.userAgentPassword && retsContext.headerInfo.setCookie) { | ||
typeIsArray = Array.isArray || function(value) { | ||
return {}.toString.call(value) === '[object Array]'; | ||
}; | ||
if (typeIsArray(headers.setCookie)) { | ||
headerCookies = headers.setCookie; | ||
if (typeIsArray(retsContext.headerInfo.setCookie)) { | ||
headerCookies = retsContext.headerInfo.setCookie; | ||
} else { | ||
headerCookies = [headers.setCookie]; | ||
headerCookies = [retsContext.headerInfo.setCookie]; | ||
} | ||
@@ -51,7 +53,4 @@ for (i = 0, len = headerCookies.length; i < len; i++) { | ||
} | ||
systemData = { | ||
retsVersion: headers.retsVersion, | ||
retsServer: headers.server | ||
}; | ||
retsParser = retsParsing.getSimpleParser('login', reject, headers); | ||
retsParser = retsParsing.getSimpleParser(retsContext, reject); | ||
systemData = {}; | ||
gotData = false; | ||
@@ -83,8 +82,11 @@ retsParser.parser.on('text', function(text) { | ||
if (!gotData) { | ||
return reject(new errors.RetsProcessingError('login', 'Failed to parse data', headers)); | ||
return reject(new errors.RetsProcessingError(retsContext, 'Failed to parse data')); | ||
} else { | ||
return resolve(systemData); | ||
return resolve({ | ||
systemData: systemData, | ||
headerInfo: retsContext.headerInfo | ||
}); | ||
} | ||
}); | ||
retsParser.parser.write(retsResponse.body); | ||
retsParser.parser.write(retsContext.body); | ||
return retsParser.parser.end(); | ||
@@ -100,4 +102,11 @@ }); | ||
logout = function(retsSession) { | ||
return retsHttp.callRetsMethod('logout', Promise.promisify(retsSession), {}); | ||
logout = function(retsSession, client) { | ||
return retsHttp.callRetsMethod({ | ||
retsMethod: 'logout', | ||
queryOptions: {} | ||
}, retsSession, client).then(function(retsContext) { | ||
return { | ||
headerInfo: retsContext.headerInfo | ||
}; | ||
}); | ||
}; | ||
@@ -104,0 +113,0 @@ |
@@ -52,4 +52,3 @@ // Generated by CoffeeScript 1.10.0 | ||
function RetsReplyError(retsMethod1, replyCode, replyText, _headerInfo) { | ||
this.retsMethod = retsMethod1; | ||
function RetsReplyError(retsContext, replyCode, replyText) { | ||
this.replyCode = replyCode; | ||
@@ -59,4 +58,4 @@ this.replyText = replyText; | ||
this.replyTag = replyCodes.tagMap[this.replyCode] != null ? replyCodes.tagMap[this.replyCode] : 'unknown reply code'; | ||
this.retsMethod = retsContext.retsMethod, this.queryOptions = retsContext.queryOptions, this.headerInfo = retsContext.headerInfo; | ||
this.message = "RETS Server reply while attempting " + this.retsMethod + " - ReplyCode " + this.replyCode + " (" + this.replyTag + "); ReplyText: " + this.replyText; | ||
this.headerInfo = headersHelper.processHeaders(_headerInfo); | ||
Error.captureStackTrace(this, RetsReplyError); | ||
@@ -72,9 +71,8 @@ } | ||
function RetsServerError(retsMethod1, httpStatus, httpStatusMessage, _headerInfo) { | ||
this.retsMethod = retsMethod1; | ||
function RetsServerError(retsContext, httpStatus, httpStatusMessage) { | ||
this.httpStatus = httpStatus; | ||
this.httpStatusMessage = httpStatusMessage; | ||
this.name = 'RetsServerError'; | ||
this.retsMethod = retsContext.retsMethod, this.queryOptions = retsContext.queryOptions, this.headerInfo = retsContext.headerInfo; | ||
this.message = "RETS Server error while attempting " + this.retsMethod + " - HTTP Status " + this.httpStatus + " returned (" + this.httpStatusMessage + ")"; | ||
this.headerInfo = headersHelper.processHeaders(_headerInfo); | ||
Error.captureStackTrace(this, RetsServerError); | ||
@@ -90,8 +88,7 @@ } | ||
function RetsProcessingError(retsMethod1, sourceError, _headerInfo) { | ||
this.retsMethod = retsMethod1; | ||
function RetsProcessingError(retsContext, sourceError) { | ||
this.sourceError = sourceError; | ||
this.name = 'RetsProcessingError'; | ||
this.retsMethod = retsContext.retsMethod, this.queryOptions = retsContext.queryOptions, this.headerInfo = retsContext.headerInfo; | ||
this.message = "Error while processing RETS response for " + this.retsMethod + " - " + (getErrorMessage(this.sourceError)); | ||
this.headerInfo = headersHelper.processHeaders(_headerInfo); | ||
Error.captureStackTrace(this, RetsProcessingError); | ||
@@ -136,7 +133,7 @@ } | ||
ensureRetsError = function(retsMethod, error, headerInfo) { | ||
ensureRetsError = function(retsContext, error) { | ||
if (error instanceof RetsError) { | ||
return error; | ||
} else { | ||
return new RetsProcessingError(retsMethod, error, headerInfo); | ||
return new RetsProcessingError(retsContext, error); | ||
} | ||
@@ -143,0 +140,0 @@ }; |
@@ -10,3 +10,3 @@ // Generated by CoffeeScript 1.10.0 | ||
'use strict'; | ||
var MultipartParser, Promise, debug, debugVerbose, errors, getObjectStream, retsParsing, through2; | ||
var MultipartParser, Promise, debug, debugVerbose, errors, getObjectStream, headersHelper, retsParsing, through2; | ||
@@ -27,14 +27,20 @@ MultipartParser = require('formidable/lib/multipart_parser').MultipartParser; | ||
getObjectStream = function(headerInfo, stream, handler, options) { | ||
headersHelper = require('./headers'); | ||
getObjectStream = function(retsContext, handler) { | ||
return new Promise(function(resolve, reject) { | ||
var bodyStream, done, flush, flushed, handleEnd, handleError, headerField, headerValue, headers, interceptor, multipartBoundary, objectStream, parser, partDone, ref, ref1, streamError; | ||
multipartBoundary = (ref = headerInfo.contentType.match(/boundary="[^"]+"/ig)) != null ? ref[0].slice('boundary="'.length, -1) : void 0; | ||
multipartBoundary = (ref = retsContext.headerInfo.contentType.match(/boundary="[^"]+"/ig)) != null ? ref[0].slice('boundary="'.length, -1) : void 0; | ||
if (!multipartBoundary) { | ||
multipartBoundary = (ref1 = headerInfo.contentType.match(/boundary=[^;]+/ig)) != null ? ref1[0].slice('boundary='.length) : void 0; | ||
multipartBoundary = (ref1 = retsContext.headerInfo.contentType.match(/boundary=[^;]+/ig)) != null ? ref1[0].slice('boundary='.length) : void 0; | ||
} | ||
if (!multipartBoundary) { | ||
throw new errors.RetsProcessingError('getObject', 'Could not find multipart boundary', headerInfo); | ||
throw new errors.RetsProcessingError(retsContext, 'Could not find multipart boundary'); | ||
} | ||
parser = new MultipartParser(); | ||
objectStream = through2.obj(); | ||
objectStream.write({ | ||
type: 'headerInfo', | ||
headerInfo: retsContext.headerInfo | ||
}); | ||
headerField = ''; | ||
@@ -59,3 +65,5 @@ headerValue = ''; | ||
err = { | ||
error: err | ||
type: 'error', | ||
error: err, | ||
headerInfo: retsContext.headerInfo | ||
}; | ||
@@ -71,3 +79,3 @@ } | ||
} else { | ||
return debug("handleEnd not ready " + (JSON.stringify({ | ||
return debug("handleEnd not ready: " + (JSON.stringify({ | ||
done: done, | ||
@@ -108,8 +116,15 @@ partDone: partDone, | ||
parser.onHeadersEnd = function() { | ||
var newRetsContext; | ||
debug("onHeadersEnd: [" + (headers.length / 2) + " headers parsed]"); | ||
bodyStream = through2(); | ||
return handler(headers, bodyStream, false, options).then(function(object) { | ||
newRetsContext = { | ||
retsMethod: retsContext.retsMethod, | ||
queryOptions: retsContext.queryOptions, | ||
headerInfo: headersHelper.processHeaders(headers), | ||
parser: bodyStream | ||
}; | ||
return handler(newRetsContext, false).then(function(object) { | ||
return objectStream != null ? objectStream.write(object) : void 0; | ||
})["catch"](function(err) { | ||
return handleError(errors.ensureRetsError('getObject', err, headers)); | ||
return handleError(errors.ensureRetsError(newRetsContext, err)); | ||
}).then(function() { | ||
@@ -137,3 +152,3 @@ partDone = true; | ||
parser.initWithBoundary(multipartBoundary); | ||
stream.on('error', function(err) { | ||
retsContext.parser.on('error', function(err) { | ||
debug("stream error"); | ||
@@ -151,3 +166,3 @@ return handleError(err); | ||
if (err) { | ||
handleError(new errors.RetsProcessingError('getObject', "Unexpected end of data: " + (errors.getErrorMessage(err)), headerInfo)); | ||
handleError(new errors.RetsProcessingError(retsContext, "Unexpected end of data: " + (errors.getErrorMessage(err)))); | ||
} | ||
@@ -158,3 +173,3 @@ flushed = true; | ||
}; | ||
stream.pipe(through2(interceptor, flush)); | ||
retsContext.parser.pipe(through2(interceptor, flush)); | ||
return resolve(objectStream); | ||
@@ -161,0 +176,0 @@ }); |
@@ -22,10 +22,15 @@ // Generated by CoffeeScript 1.10.0 | ||
callRetsMethod = function(methodName, retsSession, queryOptions) { | ||
debug("RETS " + methodName + ":", queryOptions); | ||
callRetsMethod = function(retsContext, promisifiedRetsSession, client) { | ||
debug("RETS " + retsContext.retsMethod + ":", retsContext.queryOptions); | ||
return Promise["try"](function() { | ||
return retsSession({ | ||
qs: queryOptions | ||
}); | ||
var request; | ||
request = {}; | ||
if (client.settings.method === 'POST') { | ||
request.form = retsContext.queryOptions; | ||
} else { | ||
request.qs = retsContext.queryOptions; | ||
} | ||
return promisifiedRetsSession(request); | ||
})["catch"](function(error) { | ||
debug("RETS " + methodName + " error:", error); | ||
debug("RETS " + retsContext.retsMethod + " error:", error); | ||
return Promise.reject(error); | ||
@@ -35,17 +40,16 @@ }).spread(function(response, body) { | ||
if (response.statusCode !== 200) { | ||
error = new errors.RetsServerError(methodName, response.statusCode, response.statusMessage, response.rawHeaders); | ||
debug("RETS " + methodName + " error: " + error.message); | ||
error = new errors.RetsServerError(retsContext, response.statusCode, response.statusMessage); | ||
debug("RETS " + retsContext.retsMethod + " error: " + error.message); | ||
return Promise.reject(error); | ||
} | ||
return { | ||
body: body, | ||
response: response, | ||
headerInfo: headersHelper.processHeaders(response.rawHeaders) | ||
}; | ||
retsContext.headerInfo = headersHelper.processHeaders(response.rawHeaders); | ||
retsContext.body = body; | ||
retsContext.response = response; | ||
return retsContext; | ||
}); | ||
}; | ||
streamRetsMethod = function(methodName, retsSession, queryOptions, failCallback, responseCallback, client) { | ||
streamRetsMethod = function(retsContext, regularRetsSession, client) { | ||
var done, errorHandler, request, responseHandler, stream; | ||
debug("RETS " + methodName + " (streaming)", queryOptions); | ||
debug("RETS " + retsContext.retsMethod + " (streaming)", retsContext.queryOptions); | ||
done = false; | ||
@@ -57,4 +61,4 @@ errorHandler = function(error) { | ||
done = true; | ||
debug("RETS " + methodName + " (streaming) error:", error); | ||
return failCallback(error); | ||
debug("RETS " + retsContext.retsMethod + " (streaming) error:", error); | ||
return retsContext.errorHandler(error); | ||
}; | ||
@@ -67,8 +71,9 @@ responseHandler = function(response) { | ||
done = true; | ||
retsContext.headerInfo = headersHelper.processHeaders(response.rawHeaders); | ||
if (response.statusCode !== 200) { | ||
error = new errors.RetsServerError(methodName, response.statusCode, response.statusMessage, response.rawHeaders); | ||
debug("RETS " + methodName + " (streaming) error: " + error.message); | ||
return failCallback(error); | ||
} else if (responseCallback) { | ||
return responseCallback(response); | ||
error = new errors.RetsServerError(retsContext, response.statusCode, response.statusMessage); | ||
debug("RETS " + retsContext.retsMethod + " (streaming) error: " + error.message); | ||
return typeof retsContext.errorHandler === "function" ? retsContext.errorHandler(error) : void 0; | ||
} else { | ||
return typeof retsContext.responseHandler === "function" ? retsContext.responseHandler(response) : void 0; | ||
} | ||
@@ -78,7 +83,7 @@ }; | ||
if (client.settings.method === 'POST') { | ||
request.form = queryOptions; | ||
request.form = retsContext.queryOptions; | ||
} else { | ||
request.qs = queryOptions; | ||
request.qs = retsContext.queryOptions; | ||
} | ||
if (methodName === 'getObject') { | ||
if (retsContext.retsMethod === 'getObject') { | ||
request.headers = { | ||
@@ -88,5 +93,7 @@ Accept: '*/*' | ||
} | ||
stream = retsSession(request); | ||
stream = regularRetsSession(request); | ||
stream.on('error', errorHandler); | ||
return stream.on('response', responseHandler); | ||
stream.on('response', responseHandler); | ||
stream.pipe(retsContext.parser); | ||
return retsContext; | ||
}; | ||
@@ -93,0 +100,0 @@ |
@@ -24,3 +24,3 @@ // Generated by CoffeeScript 1.10.0 | ||
getSimpleParser = function(retsMethod, errCallback, headerInfo, parserEncoding) { | ||
getSimpleParser = function(retsContext, errCallback, parserEncoding) { | ||
var result; | ||
@@ -41,3 +41,3 @@ if (parserEncoding == null) { | ||
result.finish(); | ||
return errCallback(new errors.RetsProcessingError(retsMethod, 'Unexpected results. Please check the RETS URL.', headerInfo)); | ||
return errCallback(new errors.RetsProcessingError(retsContext, 'Unexpected results. Please check the RETS URL.')); | ||
} | ||
@@ -50,6 +50,10 @@ }); | ||
} | ||
result.status = attrs; | ||
result.status = { | ||
replyCode: attrs.ReplyCode, | ||
replyTag: replyCodes.tagMap[attrs.ReplyCode], | ||
replyText: attrs.ReplyText | ||
}; | ||
if (attrs.ReplyCode !== '0' && attrs.ReplyCode !== '20208') { | ||
result.finish(); | ||
return errCallback(new errors.RetsReplyError(retsMethod, attrs.ReplyCode, attrs.ReplyText, headerInfo)); | ||
return errCallback(new errors.RetsReplyError(retsContext, attrs.ReplyCode, attrs.ReplyText)); | ||
} | ||
@@ -59,7 +63,7 @@ }); | ||
result.finish(); | ||
return errCallback(new errors.RetsProcessingError(retsMethod, "XML parsing error: " + (errors.getErrorMessage(err)), headerInfo)); | ||
return errCallback(new errors.RetsProcessingError(retsContext, "XML parsing error: " + (errors.getErrorMessage(err)))); | ||
}); | ||
result.parser.on('end', function() { | ||
result.finish(); | ||
return errCallback(new errors.RetsProcessingError(retsMethod, "Unexpected end of xml stream.", headerInfo)); | ||
return errCallback(new errors.RetsProcessingError(retsContext, "Unexpected end of xml stream.")); | ||
}); | ||
@@ -69,4 +73,4 @@ return result; | ||
getStreamParser = function(retsMethod, metadataTag, rawData, parserEncoding) { | ||
var columnText, columns, currElementName, dataText, delimiter, fail, finish, headers, parser, processStatus, response, result, retsStream, writeOutput; | ||
getStreamParser = function(retsContext, metadataTag, rawData, parserEncoding) { | ||
var columnText, columns, currElementName, dataText, delimiter, errorHandler, finish, headers, parser, processStatus, responseHandler, result, retsStream, writeOutput; | ||
if (parserEncoding == null) { | ||
@@ -108,3 +112,3 @@ parserEncoding = 'UTF-8'; | ||
}; | ||
fail = function(err) { | ||
errorHandler = function(err) { | ||
return finish('error', err); | ||
@@ -118,5 +122,4 @@ }; | ||
}; | ||
response = function(response) { | ||
headers = headersHelper.processHeaders(response.rawHeaders); | ||
return writeOutput('headerInfo', headers); | ||
responseHandler = function() { | ||
return writeOutput('headerInfo', retsContext.headerInfo); | ||
}; | ||
@@ -126,3 +129,3 @@ processStatus = function(attrs) { | ||
if (attrs.ReplyCode !== '0' && attrs.ReplyCode !== '20208') { | ||
return fail(new errors.RetsReplyError(retsMethod, attrs.ReplyCode, attrs.ReplyText, headers)); | ||
return errorHandler(new errors.RetsReplyError(retsContext, attrs.ReplyCode, attrs.ReplyText)); | ||
} | ||
@@ -138,3 +141,3 @@ status = { | ||
if (name !== 'RETS') { | ||
return fail(new errors.RetsProcessingError(retsMethod, 'Unexpected results. Please check the RETS URL.', headers)); | ||
return errorHandler(new errors.RetsProcessingError(retsContext, 'Unexpected results. Please check the RETS URL.')); | ||
} | ||
@@ -200,3 +203,3 @@ processStatus(attrs); | ||
if (!columns) { | ||
return fail(new errors.RetsProcessingError(retsMethod, 'Failed to parse columns', headers)); | ||
return errorHandler(new errors.RetsProcessingError(retsContext, 'Failed to parse columns')); | ||
} | ||
@@ -214,3 +217,3 @@ data = dataText.split(delimiter); | ||
if (!delimiter) { | ||
return fail(new errors.RetsProcessingError(retsMethod, 'Failed to parse delimiter', headers)); | ||
return errorHandler(new errors.RetsProcessingError(retsContext, 'Failed to parse delimiter')); | ||
} | ||
@@ -231,13 +234,12 @@ columns = columnText.split(delimiter); | ||
parser.on('error', function(err) { | ||
return fail(new errors.RetsProcessingError(retsMethod, "XML parsing error: " + (errors.getErrorMessage(err)), headers)); | ||
return errorHandler(new errors.RetsProcessingError(retsContext, "XML parsing error: " + (errors.getErrorMessage(err)))); | ||
}); | ||
parser.on('end', function() { | ||
return fail(new errors.RetsProcessingError(retsMethod, "Unexpected end of xml stream.", headers)); | ||
return errorHandler(new errors.RetsProcessingError(retsContext, "Unexpected end of xml stream.")); | ||
}); | ||
return { | ||
parser: parser, | ||
fail: fail, | ||
retsStream: retsStream, | ||
response: response | ||
}; | ||
retsContext.parser = parser; | ||
retsContext.errorHandler = errorHandler; | ||
retsContext.responseHandler = responseHandler; | ||
retsContext.retsStream = retsStream; | ||
return retsContext; | ||
}; | ||
@@ -244,0 +246,0 @@ |
{ | ||
"name": "rets-client", | ||
"version": "4.6.11", | ||
"version": "5.0.0", | ||
"description": "A RETS client (Real Estate Transaction Standard).", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
423
README.md
@@ -8,26 +8,27 @@ rets-client | ||
#### 4.6.0 | ||
Added support for `Location: 1` option on `getObject` calls. | ||
#### 5.0 | ||
A significant amount of internal cleanup, resulting in more consistent code and API. There are some minor breaking | ||
changes, but they're small enough that migrating to 5.0 shouldn't be much effort. The | ||
[Example Usage](https://github.com/sbruno81/rets-client#example-usage) has been updated to show 5.x patterns. | ||
#### 4.5.0 | ||
Added a new error class for RETS permissions problems on login. | ||
- object stream queries now have events with a `type` field to make discrimination easier, with the following | ||
possibilities: | ||
- `dataStream`, for a stream containing an object's raw data | ||
- `location`, when no stream is available but a URL is available in the `headerInfo` (as per the `Location: 1` option) | ||
- `headerInfo`, with the headers for the outer multipart response | ||
- `error`, for an error corresponding to a single object rather than the stream as a whole | ||
- search stream queries now return an object with a `retsStream` field rather than the bare stream | ||
- the `searchRets` method now returns an object with a `rawStream` field rather than the bare stream | ||
- headerInfo is now available on every query made | ||
- login and logout set `client.loginHeaderInfo` and `client.logoutHeaderInfo` | ||
- every streaming query will include an event with `type: 'headerInfo'` | ||
- every buffered query (and most streaming queries) will return an object including a `headerInfo` field | ||
- errors now consistently include response headers as well as the request options | ||
- all calls made to the RETS server now obey `settings.method` for POST vs GET | ||
#### 4.4.0 | ||
Added a new `parserEncoding` param for `client.search.query()` and `client.search.stream.query()` calls. UTF-8 is the | ||
default value, so this is to support RETS servers using ISO-8859-1 or some other encoding. | ||
#### 4.3.0 | ||
Added support for TimeZoneOffset and Comments in the `metadata.getSystem()` call response. | ||
#### 4.X and earlier | ||
See [the changelog](./CHANGELOG.md) for earlier changes. | ||
#### 4.2.0 | ||
Improved error handling and error classes. See the [error documentation](https://github.com/sbruno81/rets-client#errors). | ||
#### 4.1.0 | ||
Added client configuration option to use POST instead of GET for all requests, as this seems to work better for some | ||
RETS servers. See the [Example Usage](https://github.com/sbruno81/rets-client#client-configuration). | ||
#### 4.0.0 and earlier | ||
See [the changelog](./blob/master/CHANGELOG.md) for earlier changes. | ||
## Implementation Notes | ||
@@ -57,2 +58,3 @@ | ||
- create unit tests -- specifically ones that run off example RETS data rather than requiring access to a real RETS server | ||
- refactor streaming API to correctly respond to backpressure | ||
@@ -91,5 +93,8 @@ | ||
```javascript | ||
function outputFields(obj, opts) { | ||
function outputFields(obj, opts) { | ||
if (!obj) { | ||
console.log(" "+JSON.stringify(obj)) | ||
} else { | ||
if (!opts) opts = {}; | ||
var excludeFields; | ||
@@ -107,3 +112,3 @@ var loopFields; | ||
} | ||
for (var i=0; i<loopFields.length; i++) { | ||
for (var i = 0; i < loopFields.length; i++) { | ||
if (excludeFields.indexOf(loopFields[i]) != -1) { | ||
@@ -113,9 +118,10 @@ continue; | ||
if (typeof(obj[loopFields[i]]) == 'object') { | ||
console.log(" "+loopFields[i]+": "+JSON.stringify(obj[loopFields[i]])); | ||
console.log(" " + loopFields[i] + ": " + JSON.stringify(obj[loopFields[i]], null, 2).replace(/\n/g, '\n ')); | ||
} else { | ||
console.log(" "+loopFields[i]+": "+obj[loopFields[i]]); | ||
console.log(" " + loopFields[i] + ": " + JSON.stringify(obj[loopFields[i]])); | ||
} | ||
} | ||
console.log(""); | ||
}; | ||
} | ||
console.log(""); | ||
} | ||
``` | ||
@@ -125,61 +131,73 @@ | ||
```javascript | ||
var rets = require('rets-client'); | ||
var fs = require('fs'); | ||
var photoSourceId = '12345'; // <--- dummy example ID! this will usually be a MLS number / listing id | ||
var rets = require('rets-client'); | ||
var fs = require('fs'); | ||
var photoSourceId = '12345'; // <--- dummy example ID! this will usually be a MLS number / listing id | ||
// establish connection to RETS server which auto-logs out when we're done | ||
rets.getAutoLogoutClient(clientSettings, function (client) { | ||
console.log("==================================="); | ||
console.log("======== System Metadata ========"); | ||
console.log("==================================="); | ||
outputFields(client.systemData); | ||
//get resources metadata | ||
return client.metadata.getResources() | ||
.then(function (data) { | ||
console.log("======================================"); | ||
console.log("======== Resources Metadata ========"); | ||
console.log("======================================"); | ||
outputFields(data.results[0].info); | ||
for (var dataItem = 0; dataItem < data.results[0].metadata.length; dataItem++) { | ||
console.log("-------- Resource " + dataItem + " --------"); | ||
outputFields(data.results[0].metadata[dataItem], {fields: ['ResourceID', 'StandardName', 'VisibleName', 'ObjectVersion']}); | ||
} | ||
}).then(function () { | ||
//get class metadata | ||
return client.metadata.getClass("Property"); | ||
}).then(function (data) { | ||
console.log("==========================================================="); | ||
console.log("======== Class Metadata (from Property Resource) ========"); | ||
console.log("==========================================================="); | ||
outputFields(data.results[0].info); | ||
for (var classItem = 0; classItem < data.results[0].metadata.length; classItem++) { | ||
console.log("-------- Table " + classItem + " --------"); | ||
outputFields(data.results[0].metadata[classItem], {fields: ['ClassName', 'StandardName', 'VisibleName', 'TableVersion']}); | ||
} | ||
}).then(function () { | ||
//get field data for open houses | ||
return client.metadata.getTable("Property", "RESIDENTIAL"); | ||
}).then(function (data) { | ||
console.log("=============================================="); | ||
console.log("======== Residential Table Metadata ========"); | ||
console.log("==============================================="); | ||
outputFields(data.results[0].info); | ||
for (var tableItem = 0; tableItem < data.results[0].metadata.length; tableItem++) { | ||
console.log("-------- Field " + tableItem + " --------"); | ||
outputFields(data.results[0].metadata[tableItem], {fields: ['MetadataEntryID', 'SystemName', 'ShortName', 'LongName', 'DataType']}); | ||
} | ||
return data.results[0].metadata | ||
}).then(function (fieldsData) { | ||
var plucked = []; | ||
for (var fieldItem = 0; fieldItem < fieldsData.length; fieldItem++) { | ||
plucked.push(fieldsData[fieldItem].SystemName); | ||
} | ||
return plucked; | ||
}).then(function (fields) { | ||
//perform a query using DMQL2 -- pass resource, class, and query, and options | ||
return client.search.query("Property", "RESIDENTIAL", "(RecordModDate=2016-06-20+),(ActiveYN=1)", {limit:100, offset:10}) | ||
// establish connection to RETS server which auto-logs out when we're done | ||
rets.getAutoLogoutClient(clientSettings, function (client) { | ||
console.log("==================================="); | ||
console.log("======== System Metadata ========"); | ||
console.log("==================================="); | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(client.loginHeaderInfo); | ||
console.log(' ~~~~~~~~~ System Data ~~~~~~~~~'); | ||
outputFields(client.systemData); | ||
//get resources metadata | ||
return client.metadata.getResources() | ||
.then(function (data) { | ||
console.log("======================================"); | ||
console.log("======== Resources Metadata ========"); | ||
console.log("======================================"); | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(data.headerInfo); | ||
console.log(' ~~~~~~ Resources Metadata ~~~~~'); | ||
outputFields(data.results[0].info); | ||
for (var dataItem = 0; dataItem < data.results[0].metadata.length; dataItem++) { | ||
console.log(" -------- Resource " + dataItem + " --------"); | ||
outputFields(data.results[0].metadata[dataItem], {fields: ['ResourceID', 'StandardName', 'VisibleName', 'ObjectVersion']}); | ||
} | ||
}).then(function () { | ||
//get class metadata | ||
return client.metadata.getClass("Property"); | ||
}).then(function (data) { | ||
console.log("==========================================================="); | ||
console.log("======== Class Metadata (from Property Resource) ========"); | ||
console.log("==========================================================="); | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(data.headerInfo); | ||
console.log(' ~~~~~~~~ Class Metadata ~~~~~~~'); | ||
outputFields(data.results[0].info); | ||
for (var classItem = 0; classItem < data.results[0].metadata.length; classItem++) { | ||
console.log(" -------- Table " + classItem + " --------"); | ||
outputFields(data.results[0].metadata[classItem], {fields: ['ClassName', 'StandardName', 'VisibleName', 'TableVersion']}); | ||
} | ||
}).then(function () { | ||
//get field data for open houses | ||
return client.metadata.getTable("Property", "RESIDENTIAL"); | ||
}).then(function (data) { | ||
console.log("=============================================="); | ||
console.log("======== Residential Table Metadata ========"); | ||
console.log("==============================================="); | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(data.headerInfo); | ||
console.log(' ~~~~~~~~ Table Metadata ~~~~~~~'); | ||
outputFields(data.results[0].info); | ||
for (var tableItem = 0; tableItem < data.results[0].metadata.length; tableItem++) { | ||
console.log(" -------- Field " + tableItem + " --------"); | ||
outputFields(data.results[0].metadata[tableItem], {fields: ['MetadataEntryID', 'SystemName', 'ShortName', 'LongName', 'DataType']}); | ||
} | ||
return data.results[0].metadata | ||
}).then(function (fieldsData) { | ||
var plucked = []; | ||
for (var fieldItem = 0; fieldItem < fieldsData.length; fieldItem++) { | ||
plucked.push(fieldsData[fieldItem].SystemName); | ||
} | ||
return plucked; | ||
}).then(function (fields) { | ||
//perform a query using DMQL2 -- pass resource, class, and query, and options | ||
return client.search.query("Property", "RESIDENTIAL", "(RecordModDate=2016-06-20+),(ActiveYN=1)", {limit:100, offset:10}) | ||
.then(function (searchData) { | ||
@@ -189,38 +207,44 @@ console.log("============================================="); | ||
console.log("============================================="); | ||
outputFields(searchData, {exclude: ['results']}); | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(searchData.headerInfo); | ||
console.log(' ~~~~~~~~~~ Query Info ~~~~~~~~~'); | ||
outputFields(searchData, {exclude: ['results','headerInfo']}); | ||
//iterate through search results | ||
for (var dataItem = 0; dataItem < searchData.results.length; dataItem++) { | ||
console.log("-------- Result " + dataItem + " --------"); | ||
console.log(" -------- Result " + dataItem + " --------"); | ||
outputFields(searchData.results[dataItem], {fields: fields}); | ||
} | ||
if (searchData.maxRowsExceeded) { | ||
console.log("-------- More rows available!"); | ||
console.log(" -------- More rows available!"); | ||
} | ||
}); | ||
}).then(function () { | ||
// get photos | ||
return client.objects.getAllObjects("Property", "LargePhoto", photoSourceId, {alwaysGroupObjects: true, ObjectData: '*'}) | ||
}).then(function (photoResults) { | ||
console.log("================================="); | ||
console.log("======== Photo Results ========"); | ||
console.log("================================="); | ||
for (var i = 0; i < photoResults.objects.length; i++) { | ||
if (photoResults.objects[i].error) { | ||
console.log("Photo " + (i + 1) + " had an error: " + photoResults.objects[i].error); | ||
} else { | ||
console.log("Photo " + (i + 1) + ":"); | ||
outputFields(photoResults.objects[i].headerInfo); | ||
fs.writeFileSync( | ||
"/tmp/photo" + (i + 1) + "." + photoResults.objects[i].headerInfo.contentType.match(/\w+\/(\w+)/i)[1], | ||
photoResults.objects[i].data); | ||
} | ||
console.log("---------------------------------"); | ||
}).then(function () { | ||
// get photos | ||
return client.objects.getAllObjects("Property", "LargePhoto", photoSourceId, {alwaysGroupObjects: true, ObjectData: '*'}) | ||
}).then(function (photoResults) { | ||
console.log("================================="); | ||
console.log("======== Photo Results ========"); | ||
console.log("================================="); | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(photoResults.headerInfo); | ||
for (var i = 0; i < photoResults.objects.length; i++) { | ||
console.log(" -------- Photo " + (i + 1) + " --------"); | ||
if (photoResults.objects[i].error) { | ||
console.log(" Error: " + photoResults.objects[i].error); | ||
} else { | ||
outputFields(photoResults.objects[i].headerInfo); | ||
fs.writeFileSync( | ||
"/tmp/photo" + (i + 1) + "." + photoResults.objects[i].headerInfo.contentType.match(/\w+\/(\w+)/i)[1], | ||
photoResults.objects[i].data); | ||
} | ||
}); | ||
}).catch(function (errorInfo) { | ||
var error = errorInfo.error || errorInfo; | ||
console.log("ERROR: issue encountered: "+(error.stack||error)); | ||
}); | ||
} | ||
}); | ||
}).catch(function (errorInfo) { | ||
var error = errorInfo.error || errorInfo; | ||
console.log(" ERROR: issue encountered:"); | ||
outputFields(error); | ||
console.log(' '+(error.stack||error).replace(/\n/g, '\n ')); | ||
}); | ||
``` | ||
@@ -230,55 +254,66 @@ | ||
```javascript | ||
var rets = require('rets-client'); | ||
var through2 = require('through2'); | ||
var Promise = require('bluebird'); | ||
var rets = require('rets-client'); | ||
var through2 = require('through2'); | ||
var Promise = require('bluebird'); | ||
// this function doesn't do much, it's just a placeholder for whatever you want to do with the results | ||
function doAsyncProcessing(row, index, callback) { | ||
console.log("-------- Result " + index + " --------"); | ||
outputFields(row); | ||
// must be sure callback is called when this is done | ||
callback(); | ||
} | ||
// establish connection to RETS server which auto-logs out when we're done | ||
rets.getAutoLogoutClient(clientSettings, function (client) { | ||
// in order to have the auto-logout function work properly, we need to make a promise that either rejects or | ||
// resolves only once we're done processing the stream | ||
return new Promise(function (resolve, reject) { | ||
console.log("===================================="); | ||
console.log("======== Streamed Results ========"); | ||
console.log("===================================="); | ||
var count = 0; | ||
var retsStream = client.search.stream.query("OpenHouse", "OPENHOUSE", "(OpenHouseType=PUBLIC),(ActiveYN=1)", {limit:100, offset:10}); | ||
var processorStream = through2.obj(function (event, encoding, callback) { | ||
switch (event.type) { | ||
case 'data': | ||
// event.payload is an object representing a single row of results | ||
// make sure callback is called only when all processing is complete | ||
count++; | ||
doAsyncProcessing(event.payload, count, callback); | ||
break; | ||
case 'done': | ||
// event.payload is an object containing a count of rows actually received, plus some other things | ||
// now we can resolve the auto-logout promise | ||
resolve(event.payload.rowsReceived); | ||
callback(); | ||
break; | ||
case 'error': | ||
// event.payload is an Error object | ||
console.log('Error streaming RETS results: '+event.payload); | ||
retsStream.unpipe(processorStream); | ||
processorStream.end(); | ||
// we need to reject the auto-logout promise | ||
reject(event.payload); | ||
callback(); | ||
break; | ||
default: | ||
// ignore other events | ||
callback(); | ||
} | ||
}); | ||
retsStream.pipe(processorStream); | ||
// this function doesn't do much, it's just a placeholder for whatever you want to do with the results | ||
function doAsyncProcessing(row, index, callback) { | ||
console.log("-------- Result " + index + " --------"); | ||
outputFields(row); | ||
// must be sure callback is called when this is done | ||
callback(); | ||
} | ||
// establish connection to RETS server which auto-logs out when we're done | ||
rets.getAutoLogoutClient(clientSettings, function (client) { | ||
// in order to have the auto-logout function work properly, we need to make a promise that either rejects or | ||
// resolves only once we're done processing the stream | ||
return new Promise(function (resolve, reject) { | ||
console.log("===================================="); | ||
console.log("======== Streamed Results ========"); | ||
console.log("===================================="); | ||
var count = 0; | ||
var streamResult = client.search.stream.query("Property", "RES", "(LastChangeTimestamp=2016-06-20+)", {limit:10, offset:4}); | ||
var processorStream = through2.obj(function (event, encoding, callback) { | ||
switch (event.type) { | ||
case 'headerInfo': | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(event.payload); | ||
callback(); | ||
break; | ||
case 'data': | ||
// event.payload is an object representing a single row of results | ||
// make sure callback is called only when all processing is complete | ||
count++; | ||
doAsyncProcessing(event.payload, count, callback); | ||
break; | ||
case 'done': | ||
// event.payload is an object containing a count of rows actually received, plus some other things | ||
// now we can resolve the auto-logout promise | ||
resolve(event.payload.rowsReceived); | ||
callback(); | ||
break; | ||
case 'error': | ||
// event.payload is an Error object | ||
console.log('Error streaming RETS results: '+event.payload); | ||
streamResult.retsStream.unpipe(processorStream); | ||
processorStream.end(); | ||
// we need to reject the auto-logout promise | ||
reject(event.payload); | ||
callback(); | ||
break; | ||
default: | ||
// ignore other events | ||
callback(); | ||
} | ||
}); | ||
streamResult.retsStream.pipe(processorStream); | ||
}); | ||
}).catch(function (errorInfo) { | ||
var error = errorInfo.error || errorInfo; | ||
console.log(" ERROR: issue encountered:"); | ||
outputFields(error); | ||
console.log(' '+(error.stack||error).replace(/\n/g, '\n ')); | ||
}); | ||
``` | ||
@@ -288,27 +323,44 @@ | ||
```javascript | ||
var rets = require('rets-client'); | ||
// establish connection to RETS server which auto-logs out when we're done | ||
rets.getAutoLogoutClient(clientSettings, function (client) { | ||
// getObjects will accept a single string, an array of strings, or an object as shown below | ||
var photoIds = { | ||
'11111': [1,3], // get photos #1 and #3 for listingId 11111 | ||
'22222': '*', // get all photos for listingId 22222 | ||
'33333': '0' // get 'preferred' photo for listingId 33333 | ||
}; | ||
return client.objects.stream.getObjects('Property', 'Photo', photoIds, {alwaysGroupObjects: true, ObjectData: '*'}) | ||
var rets = require('rets-client'); | ||
var fs = require('fs'); | ||
// establish connection to RETS server which auto-logs out when we're done | ||
rets.getAutoLogoutClient(clientSettings, function (client) { | ||
// getObjects will accept a single string, an array of strings, or an object as shown below | ||
var photoIds = { | ||
'11111': [1,3], // get photos #1 and #3 for listingId 11111 | ||
'22222': '*', // get all photos for listingId 22222 | ||
'33333': '0' // get 'preferred' photo for listingId 33333 | ||
}; | ||
return client.objects.stream.getObjects('Property', 'LargePhoto', photoIds, {alwaysGroupObjects: true, ObjectData: '*'}) | ||
.then(function (photoStream) { | ||
console.log("========================================"); | ||
console.log("======== Photo Stream Results ========"); | ||
console.log("========================================"); | ||
return new Promise(function (resolve, reject) { | ||
var i=0; | ||
photoStream.objectStream.on('data', function (photoEvent) { | ||
i++; | ||
if (photoEvent.error) { | ||
console.log("Photo " + i + " had an error: " + photoEvent.error); | ||
} else { | ||
console.log("Photo " + (i + 1) + ":"); | ||
outputFields(photoEvent.headerInfo); | ||
fileStream = fs.createWriteStream( | ||
"/tmp/photo" + i + "." + photoEvent.headerInfo.contentType.match(/\w+\/(\w+)/i)[1]); | ||
photoEvent.dataStream.pipe(fileStream); | ||
photoStream.objectStream.on('data', function (event) { | ||
try { | ||
if (event.type == 'headerInfo') { | ||
console.log(' ~~~~~~~~~ Header Info ~~~~~~~~~'); | ||
outputFields(event.headerInfo); | ||
return | ||
} | ||
console.log(" -------- Photo " + (i + 1) + " --------"); | ||
if (event.type == 'error') { | ||
console.log(" Error: " + event.error); | ||
} else if (event.type == 'dataStream') { | ||
outputFields(event.headerInfo); | ||
fileStream = fs.createWriteStream( | ||
"/tmp/photo_" + event.headerInfo.contentId + "_" + event.headerInfo.objectId + "." + event.headerInfo.contentType.match(/\w+\/(\w+)/i)[1]); | ||
event.dataStream.pipe(fileStream); | ||
} | ||
i++; | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}); | ||
photoStream.objectStream.on('error', function (errorInfo) { | ||
reject(errorInfo); | ||
}); | ||
photoStream.objectStream.on('end', function () { | ||
@@ -318,4 +370,9 @@ resolve(); | ||
}); | ||
}).catch(function (errorInfo) { | ||
var error = errorInfo.error || errorInfo; | ||
console.log(" ERROR: issue encountered:"); | ||
outputFields(error); | ||
console.log(' '+(error.stack||error).replace(/\n/g, '\n ')); | ||
}); | ||
}); | ||
}); | ||
``` | ||
@@ -322,0 +379,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
101353
2044
393