Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add odata headers #40

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# IDE
.idea
.idea/
.DS_Store
.vscode
# Logs
logs
*.log
Expand Down
26 changes: 20 additions & 6 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,28 @@
{
"type": "node",
"request": "launch",
"name": "test",
"skipFiles": [
"<node_internals>/**"
],
"name": "npm test",
"program": "${workspaceFolder}/node_modules/.bin/jasmine",
"cwd": "${workspaceFolder}",
"outFiles": [
"${workspaceFolder}/**/*.js"
"console": "integratedTerminal",
"env": {
"NODE_ENV": "development",
"DEBUG": "themost-framework:*"
}
},
{
"name": "npm run standalone",
"program": "${workspaceFolder}/bin/standalone.js",
"request": "launch",
"type": "node",
"console": "integratedTerminal",
"env": {
"NODE_ENV": "development",
"DEBUG": "themost-framework:*"
},
"runtimeArgs": [
"--require",
"@babel/register"
]
}
]
Expand Down
70 changes: 70 additions & 0 deletions bin/standalone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* MOST Web Framework 2.0 Codename Blueshift
* Copyright (c) 2017, THEMOST LP All rights reserved
*
* Use of this source code is governed by an BSD-3-Clause license that can be
* found in the LICENSE file at https://themost.io/license
*/
import http from 'http';
import debug from 'debug';
import express from 'express';
import path from 'path';
import {ExpressDataApplication, serviceRouter} from '../modules/@themost/express/src';
import { ODataModelBuilder } from '@themost/data';

// normalize a port into a number, string, or false.
function normalizePort(val) {
let port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
// get port from environment
let port = normalizePort(process.env.PORT) || 3000;
// get bind address.
let host = process.env.HOST || 'localhost';

const app = express();

// create a new instance of data application
const dataApplication = new ExpressDataApplication(path.resolve(__dirname, '../modules/@themost/express/src/test/config'));

dataApplication.getService(ODataModelBuilder).defaultNamespace = 'Test1';
dataApplication.getService(ODataModelBuilder).hasContextLink(() => {
return `http://${host}:${port}/api/$metadata`;
});
// hold data application
app.set('ExpressDataApplication', dataApplication);
// use data middleware (register req.context)
app.use(dataApplication.middleware(app));
// set testRouter
// noinspection JSCheckFunctionSignatures
app.use('/api/', serviceRouter);


// enable namespace
debug.enable('themost-framework:*');
const log = debug('themost-framework:test');


// noinspection JSUnresolvedFunction
let server = http.createServer(app);
// listen on provided port, on all network interfaces.
server.on('error', err => {
log(err);
});
server.on('close', () => {
log('Stopping the test api server from accepting new connections.');
});
server.on('listening', () => {
let addr = server.address();
// eslint-disable-next-line no-console
log(`Test api server starts listening on http://${addr.address}:${addr.port}`);
});
server.listen(port, host);
Binary file added local.db
Binary file not shown.
13 changes: 9 additions & 4 deletions modules/@themost/express/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions modules/@themost/express/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@themost/express",
"version": "1.5.15",
"version": "1.6.0",
"description": "MOST Data ORM Express Middleware",
"main": "dist/themost_express.cjs.js",
"module": "dist/themost_express.esm.js",
Expand Down Expand Up @@ -35,8 +35,9 @@
},
"dependencies": {
"express": "^4.16.3",
"lodash": "^4.17.11",
"lodash": "^4.17.21",
"multer": "^1.4.2",
"on-headers": "^1.0.2",
"pluralize": "^7.0.0",
"q": "^1.5.1",
"rxjs": "^6.5.4",
Expand Down
2 changes: 2 additions & 0 deletions modules/@themost/express/src/middleware.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ export declare function postEntitySetAction(options?: EntitySetOptions): Request
export declare function getEntityFunction(options?: EntityOptions): RequestHandler;

export declare function postEntityAction(options?: EntityOptions): RequestHandler;

export declare function setHeaders(): RequestHandler;
27 changes: 26 additions & 1 deletion modules/@themost/express/src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {LangUtils, HttpNotFoundError, HttpBadRequestError, HttpMethodNotAllowedE
import { ResponseFormatter, StreamFormatter } from "./formatter";
import {multerInstance} from "./multer";
import fs from 'fs';
import onHeaders from 'on-headers';

const parseBoolean = LangUtils.parseBoolean;
const DefaultTopOption = 25;
Expand Down Expand Up @@ -1621,8 +1622,18 @@ function getEntitySetIndex() {
const builder = req.context.getApplication().getStrategy(ODataModelBuilder);
// get edm document
return builder.getEdm().then(result => {
let contextLink
if (typeof builder.getContextLink === 'function') {
contextLink = builder.getContextLink(req.context);
if (contextLink) {
return res.json({
"@odata.context": contextLink,
"value": result.entityContainer.entitySet
});
}
}
return res.json({
value: result.entityContainer.entitySet
"value": result.entityContainer.entitySet
});
}).catch(err => {
return next(err);
Expand Down Expand Up @@ -1653,6 +1664,20 @@ function getMetadataDocument() {
};
}

function setHeaders() {
return function(_req, res, next) {
onHeaders(res,
/**
* @this {ServerResponse}
*/
function onSetHeaders() {
this.set('OData-Version', '4.0');
});
return next();
}
}

export {setHeaders};
export {getEntitySetIndex};
export {getMetadataDocument};
export {bindEntitySet};
Expand Down
3 changes: 2 additions & 1 deletion modules/@themost/express/src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {bindEntitySet} from "./middleware";
import {getEntitySet} from "./middleware";
import {postEntitySet} from "./middleware";
import {deleteEntitySet} from "./middleware";
import { setHeaders } from './middleware';
import {getEntity} from "./middleware";
import {postEntity} from "./middleware";
import {deleteEntity} from "./middleware";
Expand Down Expand Up @@ -45,7 +46,7 @@ function readStream(stream) {
});
});
}

