From 5f65a5dd11964ec97d243021e30224a6d9b25b65 Mon Sep 17 00:00:00 2001 From: Burcu Dogan Date: Thu, 22 May 2014 17:27:36 +0200 Subject: [PATCH] Adding entity metadata registry. --- lib/datastore/entity.js | 93 ++++++++++++++++++++++++++++++++++++++++ test/datastore.entity.js | 32 ++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 lib/datastore/entity.js create mode 100644 test/datastore.entity.js diff --git a/lib/datastore/entity.js b/lib/datastore/entity.js new file mode 100644 index 00000000000..f65cad9034d --- /dev/null +++ b/lib/datastore/entity.js @@ -0,0 +1,93 @@ +var entityMeta = {}; + +var namespaceRegex = kindRegex = fieldNameRegex = new RegExp(/^[A-Za-z]+$/); + +/** + * Registers a kind and its field meta globally. In order + * to perform CRUD operations for a kind, you should register + * it with its field metadata. + * + * registerKind('namespace', 'Author', { + * name: { kind: STRING, indexed: false }, + * tags: { kind: STRING, multi: true }, // an array of string elements. + * favArticles: { kind: KEY, multi: true } + * contact: { + * kind: { + * telephone: { kind: String }, + * email: { kind: String } + * } + * } + * }); + * + * @param {string} namespace Namespace of the kind. + * @param {string} kind Name of the kind. + * @param {object} fieldMeta Object that contains metadata information + * about the fields. + */ +function registerKind(namespace, kind, fieldMeta) { + // validate the input and put it to the dictionary + namespace = namespace || 'default'; + if (!namespaceRegex.test(namespace)) { + throw new Error('Namespaces should match ' + namespaceRegex); + } + if (!kindRegex.test(kind)) { + throw new Error('Kinds should match ' + kindRegex); + } + + // TODO(jbd): Validate namespace, kind and fieldMeta. + Object.keys(fieldMeta).forEach(function (fieldName) { + validateField(fieldName, fieldMeta[fieldName]); + }); + + if (!entityMeta[namespace]) { + entityMeta[namespace] = {}; + } + entityMeta[namespace][kind] = fieldMeta; +} + +function getKind(namespace, kind) { + return entityMeta[namespace] && entityMeta[namespace][kind]; +}; + +function validateField(name, field) { + if (!fieldNameRegex.test(name)) { + throw new Error('Field name should match ' + fieldNameRegex); + } + if (!field.kind) { + throw new Error('Provide a kind for field ' + name); + } + if (typeof field.kind != 'object' && !primitiveKinds[field.kind]) { + throw new Error('Unknown kind for field ' + name); + } + if (typeof field.kind == 'object') { + Object.keys(field.key).forEach(function (key) { + validateField(key, field.key[key]); + }); + } +} + +var primitiveKinds = { + BOOL: 'BOOL', + NUMBER: 'NUMBER', + STRING: 'STRING', + BYTES: 'BYTES', + DATETIME: 'DATETIME', + KEY: 'KEY' +} + +/** + * Export primitive kinds. + */ +module.exports.kinds = primitiveKinds; + +/** + * Export registerKind. + * @type {function} + */ +module.exports.registerKind = registerKind; + +/** + * Exports getKind. + * @type {function} + */ +module.exports.getKind = getKind; diff --git a/test/datastore.entity.js b/test/datastore.entity.js new file mode 100644 index 00000000000..fcca8acdf3e --- /dev/null +++ b/test/datastore.entity.js @@ -0,0 +1,32 @@ +var assert = require('assert'), + entity = require('../lib/datastore/entity.js'); + +var blogPostMetadata = { + title: { kind: entity.kinds.STRING, indexed: true }, + tags: { kind: entity.kinds.STRING, multi: true, indexed: true }, + publishedAt: { kind: entity.kinds.DATETIME }, + author: { kind: entity.kinds.KEY, indexed: true }, + isDraft: { kind: entity.kinds.BOOL, indexed: true } +} + +describe('registerKind', function() { + + it('should be able to register valid field metadata', function(done) { + entity.registerKind('namespace', 'kind', blogPostMetadata); + done(); + }); + + it('should set the namespace to be "default" if zero value or null is provided', function(done) { + entity.registerKind(null, 'kind', blogPostMetadata); + var meta = entity.getKind('default', 'kind'); + assert.strictEqual(meta, blogPostMetadata); + done(); + }); + + it('should throw an exception if an invalid kind is provided', function(done) { + assert.throws(function() { + entity.registerKind(null, '000', blogPostMetadata); + }, /Kinds should match/); + done(); + }); +});