Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
C3SL
blendb
Commits
1a83ca16
Commit
1a83ca16
authored
Aug 04, 2016
by
Eduardo L. Buratti
Browse files
Add initial implementation source code
parent
80e47b2c
Changes
9
Hide whitespace changes
Inline
Side-by-side
gulpfile.js
0 → 100644
View file @
1a83ca16
'
use strict
'
;
var
gulp
=
require
(
'
gulp
'
);
var
gutil
=
require
(
'
gulp-util
'
);
var
raml
=
require
(
'
gulp-raml
'
);
var
rename
=
require
(
'
gulp-rename
'
);
var
jshint
=
require
(
'
gulp-jshint
'
);
var
size
=
require
(
'
gulp-size
'
);
var
jscs
=
require
(
'
gulp-jscs
'
);
var
stylish
=
require
(
'
gulp-jscs-stylish
'
);
var
mocha
=
require
(
'
gulp-mocha
'
);
var
istanbul
=
require
(
'
gulp-istanbul
'
);
var
nodemon
=
require
(
'
gulp-nodemon
'
);
var
path
=
require
(
'
path
'
);
var
raml2html
=
require
(
'
raml2html
'
);
var
through
=
require
(
'
through2
'
);
var
yaml
=
require
(
'
js-yaml
'
);
var
map
=
require
(
'
map-stream
'
);
var
srcFiles
=
[
'
src/**/*.js
'
,
'
index.js
'
,
'
gulpfile.js
'
,
'
test/**/*.js
'
];
function
handleError
(
err
)
{
console
.
error
(
err
.
toString
());
process
.
exit
(
1
);
}
function
exitOnError
(
type
)
{
return
map
(
function
(
file
,
callback
)
{
if
(
!
file
[
type
].
success
)
{
process
.
exit
(
1
);
}
callback
(
null
,
file
);
});
}
function
generateDoc
(
options
)
{
var
simplifyMark
=
function
(
mark
)
{
if
(
mark
)
{
mark
.
buffer
=
mark
.
buffer
.
split
(
'
\n
'
,
mark
.
line
+
1
)[
mark
.
line
]
.
trim
();
}
};
if
(
!
options
)
{
options
=
{};
}
switch
(
options
.
type
)
{
case
'
json
'
:
options
.
config
=
{
template
:
function
(
obj
)
{
return
JSON
.
stringify
(
obj
,
null
,
2
);
}
};
break
;
case
'
yaml
'
:
options
.
config
=
{
template
:
function
(
obj
)
{
return
yaml
.
safeDump
(
obj
,
{
skipInvalid
:
true
});
}
};
break
;
default
:
options
.
type
=
'
html
'
;
if
(
!
options
.
config
)
{
options
.
config
=
raml2html
.
getDefaultConfig
(
options
.
https
,
options
.
template
,
options
.
resourceTemplate
,
options
.
itemTemplate
);
}
}
if
(
!
options
.
extension
)
{
options
.
extension
=
'
.
'
+
options
.
type
;
}
var
stream
=
through
.
obj
(
function
(
file
,
enc
,
done
)
{
var
fail
=
function
(
message
)
{
done
(
new
gutil
.
PluginError
(
'
raml2html
'
,
message
));
};
if
(
file
.
isBuffer
())
{
var
cwd
=
process
.
cwd
();
process
.
chdir
(
path
.
resolve
(
path
.
dirname
(
file
.
path
)));
raml2html
.
render
(
file
.
contents
,
options
.
config
)
.
then
(
function
(
output
)
{
process
.
chdir
(
cwd
);
stream
.
push
(
new
gutil
.
File
({
base
:
file
.
base
,
cwd
:
file
.
cwd
,
path
:
gutil
.
replaceExtension
(
file
.
path
,
options
.
extension
),
contents
:
new
Buffer
(
output
)
}));
done
();
},
function
(
error
)
{
process
.
chdir
(
cwd
);
simplifyMark
(
error
.
context_mark
);
simplifyMark
(
error
.
problem_mark
);
process
.
nextTick
(
function
()
{
fail
(
JSON
.
stringify
(
error
,
null
,
2
));
});
}
);
}
else
if
(
file
.
isStream
())
{
fail
(
'
Streams are not supported:
'
+
file
.
inspect
());
}
else
if
(
file
.
isNull
())
{
fail
(
'
Input file is null:
'
+
file
.
inspect
());
}
});
return
stream
;
}
gulp
.
task
(
'
raml
'
,
function
()
{
gulp
.
src
(
'
specs/*.raml
'
)
.
pipe
(
raml
())
.
pipe
(
raml
.
reporter
(
'
default
'
))
.
pipe
(
exitOnError
(
'
raml
'
));
});
gulp
.
task
(
'
doc
'
,
function
()
{
return
gulp
.
src
(
'
specs/*.raml
'
)
.
pipe
(
generateDoc
())
.
on
(
'
error
'
,
handleError
)
.
pipe
(
rename
({
extname
:
'
.html
'
}))
.
pipe
(
gulp
.
dest
(
'
doc/build
'
));
});
gulp
.
task
(
'
pre-test
'
,
function
()
{
return
gulp
.
src
([
'
src/**/*.js
'
])
.
pipe
(
istanbul
())
.
pipe
(
istanbul
.
hookRequire
());
});
gulp
.
task
(
'
test
'
,
[
'
pre-test
'
],
function
()
{
return
gulp
.
src
(
'
test/**/*.spec.js
'
,
{
read
:
false
})
.
pipe
(
mocha
({
require
:
[
'
./test/common.js
'
],
reporter
:
'
spec
'
,
ui
:
'
bdd
'
,
recursive
:
true
,
colors
:
true
,
timeout
:
60000
,
slow
:
300
,
delay
:
true
}))
.
pipe
(
istanbul
.
writeReports
())
.
once
(
'
error
'
,
function
()
{
process
.
exit
(
1
);
})
.
once
(
'
end
'
,
function
()
{
process
.
exit
();
});
});
gulp
.
task
(
'
lint
'
,
function
()
{
return
gulp
.
src
(
srcFiles
)
.
pipe
(
jshint
())
.
pipe
(
jscs
())
.
pipe
(
stylish
.
combineWithHintResults
())
.
pipe
(
jshint
.
reporter
(
'
jshint-stylish
'
))
.
pipe
(
size
())
.
pipe
(
exitOnError
(
'
jshint
'
));
});
gulp
.
task
(
'
check
'
,
[
'
raml
'
,
'
lint
'
,
'
test
'
]);
gulp
.
task
(
'
develop
'
,
function
()
{
return
nodemon
({
script
:
'
index.js
'
,
ext
:
'
js
'
,
tasks
:
[
'
lint
'
]
});
});
index.js
0 → 100755
View file @
1a83ca16
#!/usr/bin/env node
/*
* Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
* Departamento de Informatica - Universidade Federal do Parana
*
* This file is part of blendb.
*
* blendb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* blendb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with blendb. If not, see <http://www.gnu.org/licenses/>.
*/
'
use strict
'
;
// Add the ./src directory to require's search path to facilitate import
// modules later on (avoiding the require('../../../../module') problem).
require
(
'
app-module-path
'
).
addPath
(
__dirname
+
'
/src
'
);
// external libraries
const
osprey
=
require
(
'
osprey
'
);
const
express
=
require
(
'
express
'
);
const
path
=
require
(
'
path
'
);
const
ramlParser
=
require
(
'
raml-parser
'
);
// connect to mongodb
const
mongo
=
require
(
'
core/mongo
'
);
mongo
.
connect
(
'
mongodb://pyke/blend
'
);
// create a new express app
const
app
=
module
.
exports
=
express
();
// load router
const
router
=
require
(
'
api/router-v1.js
'
);
// parse the RAML spec and load osprey middleware
ramlParser
.
loadFile
(
path
.
join
(
__dirname
,
'
specs/blendb-api-v1.raml
'
))
.
then
(
raml
=>
{
app
.
use
(
'
/v1
'
,
osprey
.
security
(
raml
),
osprey
.
server
(
raml
),
router
);
if
(
!
module
.
parent
)
{
let
port
=
process
.
env
.
PORT
||
3000
;
app
.
listen
(
port
);
if
(
app
.
get
(
'
env
'
)
===
'
development
'
)
{
console
.
log
(
'
Server listening on port
'
+
port
+
'
.
'
);
}
}
else
{
// signalize to the test suite that the server is ready to be tested
app
.
ready
=
true
;
}
},
err
=>
{
console
.
error
(
'
RAML Parsing Error:
'
+
err
.
message
);
process
.
exit
(
1
);
});
specs/blendb-api-v1.raml
0 → 100644
View file @
1a83ca16
#%RAML 0.8
title: BlenDB API
version: v1
baseUri: http://blendb.c3sl.ufpr.br/api/{version}
mediaType: application/json
securitySchemes:
- oauth_2_0:
description: |
OAuth2 is a protocol that lets apps request authorization to
private details in the system while avoiding the use of passwords.
This is preferred over Basic Authentication because tokens can be
limited to specific types of data, and can be revoked by users at
any time.
type: OAuth 2.0
describedBy:
headers:
Authorization:
description: |
Used to send a valid OAuth 2 access token. Do not use
together with the "access_token" query string parameter.
type: string
queryParameters:
access_token:
description: |
Used to send a valid OAuth 2 access token. Do not use
together with the "Authorization" header.
type: string
responses:
401:
description: |
Bad or expired token. This can happen if access token
has expired or has been revoked by the user.
body:
application/json:
example: |
{
id: "invalid_oauth_token",
message: "Bad or expired token. This can happen if access token has expired or has been revoked by the user."
}
403:
description: |
Bad OAuth2 request (wrong consumer key, bad nonce,
expired timestamp, ...).
body:
application/json:
example: |
{
id: "invalid_oauth_request",
message: "Bad OAuth2 request (wrong consumer key, bad nonce, expired timestamp, ...)."
}
settings:
authorizationUri: http://simmc.c3sl.ufpr.br/oauth/authorize
accessTokenUri: http://simmc.c3sl.ufpr.br/oauth/access_token
authorizationGrants: [ code, token ]
scopes:
- "user"
- "user:email"
resourceTypes:
- base:
get?: &common
responses:
403:
description: API rate limit exceeded.
headers:
X-RateLimit-Limit:
type: integer
X-RateLimit-Remaining:
type: integer
X-RateLimit-Reset:
type: integer
body:
application/json:
example: |
{
id: "too_many_requests",
message: "API Rate limit exceeded."
}
post?: *common
put?: *common
delete?: *common
- collection:
type: base
get?:
description: |
List all of the <<resourcePathName>> (with optional
filtering).
responses:
200:
description: |
A list of <<resourcePathName>>.
body:
application/json:
schema: <<collectionSchema>>
example: <<collectionExample>>
post?:
description: |
Create a new <<resourcePathName|!singularize>>.
responses:
201:
description: |
Sucessfully created a new
<<resourcePathName|!singularize>>.
headers:
Location:
description: |
A link to the newly created
<<resourcePathName|!singularize>>.
type: string
409:
description: |
Failed to create a new
<<resourcePathName|!singularize>> because a conflict
with an already existing
<<resourcePathName|!singularize>> was detected.
body:
application/json:
example: |
{
"id": "already_exists",
"message": "The <<resourcePathName|!singularize>> could not be created due to a conflict with an already existing <<resourcePathName|!singularize>>."
}
- item:
type: base
get?:
description: |
Return a single <<resourcePathName|!singularize>>.
responses:
200:
description: |
A single <<resourcePathName|!singularize>>.
body:
application/json:
schema: <<itemSchema>>
example: <<itemExample>>
404:
description: |
The <<resourcePathName|!singularize>> could not be
found.
body:
application/json:
example: |
{
"id": "not_found",
"message": "The <<resourcePathName|!singularize>> could not be found."
}
put?:
description: |
Update a <<resourcePathName>>.
responses:
204:
description: |
The <<resourcePathName|!singularize>> was updated.
404:
description: |
The <<resourcePathName|!singularize>> could not be
found.
body:
application/json:
example: |
{
"id": "not_found",
"message": "The <<resourcePathName|!singularize>> could not be found."
}
409:
description: |
Failed to update the <<resourcePathName|!singularize>>
because a conflict with another
<<resourcePathName|!singularize>> was detected.
body:
application/json:
example: |
{
"id": "already_exists",
"message": "Failed to update the <<resourcePathName|!singularize>> because a conflict with another <<resourcePathName|!singularize>> was detected."
}
patch?:
description: |
Partially update a <<resourcePathName>>.
responses:
204:
description: |
The <<resourcePathName|!singularize>> was updated.
404:
description: |
The <<resourcePathName|!singularize>> could not be
found.
body:
application/json:
example: |
{
"id": "not_found",
"message": "The <<resourcePathName|!singularize>> could not be found."
}
409:
description: |
Failed to update the <<resourcePathName|!singularize>>
because a conflict with another
<<resourcePathName|!singularize>> was detected.
body:
application/json:
example: |
{
"id": "already_exists",
"message": "Failed to update the <<resourcePathName|!singularize>> because a conflict with another <<resourcePathName|!singularize>> was detected."
}
delete?:
description: |
Removes a <<resourcePathName>>.
responses:
204:
description: |
The <<resourcePathName|!singularize>> was removed.
404:
description: |
The <<resourcePathName|!singularize>> could not be
found.
body:
application/json:
example: |
{
"id": "not_found",
"message": "The <<resourcePathName|!singularize>> could not be found."
}
- index:
type: base
get?:
description: |
Return an index on the <<resourcePathName>> collection.
responses:
200:
description: |
An index on the <<resourcePathName>> collection.
body:
application/json:
traits:
- paged:
queryParameters:
page:
description: Specify the page that you want to retrieve
type: integer
default: 1
example: 1
per_page:
description: The number of items to return per page
type: integer
minimum: 1
maximum: 50
default: 10
example: 20
- searchable:
queryParameters:
query:
description: |
Query string that filters the data returned for your
request.
type: string
- filtered:
queryParameters:
filters:
description: |
Filters that restrict the data returned for your request.
type: string
- projectable:
queryParameters:
fields:
description: |
Fields to be returned.
type: string
/metrics:
description: |
A Metric represents a statistic that can be queried to generate reports.
This collection allows the user to list all the metrics available in the
system and their descriptions.
securedBy: [ null, oauth_2_0 ]
get:
/dimensions:
description: |
A Dimension allows the data to be aggregated by one or more columns.
This collection allows the user to list all the dimensions available in
the system and their descriptions.
securedBy: [ null, oauth_2_0 ]
get:
/data:
description: |
This is the main part of the API. You may query it for report
data by specifying metrics (at least one). You may also supply
additional query parameters such as dimensions, filters, and
start/end dates to refine your query.
type: base
get:
is: [ filtered ]
queryParameters:
metrics:
description: |
A list of comma-separated metrics.
type: string
required: true
example: "met:daysSinceLastContact,met:estimatedNetworkBandwidth"
dimensions:
description: |
A list of comma-separated dimensions.
type: string
required: true
example: "dim:project,dim:point"
start-date:
description: |
Start date for fetching data. Requests can specify a
start date formatted as YYYY-MM-DD, or as a relative date
(e.g., today, yesterday, or NdaysAgo where N is a positive
integer).
type: string
required: false
pattern: "[0-9]{4}-[0-9]{2}-[0-9]{2}|today|yesterday|[0-9]+(daysAgo)"
example: 7daysAgo
end-date:
description: |
End date for fetching data. Requests can specify a
end date formatted as YYYY-MM-DD, or as a relative date
(e.g., today, yesterday, or NdaysAgo where N is a positive
integer).
type: string
required: false
pattern: "[0-9]{4}-[0-9]{2}-[0-9]{2}|today|yesterday|[0-9]+(daysAgo)"
example: yesterday
filters:
description: |
Filters that restrict the data returned for your request.
type: string
example: "dim:location(4).id%3D%3D10723"
sort:
description: |
A list of comma-separated dimensions and metrics
indicating the sorting order and sorting direction for
the returned data.
type: string
example: "dim:project"
responses:
200:
description: |
Query successfully executed. Data is returned in a table format.
body:
application/json:
400:
description: |
The supplied query is invalid. Specified metric or dimension
doesn't exist, incorrect formatting for a filter, unacceptable
date range, etc.
body:
application/json:
example: |
{
"id": "metric_not_found",
"message": "The specified metric 'met:electricCharge' could not be found."
}
/collect/{class}:
description: |
This API may be used to send data to the monitoring system. There are a
few available data types (like network bandwidth usage, machine
inventory, etc.) and each of them requires a specific format for the
data being sent.
type: base
uriParameters:
class:
description: The class of data that is being collected.
type: string
minLength: 4
maxLength: 64
pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$