serviceRouter.use(setHeaders());
/* GET / */
serviceRouter.get('/', getEntitySetIndex());

Expand Down
41 changes: 41 additions & 0 deletions modules/@themost/express/src/service.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import passport from 'passport';
import {serviceRouter} from './service';
import {dateReviver} from './helpers';
import { ApplicationService } from '@themost/common';
import { ODataModelBuilder } from '@themost/data';

class ServiceRouterExtension extends ApplicationService {
constructor(app) {
Expand Down Expand Up @@ -88,6 +89,45 @@ describe('serviceRouter', () => {
//
});

it('should GET /api/', async () => {
// change user
spyOn(passportStrategy, 'getUser').and.returnValue({
name: 'alexis.rees@example.com'
});
let response = await request(app)
.get('/api/')
.set('Content-Type', 'application/json')
.set('Accept', 'application/json');
expect(response.status).toBe(200);
expect(response.body).toBeTruthy();
expect(response.headers['odata-version']).toBe('4.0');

const dataApplication = app.get('ExpressDataApplication');
dataApplication.getService(ODataModelBuilder).hasContextLink(() => {
return `http://server.example.com/api/$metadata`;
});
response = await request(app)
.get('/api/')
.set('Content-Type', 'application/json')
.set('Accept', 'application/json');
expect(response.status).toBe(200);
expect(Object.prototype.hasOwnProperty.call(response.body, '@odata.context')).toBeTrue();
expect(response.body['@odata.context']).toBe('http://server.example.com/api/$metadata');

});

it('should GET /api/$metadata', async () => {
// change user
spyOn(passportStrategy, 'getUser').and.returnValue({
name: 'alexis.rees@example.com'
});
let response = await request(app)
.get('/api/$metadata');
expect(response.status).toBe(200);
expect(response.body).toBeTruthy();
expect(response.headers['odata-version']).toBe('4.0');
});

it('should GET /api/users/', async () => {
// change user
spyOn(passportStrategy, 'getUser').and.returnValue({
Expand All @@ -99,6 +139,7 @@ describe('serviceRouter', () => {
.set('Accept', 'application/json');
expect(response.status).toBe(200);
expect(response.body).toBeTruthy();
expect(response.headers['odata-version']).toBe('4.0');
});

it('should use an entity set function', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"name": "releaseDate",
"title": "Release Date",
"description": "The release date of a product or product model. This can be used to distinguish the exact variant of a product.",
"type": "Date"
"type": "DateTime"
}
],
"constraints": [
Expand Down
Loading