Comparing version 2.0.0 to 2.1.0
@@ -108,3 +108,5 @@ 'use strict'; | ||
Table: { | ||
CREATE_TABLE: 'CreateTable' | ||
CREATE_TABLE: 'CreateTable', | ||
INSERT_ENTITY: 'InsertEntity', | ||
DELETE_TABLE: 'DeleteTable' | ||
} | ||
@@ -111,0 +113,0 @@ } |
@@ -73,3 +73,7 @@ 'use strict'; | ||
AtomXmlNotSupported: new ErrorCode('Atom+XmlNotSupported', 501, 'Atom feed is currently not supported by Azurite.'), | ||
TableAlreadyExists: new ErrorCode('TableAlreadyExists', 409, 'The table specified already exists.') | ||
TableAlreadyExists: new ErrorCode('TableAlreadyExists', 409, 'The table specified already exists.'), | ||
TableNotFound: new ErrorCode('TableNotFound', 404, 'The table specified does not exist.'), | ||
EntityAlreadyExists: new ErrorCode('EntityAlreadyExists', 409, 'The specified entity already exists.'), | ||
// See https://docs.microsoft.com/en-us/rest/api/storageservices/understanding-the-table-service-data-model | ||
ReservedTableName: new ErrorCode('BadRequest', 404, 'The table name is reserved.'), | ||
} |
@@ -9,2 +9,3 @@ 'use strict'; | ||
TableProxy = require('./../../model/table/TableProxy'), | ||
EntityProxy = require('./../../model/table/EntityProxy'), | ||
EntityGenerator = require('./../../model/table/EntityGenerator'), | ||
@@ -43,2 +44,3 @@ Tables = require('./../Constants').TableStorageTables, | ||
createTable(request) { | ||
this.db.addCollection(request.tableName); | ||
const coll = this.db.getCollection(Tables.Tables); | ||
@@ -50,2 +52,14 @@ const tableEntity = EntityGenerator.generateTable(request.tableName); | ||
insertEntity(request) { | ||
const proxy = this._createEntity(request.tableName, request.payload); | ||
return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy })); | ||
} | ||
deleteTable(request) { | ||
const coll = this.db.getCollection(Tables.Tables); | ||
coll.chain().find({ name: { '$eq': request.tableName } }).remove(); | ||
this.db.removeCollection(request.tableName); | ||
return BbPromise.resolve(new AzuriteTableResponse({})); | ||
} | ||
_getTable(name) { | ||
@@ -59,4 +73,31 @@ const coll = this.db.getCollection(Tables.Tables); | ||
_getEntity(tableName, partitionKey, rowKey) { | ||
const coll = this.db.getCollection(tableName); | ||
if (coll === null) { | ||
return undefined; | ||
} | ||
const result = coll.chain() | ||
.find({ | ||
'$and': | ||
[ | ||
{ | ||
PartitionKey: { '$eq': partitionKey } | ||
}, | ||
{ | ||
RowKey: { '$eq': rowKey } | ||
} | ||
] | ||
}) | ||
.data(); | ||
return (result.length === 0) ? undefined : new EntityProxy(result[0]); | ||
} | ||
_createEntity(tableName, rawEntity) { | ||
const coll = this.db.getCollection(tableName), | ||
entity = EntityGenerator.generateEntity(rawEntity, tableName), | ||
entityProxy = new EntityProxy(coll.insert(entity)); | ||
return entityProxy; | ||
} | ||
} | ||
module.exports = new TableStorageManager(); |
@@ -5,2 +5,4 @@ 'use strict'; | ||
Operations = require('./../../core/Constants').Operations.Table, | ||
insertEntity = require('./../../actions/table/InsertEntity'), | ||
deleteTable = require('./../../actions/table/DeleteTable'), | ||
createTable = require('./../../actions/table/CreateTable'); | ||
@@ -25,2 +27,10 @@ | ||
createTable.process(request, res); | ||
} | ||
actions[Operations.INSERT_ENTITY] = (request, res) => { | ||
insertEntity.process(request, res); | ||
} | ||
actions[Operations.DELETE_TABLE] = (request, res) => { | ||
deleteTable.process(request, res); | ||
} |
@@ -10,2 +10,5 @@ 'use strict'; | ||
ValidationContext = require('./../../validation/table/ValidationContext'), | ||
TableExistsVal = require('./../../validation/table/TableExists'), | ||
ConflictingEntityVal = require('./../../validation/table/ConflictingEntity'), | ||
TableNameVal = require('./../../validation/table/TableName'), | ||
ConflictingTableVal = require('./../../validation/table/ConflictingTable'); | ||
@@ -21,5 +24,7 @@ | ||
tableProxy = tsm._getTable(request.tableName), | ||
entityProxy = tsm._getEntity(request.tableName, request.partitionKey, request.rowKey), | ||
validationContext = new ValidationContext({ | ||
request: request, | ||
table: tableProxy | ||
table: tableProxy, | ||
entity: entityProxy | ||
}); | ||
@@ -42,3 +47,15 @@ validations[req.azuriteOperation](validationContext); | ||
valContext | ||
.run(ConflictingTableVal); | ||
.run(ConflictingTableVal) | ||
.run(TableNameVal); | ||
} | ||
validations[Operations.INSERT_ENTITY] = (valContext) => { | ||
valContext | ||
.run(TableExistsVal) | ||
.run(ConflictingEntityVal); | ||
} | ||
validations[Operations.DELETE_TABLE] = (valContext) => { | ||
valContext | ||
.run(TableExistsVal); | ||
} |
@@ -21,5 +21,13 @@ 'use strict'; | ||
this.tableName = this.payload.TableName; | ||
this.partitionKey = this.payload.PartitionKey; | ||
this.rowKey = this.payload.RowKey; | ||
this.tableName = this.payload.TableName || req.params[0].replace(/[\('\)]/g, ''); | ||
const res = this._parseEntityKeys(req.params[1] || ''), | ||
partitionKey = res.partitionKey, | ||
rowKey = res.rowKey; | ||
this.partitionKey = this.payload.PartitionKey || partitionKey; | ||
this.rowKey = this.payload.RowKey || rowKey; | ||
if (Object.keys(this.payload).length === 0 && this.payload.constructor === Object) { | ||
this.payload === undefined; | ||
} | ||
} | ||
@@ -29,3 +37,3 @@ | ||
this.httpProps[N.CONTENT_TYPE] = httpHeaders[N.CONTENT_TYPE] || `application/json;`; | ||
this.httpProps[N.ACCEPT] = httpHeaders[N.ACCEPT] || `application/json;odata=nometadata`; | ||
this.httpProps[N.ACCEPT] = httpHeaders[N.ACCEPT] || `application/json;odata=nometadata`; | ||
this.httpProps[N.PREFER] = httpHeaders[N.PREFER] || `return-content`; | ||
@@ -36,8 +44,28 @@ } | ||
if (value === undefined) return undefined; | ||
if (value.includes(`odata=nometadata`)) return Constants.ODataMode.NONE; | ||
if (value.includes(`odata=nometadata`)) return Constants.ODataMode.NONE; | ||
if (value.includes(`odata=minimalmetadata`)) return Constants.ODataMode.MINIMAL; | ||
if (value.includes(`odata=fullmetadata`)) return Constants.ODataMode.FULL; | ||
} | ||
_parseEntityKeys(str) { | ||
const empty = { | ||
partitionKey: undefined, | ||
rowKey: undefined | ||
}; | ||
if (str === '') { | ||
return empty; | ||
} | ||
str = str.replace(/[\(\)]/g); | ||
const regex = new RegExp(/\(PartitionKey='(.*)',\s*RowKey='(.*)'\)/); | ||
const res = regex.exec(str); | ||
if (res === null) { | ||
return empty; | ||
} | ||
return { | ||
partitionKey: res[1], | ||
rowKey: res[2] | ||
}; | ||
} | ||
} | ||
module.exports = AzuriteTableRequest; |
'use strict'; | ||
const etag = require('./../../core/utils').computeEtag; | ||
const _baseUrl = `http://127.0.0.1:10002/devstoreaccount1/`; | ||
/** | ||
@@ -22,12 +26,22 @@ * Generates a Table Storage 'Table' entity and a Table Storage 'Entity' entity. | ||
entity.name = name; | ||
const baseUrl = `http://127.0.0.1:10002/devstoreaccount1/`; | ||
entity.odata = {}; | ||
entity.odata.metadata = `${baseUrl}$metadata#Tables/@Element`; | ||
entity.odata.metadata = `${_baseUrl}$metadata#Tables/@Element`; | ||
entity.odata.type = `devstoreaccount1.Tables`; | ||
entity.odata.id = `${baseUrl}Tables('${name}')`; | ||
entity.odata.id = `${_baseUrl}Tables('${name}')`; | ||
entity.odata.editLink = `Tables('${name}')`; | ||
return entity; | ||
} | ||
generateEntity(entity, tableName) { | ||
// Enriching raw entity from payload with odata attributes | ||
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)); | ||
return entity; | ||
} | ||
} | ||
module.exports = new EntityGenerator(); |
@@ -10,2 +10,5 @@ 'use strict' | ||
parse(contentType, body) { | ||
if (body.length === 0) { | ||
return {}; | ||
} | ||
switch (contentType) { | ||
@@ -12,0 +15,0 @@ case 'application/atom+xml;': |
'use strict'; | ||
const ODataMode = require('./../../core/Constants').ODataMode, | ||
InternalAzuriteError = require('./../../core/InternalAzuriteError'); | ||
const BaseProxy = require('./BaseProxy'); | ||
class TableProxy { | ||
class TableProxy extends BaseProxy { | ||
constructor(entity) { | ||
this._ = entity; | ||
super(entity); | ||
this.name = entity.name; | ||
} | ||
/** | ||
* Returns the odata representation of the table entity. | ||
* | ||
* @param {any} odata is (nometadata|minimalmetadata|fullmetadata) | ||
* @returns | ||
* @memberof TableProxy | ||
*/ | ||
odata(mode) { | ||
switch (mode) { | ||
case ODataMode.NONE: | ||
return { | ||
TableName: this._.name | ||
} | ||
break; | ||
case ODataMode.MINIMAL: | ||
return { | ||
"odata.metadata": this._.odata.metadata, | ||
TableName: this._.name | ||
} | ||
break; | ||
case ODataMode.FULL: | ||
return { | ||
"odata.metadata": this._.odata.metadata, | ||
"odata.type": this._.odata.type, | ||
"odata.id": this._.odata.id, | ||
"odata.editLink": this._.odata.editLink, | ||
TableName: this._.name | ||
} | ||
break; | ||
default: | ||
throw new InternalAzuriteError(`TableProxy: Unsupported OData type "${mode}".`); | ||
} | ||
} | ||
} | ||
module.exports = TableProxy; |
'use strict'; | ||
const env = require('./../../core/env'); | ||
const env = require('./../../core/env'), | ||
AzuriteTableRequest = require('./../../model/table/AzuriteTableRequest'), | ||
Operations = require('./../../core/Constants').Operations.Table; | ||
@@ -11,3 +13,4 @@ /* | ||
module.exports = (app) => { | ||
app.route(`/${env.emulatedStorageAccountName}/:table`) | ||
// app.route(`/${env.emulatedStorageAccountName}/:table`) | ||
app.route(new RegExp(`\/${env.emulatedStorageAccountName}\/([A-Za-z0-9]+)(.*)`)) | ||
.get((req, res, next) => { | ||
@@ -23,2 +26,6 @@ next(); | ||
.post((req, res, next) => { | ||
if (req.azuriteOperation === undefined) { | ||
req.azuriteOperation = Operations.INSERT_ENTITY; | ||
req.azuriteRequest = new AzuriteTableRequest({ req: req, payload: req.payload }); | ||
} | ||
next(); | ||
@@ -25,0 +32,0 @@ }) |
@@ -13,3 +13,3 @@ 'use strict'; | ||
module.exports = (app) => { | ||
app.route(`/${env.emulatedStorageAccountName}/Tables`) | ||
app.route(new RegExp(`\/${env.emulatedStorageAccountName}\/Tables(.*)`)) | ||
.get((req, res, next) => { | ||
@@ -30,4 +30,6 @@ next(); | ||
.delete((req, res, next) => { | ||
req.azuriteOperation = Operations.DELETE_TABLE; | ||
req.azuriteRequest = new AzuriteTableRequest({ req: req }); | ||
next(); | ||
}); | ||
} |
@@ -6,3 +6,2 @@ 'use strict'; | ||
class ConflictingTable { | ||
@@ -9,0 +8,0 @@ constructor() { |
{ | ||
"name": "azurite", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "A lightweight server clone of Azure Blob Storage that simulates most of the commands supported by it with minimal dependencies.", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -162,4 +162,2 @@ # Azurite | ||
- Delete Table [TODO] | ||
- Insert Entity [TODO] | ||
- Query Entity [TODO] | ||
@@ -166,0 +164,0 @@ - Query Tables [TODO] |
# 2.0 | ||
## 2.1.0 | ||
- support for InsertEntity [#131](https://github.com/arafato/azurite/issues/131) | ||
- support for DeleteTable [#130](https://github.com/arafato/azurite/issues/130) | ||
## 2.0.0 | ||
@@ -3,0 +6,0 @@ - Initial support for Table Storage: |
373109
174
7333
170