Skip to content

Commit

Permalink
Adding entity metadata registry.
Browse files Browse the repository at this point in the history
  • Loading branch information
rakyll committed May 22, 2014
1 parent 4f4b996 commit 5f65a5d
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
93 changes: 93 additions & 0 deletions lib/datastore/entity.js
Original file line number Diff line number Diff line change
@@ -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;
32 changes: 32 additions & 0 deletions test/datastore.entity.js
Original file line number Diff line number Diff line change
@@ -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();
});
});

0 comments on commit 5f65a5d

Please sign in to comment.