Skip to content

Commit

Permalink
Tests for service document ready
Browse files Browse the repository at this point in the history
  • Loading branch information
r1mar committed Oct 13, 2022
1 parent 4cec57f commit cf29fa0
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 0 deletions.
228 changes: 228 additions & 0 deletions src/metadata/ODataServiceDocument.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import { Router } from 'express';
import pipes from '../pipes';
import Resource from '../ODataResource';

export default class Metadata {
constructor(server) {
this._server = server;
this._hooks = {
};
this._count = 0;
}

get() {
return this;
}

before(fn) {
this._hooks.before = fn;
return this;
}

after(fn) {
this._hooks.after = fn;
return this;
}

auth(fn) {
this._hooks.auth = fn;
return this;
}

_router() {
/*eslint-disable */
const router = Router();
/* eslint-enable */
router.get('/\\$metadata', (req, res) => {
pipes.authorizePipe(req, res, this._hooks.auth)
.then(() => pipes.beforePipe(req, res, this._hooks.before))
.then(() => this.ctrl(req))
.then((result) => pipes.respondPipe(req, res, result || {}))
.then((data) => pipes.afterPipe(req, res, this._hooks.after, data))
.catch((err) => pipes.errorPipe(req, res, err));
});

return router;
}

visitProperty(node, root) {
const result = {};

switch (node.instance) {
case 'ObjectId':
result.$Type = 'self.ObjectId';
break;

case 'Number':
result.$Type = 'Edm.Double';
break;

case 'Date':
result.$Type = 'Edm.DateTimeOffset';
break;

case 'String':
result.$Type = 'Edm.String';
break;

case 'Array': // node.path = p1; node.schema.paths
result.$Collection = true;
if (node.schema && node.schema.paths) {
this._count += 1;
const notClassifiedName = `${node.path}Child${this._count}`;
// Array of complex type
result.$Type = `self.${notClassifiedName}`;
root(notClassifiedName, this.visitor('ComplexType', node.schema.paths, root));
} else {
const arrayItemType = this.visitor('Property', { instance: node.options.type[0].name }, root);

result.$Type = arrayItemType.$Type;
}
break;

default:
return null;
}

return result;
}

visitEntityType(node, root) {
const properties = Object.keys(node)
.filter((path) => path !== '_id')
.reduce((previousProperty, curentProperty) => {
const result = {
...previousProperty,
[curentProperty]: this.visitor('Property', node[curentProperty], root),
};

return result;
}, {});

return {
$Kind: 'EntityType',
$Key: ['id'],
id: {
$Type: 'self.ObjectId',
$Nullable: false,
},
...properties,
};
}

visitComplexType(node, root) {
const properties = Object.keys(node)
.filter((item) => item !== '_id')
.reduce((previousProperty, curentProperty) => {
const result = {
...previousProperty,
[curentProperty]: this.visitor('Property', node[curentProperty], root),
};

return result;
}, {});

return {
$Kind: 'ComplexType',
...properties,
};
}

static visitAction(node) {
return {
$Kind: 'Action',
$IsBound: true,
$Parameter: [{
$Name: node.resource,
$Type: `self.${node.resource}`,
$Collection: node.binding === 'collection' ? true : undefined,
}],
};
}

static visitFunction(node) {
return {
$Kind: 'Function',
...node.params,
};
}

visitor(type, node, root) {
switch (type) {
case 'Property':
return this.visitProperty(node, root);

case 'ComplexType':
return this.visitComplexType(node, root);

case 'Action':
return Metadata.visitAction(node);

case 'Function':
return Metadata.visitFunction(node, root);

default:
return this.visitEntityType(node, root);
}
}

ctrl() {
const entityTypeNames = Object.keys(this._server.resources);
const entityTypes = entityTypeNames.reduce((previousResource, currentResource) => {
const resource = this._server.resources[currentResource];
const result = { ...previousResource };
const attachToRoot = (name, value) => { result[name] = value; };

if (resource instanceof Resource) {
const { paths } = resource.model.model.schema;

result[currentResource] = this.visitor('EntityType', paths, attachToRoot);
const actions = Object.keys(resource.actions);
if (actions && actions.length) {
actions.forEach((action) => {
result[action] = this.visitor('Action', resource.actions[action], attachToRoot);
});
}
} else {
result[currentResource] = this.visitor('Function', resource, attachToRoot);
}

return result;
}, {});

const entitySetNames = Object.keys(this._server.resources);
const entitySets = entitySetNames.reduce((previousResource, currentResource) => {
const result = { ...previousResource };
result[currentResource] = this._server.resources[currentResource] instanceof Resource ? {
$Collection: true,
$Type: `self.${currentResource}`,
} : {
$Function: `self.${currentResource}`,
};

return result;
}, {});

const document = {
$Version: '4.0',
ObjectId: {
$Kind: 'TypeDefinition',
$UnderlyingType: 'Edm.String',
$MaxLength: 24,
},
...entityTypes,
$EntityContainer: 'org.example.DemoService',
['org.example.DemoService']: { // eslint-disable-line no-useless-computed-key
$Kind: 'EntityContainer',
...entitySets,
},
};

return new Promise((resolve) => {
resolve({
status: 200,
metadata: document,
});
});
}
}
48 changes: 48 additions & 0 deletions test/service.document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'should';
import request from 'supertest';
import { host, port, bookSchema, odata, assertSuccess } from './support/setup';
import FakeDb from './support/fake-db';

describe('metadata.format', () => {
let httpServer, server, db;

const jsonDocument = {
'@context': 'http://localhost:8080/',
value: [{
kind: 'EntitySet',
name: 'books',
url: 'books'
}]
};
beforeEach(async function() {
db = new FakeDb();
server = odata(db);
server.resource('book', bookSchema);

});

afterEach(() => {
httpServer.close();
});

it('should return json if no format given', async function() {
httpServer = server.listen(port);
const res = await request(host).get('/');
assertSuccess(res);
checkContentType(res, 'application/json');
res.body.should.deepEqual(jsonDocument);
});

it('should return 406 if other than json format requested', async function() {
httpServer = server.listen(port);
const res = await request(host).get('/').set('accept', 'application/xml');
res.status.should.be.equal(406);
});

});


function checkContentType(res, value) {
res.header.should.have.property('content-type');
res.header['content-type'].should.containEql(value);
}

0 comments on commit cf29fa0

Please sign in to comment.