Skip to content
Merged
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
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<img src="https://dadi.tech/assets/products/dadi-api-full.png" alt="DADI API" height="65"/>
<img src="https://dadi.cloud/assets/products/dadi-api-full.png" alt="DADI API" height="65"/>

[![npm (scoped)](https://img.shields.io/npm/v/@dadi/api.svg?maxAge=10800&style=flat-square)](https://www.npmjs.com/package/@dadi/api)
[![coverage](https://img.shields.io/badge/coverage-88%25-yellow.svg?style=flat)](https://github.com/dadi/api)
Expand All @@ -14,9 +14,9 @@

## Overview

DADI API is built on Node.JS. It is a high performance RESTful API layer designed in support of [API-first development and the principle of COPE](https://dadi.tech/platform/concepts/api-first-and-cope/). It can use virtually any database engine, such as [MongoDB](https://github.com/dadi/api-mongodb), [CouchDB](https://github.com/dadi/api-couchdb), [RethinkDB](https://github.com/dadi/api-rethinkdb) or simply a [JSON filestore](https://github.com/dadi/api-filestore).
DADI API is built on Node.JS. It is a high performance RESTful API layer designed in support of API-first development and the principle of COPE. It can use virtually any database engine, such as [MongoDB](https://github.com/dadi/api-mongodb), [CouchDB](https://github.com/dadi/api-couchdb), [RethinkDB](https://github.com/dadi/api-rethinkdb) or simply a [JSON filestore](https://github.com/dadi/api-filestore).

You can consider it as the data layer within a platform (including the data model). It is designed to be plugged into a templating layer (such as [DADI Web](https://dadi.tech/platform/web)), a mobile application or to be used with any other data consumer.
You can consider it as the data layer within a platform (including the data model). It is designed to be plugged into a templating layer (such as [DADI Web](https://dadi.cloud/en/web)), a mobile application or to be used with any other data consumer.

Calls to a DADI API can contain your business/domain logic (the part of a platform that encodes the real-world business rules that determine how data is created, displayed, stored and changed). It has full support for searching, filtering, limiting, sorting, offsetting, input validation and data aggregation (through support for MongoDB's aggregation pipeline).

Expand All @@ -35,7 +35,7 @@ It is part of DADI, a suite of components covering the full development stack, b

### Install API

The quickest way to get started with *API* is to use [DADI CLI](https://github.com/dadi/cli). See [Creating an API](https://docs.dadi.tech/#api/creating-an-api) for full installation details.
The quickest way to get started with *API* is to use [DADI CLI](https://github.com/dadi/cli). See [Creating an API](https://docs.dadicloud/api) for full installation details.


### Configuration
Expand Down Expand Up @@ -95,7 +95,7 @@ Connection: keep-alive

The HTTP 401 response received in the previous step shows that the server is running. To start using the REST endpoints you'll need a user account so you can obtain access tokens for interacting with the API.

User accounts provide an authentication layer for API. Each user account has a *__clientId__* and a *__secret__*. These are used to obtain access tokens for interacting with the API. See the [Authentication](https://docs.dadi.tech/#api/authentication) section of the API documentation for full details.
User accounts provide an authentication layer for API. Each user account has a *__clientId__* and a *__secret__*. These are used to obtain access tokens for interacting with the API. See the [Authentication](https://docs.dadi.cloud/api#authentication) section of the API documentation for full details.

#### Creating the first user

Expand Down Expand Up @@ -144,11 +144,11 @@ $ npm test


## Links
* [API Documentation](https://docs.dadi.tech/#api/)
* [API Documentation](https://docs.dadi.cloud/api/)

## Contributors

DADI API is based on an original idea by Joseph Denne. It is developed and maintained by the engineering team at DADI ([https://dadi.tech](https://dadi.tech))
DADI API is based on an original idea by Joseph Denne. It is developed and maintained by the engineering team at DADI ([https://dadi.cloud](https://dadi.cloud))

* Adam K Dean <akd@dadi.co>
* Arthur Mingard <am@dadi.co>
Expand All @@ -167,7 +167,7 @@ DADI API is based on an original idea by Joseph Denne. It is developed and maint
DADI is a data centric development and delivery stack, built specifically in support of the principles of API first and COPE.

Copyright notice<br />
(C) 2018 DADI+ Limited <support@dadi.tech><br />
(C) 2018 DADI+ Limited <support@dadi.cloud><br />
All rights reserved

This product is part of DADI.<br />
Expand Down
11 changes: 8 additions & 3 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,13 @@ var conf = convict({
},
s3: {
accessKey: {
doc: 'The AWS access key used to connect to S3',
doc: 'The access key used to connect to an S3-compatible storage provider',
format: String,
default: '',
env: 'AWS_S3_ACCESS_KEY'
},
secretKey: {
doc: 'The AWS secret key used to connect to S3',
doc: 'The secret key used to connect to an S3-compatible storage provider',
format: String,
default: '',
env: 'AWS_S3_SECRET_KEY'
Expand All @@ -325,10 +325,15 @@ var conf = convict({
env: 'AWS_S3_BUCKET_NAME'
},
region: {
doc: 'The AWS region',
doc: 'The region for an S3-compatible storage provider',
format: String,
default: '',
env: 'AWS_S3_REGION'
},
endpoint: {
doc: 'The endpoint for an S3-compatible storage provider',
format: String,
default: ''
}
}
},
Expand Down
78 changes: 59 additions & 19 deletions dadi/lib/controller/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const Busboy = require('busboy')
const imagesize = require('imagesize')
const PassThrough = require('stream').PassThrough
const path = require('path')
const serveStatic = require('serve-static')
const sha1 = require('sha1')
const url = require('url')

Expand Down Expand Up @@ -78,18 +77,12 @@ MediaController.prototype.count = function (req, res, next) {
}

/**
* Serve a media file from its location on disk.
* Serve a media file from its location.
*/
MediaController.prototype.getFile = function (req, res, next, route) {
// `serveStatic` will look at the entire URL to find the file it needs to
// serve, but we're not serving files from the root. To get around this, we
// pass it a modified version of the URL, where the root URL becomes just the
// filename parameter.
const modifiedReq = Object.assign({}, req, {
url: `${route}/${req.params.filename}`
})
let storageHandler = StorageFactory.create(req.params.filename)

return serveStatic(config.get('media.basePath'))(modifiedReq, res, next)
return storageHandler.get(req.params.filename, route, req, res, next)
}

/**
Expand Down Expand Up @@ -211,22 +204,24 @@ MediaController.prototype.post = function (req, res, next) {
_createdBy: req.client && req.client.clientId
}

const callback = (err, response) => {
response.results = response.results.map(document => {
return mediaModel.formatDocuments(document)
})

help.sendBackJSON(201, res, next)(err, response)
}

return this.writeFile(req, this.fileName, this.mimetype, dataStream).then(result => {
if (fields.includes('contentLength')) {
obj.contentLength = result.contentLength
}

obj.path = result.path

this.model.create(obj, internals, callback, req)
return this.model.create({
documents: obj,
internals,
req
}).then(response => {
response.results = response.results.map(document => {
return mediaModel.formatDocuments(document)
})

help.sendBackJSON(201, res, next)(err, response)
})
})
})
})
Expand Down Expand Up @@ -256,6 +251,51 @@ MediaController.prototype.post = function (req, res, next) {
}
}

MediaController.prototype.delete = function (req, res, next) {
let query = req.params.id ? { _id: req.params.id } : req.body.query

if (!query) return next()

this.model.get({
query, req
}).then(results => {
if (!results.results[0]) return next()

let file = results.results[0]

// remove physical file
let storageHandler = StorageFactory.create(file.fileName)

storageHandler.delete(file)
.then(result => {
this.model.delete({
query,
req
}).then(({deletedCount, totalCount}) => {
if (config.get('feedback')) {
// Send 200 with JSON payload.
return help.sendBackJSON(200, res, next)(null, {
status: 'success',
message: 'Documents deleted successfully',
deleted: deletedCount,
totalCount
})
}

// Send 204 with no content.
res.statusCode = 204
res.end()
}).catch(error => {
return help.sendBackJSON(200, res, next)(error)
})
}).catch(err => {
return next(err)
})
}).catch(err => {
return next(err)
})
}

/**
*
*/
Expand Down
13 changes: 12 additions & 1 deletion dadi/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,7 @@ Server.prototype.addComponent = function (options) {

this.components[mediaRoute] = options.component
this.components[mediaRoute + '/:token?'] = options.component
this.components[mediaRoute + '/' + idParam] = options.component
this.components[mediaRoute + '/:filename(.*png|.*jpg|.*jpeg|.*gif|.*bmp|.*tiff|.*pdf)'] = options.component

if (options.component.setRoute) {
Expand Down Expand Up @@ -1157,7 +1158,7 @@ Server.prototype.addComponent = function (options) {

// GET media
this.app.use(mediaRoute, (req, res, next) => {
var method = req.method && req.method.toLowerCase()
let method = req.method && req.method.toLowerCase()
if (method !== 'get') return next()

if (options.component[method]) {
Expand All @@ -1171,6 +1172,16 @@ Server.prototype.addComponent = function (options) {
return options.component.getFile(req, res, next, mediaRoute)
}
})

// DELETE media
this.app.use(`${mediaRoute}/${idParam}`, (req, res, next) => {
let method = req.method && req.method.toLowerCase()
if (method !== 'delete') return next()

if (options.component[method]) {
return options.component[method](req, res, next)
}
})
}
}

Expand Down
40 changes: 18 additions & 22 deletions dadi/lib/search/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
var _ = require('underscore')
var path = require('path')
var url = require('url')
var help = require(path.join(__dirname, '/../help'))
var model = require(path.join(__dirname, '/../model'))
const path = require('path')
const url = require('url')
const help = require(path.join(__dirname, '/../help'))
const model = require(path.join(__dirname, '/../model'))
/*

Search middleware allowing cross-collection querying
Expand All @@ -17,55 +16,52 @@ http://api.example.com/1.0/search?collections=library/books,library/films&query=

*/
module.exports = function (server) {
server.app.use('/:version/search', function (req, res, next) {
// sorry, we only process GET requests at this endpoint
var method = req.method && req.method.toLowerCase()
if (method !== 'get') {
server.app.use('/:version/search', (req, res, next) => {
if (req.method && req.method.toLowerCase() !== 'get') {
return next()
}

var path = url.parse(req.url, true)
var options = path.query
let parsedUrl = url.parse(req.url, true)
let options = parsedUrl.query

// no collection and no query params
if (!(options.collections && options.query)) {
return help.sendBackJSON(400, res, next)(null, {'error': 'Bad Request'})
}

// split the collections param
var collections = options.collections.split(',')
let collections = options.collections.split(',')

// extract the query from the querystring
var query = help.parseQuery(options.query)
let query = help.parseQuery(options.query)

// determine API version
var apiVersion = path.pathname.split('/')[1]
let apiVersion = parsedUrl.pathname.split('/')[1]

// no collections specfied
if (collections.length === 0) {
return help.sendBackJSON(400, res, next)(null, {'error': 'Bad Request'})
}

var results = {}
var idx = 0
let results = {}
let idx = 0

_.each(collections, function (collection) {
collections.forEach(collection => {
// get the database and collection name from the
// collection parameter
var parts = collection.split('/')
var database, name, mod
let parts = collection.split('/')
let database, name, mod

query._apiVersion = apiVersion

if (_.isArray(parts) && parts.length > 1) {
if (Array.isArray(parts) && parts.length > 1) {
database = parts[0]
name = parts[1]
mod = model(name, null, null, database)
}

if (mod) {
// query!
mod.find(query, function (err, docs) {
mod.find(query, (err, docs) => {
if (err) {
return help.sendBackJSON(500, res, next)(err)
}
Expand Down
Loading