Comparing version 2.3.0 to 2.4.0
@@ -112,3 +112,4 @@ 'use strict'; | ||
DELETE_ENTITY: 'DeleteEntity', | ||
QUERY_TABLE: 'QueryTable' | ||
QUERY_TABLE: 'QueryTable', | ||
QUERY_ENTITY: 'QueryEntity' | ||
} | ||
@@ -115,0 +116,0 @@ } |
@@ -51,3 +51,3 @@ 'use strict'; | ||
insertEntity(request) { | ||
const proxy = this._createEntity(request.tableName, request.payload); | ||
const proxy = this._createOrUpdateEntity(request.tableName, request.payload); | ||
return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy })); | ||
@@ -74,2 +74,3 @@ } | ||
.find({ name: request.tableName }) | ||
.limit(request.top) | ||
.data(); | ||
@@ -87,2 +88,3 @@ // there must be a table since we are validating its existence in validation pipeline | ||
}) | ||
.limit(request.top) | ||
.data(); | ||
@@ -92,2 +94,3 @@ } else { // Returning all tables | ||
.find({}) | ||
.limit(request.top) | ||
.data(); | ||
@@ -101,2 +104,27 @@ } | ||
queryEntities(request) { | ||
const coll = this.db.getCollection(request.tableName); | ||
const findExpr = {}; | ||
if (request.partitionKey) { | ||
findExpr['partitionKey'] = partitionKey; | ||
} | ||
if (request.rowKey) { | ||
findExpr['rowKey'] = rowKey; | ||
} | ||
const chain = coll.chain().find(findExpr); | ||
if (request.filter) { | ||
chain.where((item) => { | ||
return eval(request.filter) | ||
}); | ||
} | ||
const result = chain.limit(request.top).data(); | ||
const payload = []; | ||
for (const item of result) { | ||
payload.push(new EntityProxy(item)); | ||
} | ||
return BbPromise.resolve(new AzuriteTableResponse({ payload: payload })); | ||
} | ||
_getTable(name) { | ||
@@ -117,6 +145,6 @@ const coll = this.db.getCollection(Tables.Tables); | ||
{ | ||
PartitionKey: { '$eq': partitionKey } | ||
partitionKey: { '$eq': partitionKey } | ||
}, | ||
{ | ||
RowKey: { '$eq': rowKey } | ||
rowKey: { '$eq': rowKey } | ||
} | ||
@@ -138,6 +166,6 @@ ] | ||
{ | ||
PartitionKey: { '$eq': partitionKey } | ||
partitionKey: { '$eq': partitionKey } | ||
}, | ||
{ | ||
RowKey: { '$eq': rowKey } | ||
rowKey: { '$eq': rowKey } | ||
} | ||
@@ -150,6 +178,16 @@ ] | ||
_createEntity(tableName, rawEntity) { | ||
_createOrUpdateEntity(tableName, rawEntity) { | ||
const coll = this.db.getCollection(tableName), | ||
entity = EntityGenerator.generateEntity(rawEntity, tableName), | ||
entityProxy = new EntityProxy(coll.insert(entity)); | ||
res = coll.findOne( { partitionKey: entity.partitionKey, rowKey: entity.rowKey }); | ||
if (res !== null) { | ||
res.partitionKey = entity.partitionKey; | ||
res.rowKey = entity.rowKey; | ||
res.attribs = entity.attribs; | ||
res.odata = entity.odata; | ||
coll.update(res); | ||
return new EntityProxy(res); | ||
} | ||
const entityProxy = new EntityProxy(coll.insert(entity)); | ||
return entityProxy; | ||
@@ -159,2 +197,3 @@ } | ||
module.exports = new TableStorageManager(); |
@@ -9,2 +9,3 @@ 'use strict'; | ||
queryTable = require('./../../actions/table/QueryTable'), | ||
queryEntities = require('./../../actions/table/QueryEntities'), | ||
createTable = require('./../../actions/table/CreateTable'); | ||
@@ -45,2 +46,6 @@ | ||
queryTable.process(request, res); | ||
} | ||
actions[Operations.QUERY_ENTITY] = (request, res) => { | ||
queryEntities.process(request, res); | ||
} |
@@ -72,2 +72,7 @@ 'use strict'; | ||
.run(TableExistsVal) | ||
} | ||
validations[Operations.QUERY_ENTITY] = (valContext) => { | ||
valContext | ||
.run(TableExistsVal) | ||
} |
@@ -93,14 +93,23 @@ 'use strict'; | ||
.replace(/\bor\b/g, '||') | ||
.replace(/\bnot\b/g, '!'); | ||
.replace(/\(/g, ' ( ') | ||
.replace(/\)/g, ' ) ') | ||
.replace(/\bnot\b/g, ' ! '); | ||
// if a token is neither a number, nor a boolean, nor a string enclosed with quotation marks it is an operand | ||
// If a token is neither a number, nor a boolean, nor a string enclosed with quotation marks it is an operand. | ||
// Operands are attributes of the object used within the where clause of LokiJS, thus we need to prepend each | ||
// attribute with an object identifier 'item'. | ||
// attribute with an object identifier 'item.attribs'. | ||
let transformedQuery = ''; | ||
for (const token of filter.split(' ')) { | ||
if (token === '') { | ||
continue; | ||
} | ||
if (!token.match(/\d+/) && | ||
token !== 'true' && token !== 'false' && | ||
!token.includes('`') && | ||
!['===', '>', '>=', '<', '<=', '!==', '&&', '||', '!'].includes(token)) { | ||
transformedQuery += `item.${token} `; | ||
!['===', '>', '>=', '<', '<=', '!==', '&&', '||', '!', '(', ')'].includes(token)) { | ||
if (token === 'PartitionKey' || token === 'RowKey') { | ||
transformedQuery += `item.${token} `; | ||
} else { | ||
transformedQuery += `item.attribs.${token} `; | ||
} | ||
} else { | ||
@@ -107,0 +116,0 @@ transformedQuery += `${token} `; |
@@ -34,10 +34,18 @@ 'use strict'; | ||
generateEntity(entity, tableName) { | ||
generateEntity(rawEntity, tableName) { | ||
// Enriching raw entity from payload with odata attributes | ||
const entity = {}; | ||
entity.partitionKey = rawEntity.PartitionKey; | ||
entity.rowKey = rawEntity.RowKey; | ||
entity.attribs = rawEntity; | ||
delete entity.attribs.PartitionKey; | ||
delete entity.attribs.RowKey; | ||
entity.odata = {}; | ||
entity.odata.metadata = `${_baseUrl}${tableName}$metadata#${tableName}/@Element`; | ||
entity.odata.type = `devstoreaccount1.${tableName}`; | ||
entity.odata.id = `${_baseUrl}${tableName}(PartitionKey='${entity.PartitionKey}',RowKey='${entity.RowKey}')`; | ||
entity.odata.editLink = `${tableName}(PartitionKey='${entity.PartitionKey}',RowKey='${entity.RowKey}')`; | ||
entity.odata.etag = etag(JSON.stringify(entity)); | ||
entity.odata.id = `${_baseUrl}${tableName}(PartitionKey='${rawEntity.PartitionKey}',RowKey='${rawEntity.RowKey}')`; | ||
entity.odata.editLink = `${tableName}(PartitionKey='${rawEntity.PartitionKey}',RowKey='${rawEntity.RowKey}')`; | ||
entity.odata.etag = etag(JSON.stringify(rawEntity)); | ||
return entity; | ||
@@ -44,0 +52,0 @@ } |
@@ -10,4 +10,4 @@ 'use strict'; | ||
super(entity); | ||
this.partitionKey = entity.PartitionKey; | ||
this.rowKey = entity.RowKey; | ||
this.partitionKey = entity.partitionKey; | ||
this.rowKey = entity.rowKey; | ||
} | ||
@@ -18,5 +18,5 @@ | ||
* | ||
* @param {any} odata is (nometadata|minimalmetadata|fullmetadata) | ||
* @param {any} mode is (nometadata|minimalmetadata|fullmetadata) | ||
* @returns | ||
* @memberof TableProxy | ||
* @memberof EntityProxy | ||
*/ | ||
@@ -26,8 +26,31 @@ odata(mode) { | ||
if (mode === ODataMode.FULL) { | ||
odata.etag = this._.odata.etag; | ||
odata['odata.etag'] = this._.odata.etag; | ||
} | ||
return odata; | ||
} | ||
/** | ||
* Returns all attributes (including partition key, row key) of the entity, and | ||
* depending on @param mode the odata type specifications. | ||
* | ||
* @param {any} mode is (nometadata|minimalmetadata|fullmetadata) | ||
* @returns | ||
* @memberof EntityProxy | ||
*/ | ||
attribs(mode) { | ||
if (mode === ODataMode.MINIMAL || mode === ODataMode.FULL) { | ||
return this._.attribs; // also return the OData type specifications | ||
} | ||
// In case of no metadata we filter out the OData type specifications | ||
const attribs = {}; | ||
for (const key of Object.keys(this._.attribs)) { | ||
if (key.includes('@odata.type')) { | ||
continue; | ||
} | ||
attribs[key] = this._.attribs[key]; | ||
} | ||
return attribs; | ||
} | ||
} | ||
module.exports = EntityProxy; |
@@ -15,5 +15,7 @@ 'use strict' | ||
case 'application/atom+xml': | ||
case 'application/atom+xml;': | ||
throw new IAError(`accept value of 'atom+xml' is currently not supported by Azurite`); | ||
break; | ||
case 'application/json': | ||
case 'application/json;': | ||
const txt = body.toString('utf8'); | ||
@@ -20,0 +22,0 @@ return JSON.parse(txt); |
@@ -13,5 +13,6 @@ 'use strict'; | ||
module.exports = (app) => { | ||
// app.route(`/${env.emulatedStorageAccountName}/:table`) | ||
app.route(new RegExp(`\/${env.emulatedStorageAccountName}\/([A-Za-z0-9]+)(.*)`)) | ||
.get((req, res, next) => { | ||
req.azuriteOperation = Operations.QUERY_ENTITY; | ||
req.azuriteRequest = new AzuriteTableRequest({ req: req }); | ||
next(); | ||
@@ -18,0 +19,0 @@ }) |
{ | ||
"name": "azurite", | ||
"version": "2.3.0", | ||
"version": "2.4.0", | ||
"description": "A lightweight server clone of Azure Blob, Queue, and Table Storage that simulates most of the commands supported by it with minimal dependencies.", | ||
@@ -12,4 +12,4 @@ "scripts": { | ||
"table": "node bin/table", | ||
"clean": "rimraf azurite-testdrive *.nupkg blob.exe queue.exe", | ||
"nuget": "cross-var \"npm run clean && pkg -t node6-win --output blob bin/blob && pkg -t node6-win --output queue bin/queue && nuget pack -Version $npm_package_version && nuget push *.nupkg -Source https://www.nuget.org/api/v2/package\"", | ||
"clean": "rimraf azurite-testdrive *.nupkg blob.exe queue.exe table.exe", | ||
"nuget": "cross-var \"npm run clean && pkg -t node6-win --output blob bin/blob && pkg -t node6-win --output queue bin/queue && pkg -t node6-win --output table bin/table && nuget pack -Version $npm_package_version && nuget push *.nupkg -Source https://www.nuget.org/api/v2/package\"", | ||
"docker": "cross-var \"docker build -t arafato/azurite:$npm_package_version . && docker build -t arafato/azurite:latest . && docker push arafato/azurite:$npm_package_version && docker push arafato/azurite:latest\"" | ||
@@ -16,0 +16,0 @@ }, |
# 2.0 | ||
## 2.3.0 | ||
- support for QueryEntities [#132](https://github.com/arafato/azurite/issues/132) | ||
## 2.3.0 | ||
- support for QueryTable [#133](https://github.com/arafato/azurite/issues/133) | ||
- thanks to @tlycken for various bugfixes (see PR @ https://github.com/arafato/azurite/pull/163) | ||
## 2.2.2 | ||
@@ -3,0 +8,0 @@ - fixes [#160](https://github.com/arafato/azurite/issues/160): adds working $root container semantics |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
389737
180
7711
2