Skip to content
This repository has been archived by the owner on Mar 22, 2018. It is now read-only.

Commit

Permalink
implements #141 #129
Browse files Browse the repository at this point in the history
  • Loading branch information
arafato committed Feb 22, 2018
1 parent d2d1a8d commit a265337
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 17 deletions.
3 changes: 2 additions & 1 deletion lib/core/ErrorCodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ module.exports = {
PopReceiptMismatch: new ErrorCode('PopReceiptMismatch', 400, 'The specified pop receipt did not match the pop receipt for a dequeued message.'),

// TABLE
AtomXmlNotSupported: new ErrorCode('Atom+XmlNotSupported', 501, 'Atom feed is currently not supported ny Azurite.')
AtomXmlNotSupported: new ErrorCode('Atom+XmlNotSupported', 501, 'Atom feed is currently not supported by Azurite.'),
TableAlreadyExists: new ErrorCode('TableAlreadyExists', 409, 'The table specified already exists.')
}
13 changes: 12 additions & 1 deletion lib/core/table/TableStorageManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Loki = require('lokijs'),
fsn = BbPromise.promisifyAll(require("fs")),
AzuriteTableResponse = require('./../../model/table/AzuriteTableResponse'),
TableProxy = require('./../../model/table/TableProxy'),
EntityGenerator = require('./../../model/table/EntityGenerator'),
Tables = require('./../Constants').TableStorageTables,
env = require('./../../core/env');

Expand Down Expand Up @@ -39,9 +40,19 @@ class TableStorageManager {

createTable(request) {
const coll = this.db.getCollection(Tables.Tables);
const proxy = new TableProxy(coll.insert(request.tableName));
const tableEntity = EntityGenerator.generateTable(request.tableName);
const proxy = new TableProxy(coll.insert(tableEntity));
return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy }));
}

_getTable(name) {
const coll = this.db.getCollection(Tables.Tables);
const result = coll.chain()
.find({ name: name })
.data();
return (result.length === 0) ? undefined : new TableProxy(result[0]);
}

}

module.exports = new TableStorageManager();
16 changes: 12 additions & 4 deletions lib/middleware/table/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ const BbPromise = require('bluebird'),
N = require('./../../core/HttpHeaderNames'),
AError = require('./../../core/AzuriteError'),
ErrorCodes = require('./../../core/ErrorCodes'),
Operations = require('./../../core/Constants').Operations.Table;

Operations = require('./../../core/Constants').Operations.Table,
tsm = require('./../../core/table/TableStorageManager'),
ValidationContext = require('./../../validation/table/ValidationContext'),
ConflictingTableVal = require('./../../validation/table/ConflictingTable');

module.exports = (req, res, next) => {
BbPromise.try(() => {
// Azurite currently does not support XML-Atom responses, only supports JSON-based responses.
if (req.headers[N.CONTENT_TYPE] === `application/atom+xml`) {
throw new AError(ErrorCodes.AtomXmlNotSupported);
}
const validationContext = new ValidationContext({});
const request = req.azuriteRequest,
tableProxy = tsm._getTable(request.tableName),
validationContext = new ValidationContext({
request: request,
table: tableProxy
});
validations[req.azuriteOperation](validationContext);
next();
}).catch((e) => {
Expand All @@ -29,5 +36,6 @@ validations[undefined] = () => {
}

validations[Operations.CREATE_TABLE] = (valContext) => {
// TODO: Implement validation
valContext
.run(ConflictingTableVal);
}
1 change: 1 addition & 0 deletions lib/model/table/AzuriteTableRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class AzuriteTableRequest {
}

_initHttpProps(httpHeaders) {
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.PREFER] = httpHeaders[N.PREFER] || `return-content`;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use strict';

/**
* Generates a Table Storage 'Table' Entity
* Generates a Table Storage 'Table' entity and a Table Storage 'Entity' entity.
*
* @class TableGenerator
*/
class TableGenerator {
class EntityGenerator {
constructor() {
}

Expand All @@ -16,9 +16,9 @@ class TableGenerator {
* @returns
* @memberof TableGenerator
*/
generateStorageEntity(name) {
generateTable(name) {
const entity = {};
entity.TableName = name;
entity.name = name;
const baseUrl = `http://127.0.0.1:10002/devstoreaccount1/`;
entity.odata = {};
entity.odata.metadata = `${baseUrl}$metadata#Tables/@Element`;
Expand All @@ -29,4 +29,4 @@ class TableGenerator {
}
}

module.exports = new TableGenerator();
module.exports = new EntityGenerator();
4 changes: 2 additions & 2 deletions lib/model/table/RequestPayloadParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ class RequestPayLoadParser {

parse(contentType, body) {
switch (contentType) {
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');
return JSON.parse(txt);
break;
Expand Down
8 changes: 4 additions & 4 deletions lib/model/table/TableProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const ODataMode = require('./../../core/Constants').ODataMode,
class TableProxy {
constructor(entity) {
this._ = entity;
this.name = entity.TableName;
this.name = entity.name;
}

/**
Expand All @@ -20,13 +20,13 @@ class TableProxy {
switch (mode) {
case ODataMode.NONE:
return {
TableName: this._.TableName
TableName: this._.name
}
break;
case ODataMode.MINIMAL:
return {
"odata.metadata": this._.odata.metadata,
TableName: this._.TableName
TableName: this._.name
}
break;
case ODataMode.FULL:
Expand All @@ -35,7 +35,7 @@ class TableProxy {
"odata.type": this._.odata.type,
"odata.id": this._.odata.id,
"odata.editLink": this._.odata.editLink,
TableName: this._.TableName
TableName: this._.name
}
break;
default:
Expand Down
18 changes: 18 additions & 0 deletions lib/validation/table/ConflictingTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

const AError = require('./../../core/AzuriteError'),
ErrorCodes = require('./../../core/ErrorCodes');


class ConflictingTable {
constructor() {
}

validate({ table = undefined }) {
if (table !== undefined) {
throw new AError(ErrorCodes.TableAlreadyExists);
}
}
}

module.exports = new ConflictingTable;
43 changes: 43 additions & 0 deletions lib/validation/table/ValidationContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

/**
* The in-memory DB of Azurite serves as the exclusive source of truth for every validation.
* Since the validation is synchronous / single-threaded we can be certain about the exact state of the entire
* application before and after @see ValidationContext exits.
*
* In case a validation fails an according @see AzuriteException is thrown which is then processed
* by the validation middleware module middleware/table/validation.js
*
* @class ValidationContext
*/
class ValidationContext {
constructor({ request = undefined, table = undefined, entity = undefined }) {
this.request = request;
this.table = table;
this.entity = entity;
}

/**
* Runs a validation module.
*
* @param {Object} valModule
* @param {Object} moduleOptions - allows a validation module to selectively add attributes or overwrite them
* @param {boolean} skip - if set to true validation module is not run.
* @returns this
*
* @memberOf ValidationContext
*/
run(valModule, moduleOptions, skip) {
if (skip) {
return this;
}
valModule.validate({
request: moduleOptions ? moduleOptions.request || this.request : this.request,
table: moduleOptions ? moduleOptions.table || this.table : this.table,
entity: moduleOptions ? moduleOptions.entity || this.entity : this.entity,
moduleOptions: moduleOptions });
return this;
}
}

module.exports = ValidationContext;

0 comments on commit a265337

Please sign in to comment.