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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ loginEvent: (username) => {
deviceType: 'Browser',
eventTime: new Date().getTime(),
eventType: 'login',
eventData: {}
eventData: {},
eventApiEnv: `${EVENT_API_ENV}`
})
}
```
Expand All @@ -62,7 +63,8 @@ playEpisodeEvent: (username, playEvent) => {
episodeName: playEvent.episodeName,
minutesPlayed: playEvent.minutesPlayed,
minutesRemaining: playEvent.minutesRemaining
}
},
eventApiEnv: `${EVENT_API_ENV}`
})
}
```
61 changes: 61 additions & 0 deletions server/docs/definitions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
definitions:
Error:
type: object
properties:
message:
type: string
description: Reason for error
example: Error message
stack:
type: string
description: Stack trace resulting in error
Event:
type: object
properties:
clientId:
type: string
description: Id of user initiating event
example: user01
deviceType:
type: string
description: type of device where the event originates
enum: [iOS, Android, Browser, API]
example: Android
eventApiEnv:
type: string
description: environment where the event originates
enum: [production, test]
example: test
eventTime:
type: string
format: unix-time
description: valid timestamp of when event is triggered
example: 1522767185
eventData:
type: object
description: raw data of the event being streamed
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The eventData maps to a specific eventType's required keys. For example the playEpisode eventType eventData has 3 fields: episodeName, minutesPlayed, and minutesRemaining. I would add an example, like:

example: {
    episodeName: "Ethereum Governance with Hudson Jameson",
    minutesPlayed: 1622.53,
    minutesRemaining: 117.62
}

eventType:
type: string
description: type of event being streamed
enum: [register,
login,
logout,
playEpisode,
pauseEpisode,
likeEpisode,
completedEpisode,
fastForwardEpisode,
rewindEpisode,
seekEpisode,
searchEpisode]
required:
- clientId
- deviceType
- eventApiEnv
- eventTime
- eventData
- eventType
Token:
type: string
description: JSON web token to store credentials
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1OWY4Njc3YzFkNmZmZDNkMGJkODMzNGIiLCJwYXNzd29yZCI6IiQyYSQwOCRoMEgyR2FWbWh1V0tNd1hSWlRYekxPcDMuWG85OGJmYVZYWXp2cURuU2p1SGlrZE9YRmd3LiIsInVzZXJuYW1lIjoiam9obkBqYWNvYi5jb20iLCJfX3YiOjAsImNyZWF0ZWRBdCI6IjIwMTctMTAtMzFUMTI6MDc6MjQuNTAyWiIsImlhdCI6MTUwOTQ1Mjk0MywiZXhwIjoxNjUzNDUyOTQzfQ.2yECxNnmM6u9wcUkYq1OPX6HzfcLeaOgoQ35PJ2fQZs
91 changes: 91 additions & 0 deletions server/docs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>SED API Docs</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.4.0/swagger-ui.css">
<style>
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}

*,
*:before,
*:after {
box-sizing: inherit;
}

body {
margin: 0;
background: #fafafa;
}
</style>
</head>

<body>

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>

<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>

<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>

<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>

<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>


<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>

<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>

</defs>
</svg>

<div id="swagger-ui"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.4.0/swagger-ui-bundle.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.4.0/swagger-ui-standalone-preset.js">
</script>
<script>
window.onload = function() {
window.ui = SwaggerUIBundle({
url: "/api/v1/docs/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
}
</script>
</body>

</html>
41 changes: 41 additions & 0 deletions server/docs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const swaggerJSDoc = require('swagger-jsdoc');
const express = require('express');

const router = express.Router(); // eslint-disable-line new-cap

// Swagger definition aka OpenAPI v2.0
// https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md

const swaggerDefinition = {
info: {
// API information (required)
title: 'SEDaily DevOps', // Title (required)
version: '1.0.0', // Version (required)
description: 'Software Engineering Daily Logging, Monitoring, and Analytics API Documentation. You can use these by not only browsing the API routes, but also by executing requests against the server.'
},
basePath: '/api/v1',
produces: ['application/json'],
consumes: ['application/json'],
};

const options = {
swaggerDefinition,
apis: [
// controllers include parameters, tags and paths
'./server/controllers/*.js',
// a few "general" paths are defined in index
'./server/routes/*.route.js',
// responses, securityDefinitions and general definitions/parameters in separate yaml
'./server/docs/*.yaml',
]
};
const swaggerSpec = swaggerJSDoc(options);

router.get('/swagger.json', (req, res) => {
res.setHeader('Content-Type', 'application/json');
res.send(swaggerSpec);
});

router.use('/', express.static(__dirname));

module.exports = router;
28 changes: 28 additions & 0 deletions server/docs/responses.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
responses:
NotFound:
description: The specified resource was not found
schema:
$ref: '#/definitions/Error'
Unauthorized:
description: Unauthorized
schema:
$ref: '#/definitions/Error'
BadRequest:
description: Bad request - missing required elements
schema:
$ref: '#/definitions/Error'
SuccessfulAuthentication:
description: Successful authentication
schema:
type: object
properties:
token:
$ref: '#/definitions/Token'
Success:
description: successful operation
schema:
type: object
properties:
result:
type: string
enum: 'success'
9 changes: 9 additions & 0 deletions server/docs/securityDefinitions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
securityDefinitions:
Token:
description: |
For accessing the protected API resources, you must have received a a valid JWT token after registering or logging in. This JWT token must then be used for all protected resources by passing it in via the 'Authorization' header.

A JWT token is generated by the API by either registering via /auth/register or logging in via /auth/login.
type: apiKey
name: Authorization
in: header
32 changes: 32 additions & 0 deletions server/routes/event.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,38 @@ const router = express.Router();
// Disabling validation for now
// router.use(expressJwt({ secret: config.jwtSecret }));

/**
* @swagger
* tags:
* - name: event
* description: Sending user events
*/

/**
* @swagger
* /event:
* post:
* summary: Stream an event
* description: Stream an event
* tags: [event]
* parameters:
* - in: body
* name: event
* schema:
* $ref: '#/definitions/Event'
* required: true
* description: Event to stream
* responses:
* '200':
* $ref: '#/responses/Success'
* '400':
* $ref: '#/responses/BadRequest'
* '401':
* $ref: '#/responses/Unauthorized'
* '404':
* $ref: '#/responses/NotFound'
*/

router.route('/')
.post(validate(paramValidation.event), eventCtrl.validateEventType, eventCtrl.newEvent);

Expand Down
21 changes: 21 additions & 0 deletions server/routes/index.route.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import express from 'express';
import eventRoutes from './event.route';
import errorRoutes from './error.route';
import docRoutes from '../docs';

const router = express.Router(); // eslint-disable-line new-cap

/**
* @swagger
* tags:
* - name: general
* description: General/server-related
*/

/**
* @swagger
* /health-check:
* get:
* summary: Health check of server
* description: Confirm SED API server running and okay
* tags: [general]
* responses:
* 200:
* description: successful operation
*/

router.get('/health-check', (req, res) =>
res.send('OK'));

router.use('/event', eventRoutes);
router.use('/error', errorRoutes);
router.use('/docs', docRoutes);

export default router;