Skip to content

Commit 6f53407

Browse files
authored
Merge pull request #1205 from jembi/TB-151-apps-endpoints
Tb 151 apps endpoints
2 parents 563d83c + 7d4d45f commit 6f53407

File tree

6 files changed

+458
-2
lines changed

6 files changed

+458
-2
lines changed

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "openhim-core",
33
"description": "The OpenHIM core application that provides logging and routing of http requests",
4-
"version": "8.1.2",
4+
"version": "8.2.0",
55
"main": "./lib/server.js",
66
"bin": {
77
"openhim-core": "./bin/openhim-core.js"

src/api/apps.js

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
'use strict'
2+
3+
import logger from 'winston'
4+
5+
import * as authorisation from './authorisation'
6+
import {AppModelAPI} from '../model/apps'
7+
8+
/*
9+
Checks admin permission for create, update and delete operations.
10+
Throws error if user does not have admin access
11+
*/
12+
const checkUserPermission = (ctx, operation) => {
13+
if (!authorisation.inGroup('admin', ctx.authenticated)) {
14+
ctx.statusCode = 403
15+
throw Error(
16+
`User ${ctx.authenticated.email} is not an admin, API access to ${operation} an app denied.`
17+
)
18+
}
19+
}
20+
21+
/*
22+
Returns app if it exists, if not it throws an error
23+
*/
24+
const checkAppExists = async (ctx, appId) => {
25+
const app = await AppModelAPI.findById(appId)
26+
27+
if (!app) {
28+
ctx.statusCode = 404
29+
throw Error(`App with id ${appId} does not exist`)
30+
}
31+
32+
return app
33+
}
34+
35+
// Creates error response for operations create, read, update and delete
36+
const createErrorResponse = (ctx, operation, error) => {
37+
logger.error(`Could not ${operation} an app via the API: ${error.message}`)
38+
39+
ctx.body = {
40+
error: error.message
41+
}
42+
ctx.status = ctx.statusCode ? ctx.statusCode : 500
43+
}
44+
45+
const validateId = (ctx, id) => {
46+
if (!id.match(/^[0-9a-fA-F]{24}$/)) {
47+
ctx.statusCode = 400
48+
throw Error(`App id "${id}" is invalid. ObjectId should contain 24 characters`)
49+
}
50+
}
51+
52+
export async function addApp(ctx) {
53+
try {
54+
checkUserPermission(ctx, 'add')
55+
56+
const app = new AppModelAPI(ctx.request.body)
57+
58+
await app
59+
.save()
60+
.then(app => {
61+
logger.info(`User ${ctx.request.email} created app ${app.name}`)
62+
63+
ctx.status = 201
64+
ctx.body = app
65+
})
66+
.catch(e => {
67+
ctx.statusCode = 400
68+
throw e
69+
})
70+
} catch (e) {
71+
createErrorResponse(ctx, 'add', e)
72+
}
73+
}
74+
75+
export async function updateApp(ctx, appId) {
76+
try {
77+
checkUserPermission(ctx, 'update')
78+
79+
validateId(ctx, appId)
80+
81+
await checkAppExists(ctx, appId)
82+
83+
const update = ctx.request.body
84+
85+
await AppModelAPI.findOneAndUpdate({_id: appId}, update, {
86+
new: true,
87+
runValidators: true
88+
})
89+
.then(app => {
90+
logger.info(`User ${ctx.authenticated.email} updated app ${app.name}`)
91+
92+
ctx.body = app
93+
ctx.status = 200
94+
})
95+
.catch(e => {
96+
ctx.statusCode = 400
97+
throw e
98+
})
99+
} catch (e) {
100+
createErrorResponse(ctx, 'update', e)
101+
}
102+
}
103+
104+
export async function getApps(ctx) {
105+
try {
106+
const apps = await AppModelAPI.find(ctx.request.query)
107+
108+
logger.info(`User ${ctx.authenticated.email} fetched ${apps.length} apps`)
109+
110+
ctx.body = apps
111+
ctx.status = 200
112+
} catch (e) {
113+
createErrorResponse(ctx, 'retrieve', e)
114+
}
115+
}
116+
117+
export async function getApp(ctx, appId) {
118+
try {
119+
validateId(ctx, appId)
120+
121+
const app = await checkAppExists(ctx, appId)
122+
123+
logger.info(`User ${ctx.authenticated.email} app fetched ${appId}`)
124+
125+
ctx.body = app
126+
ctx.status = 200
127+
} catch (e) {
128+
createErrorResponse(ctx, 'retrieve', e)
129+
}
130+
}
131+
132+
export async function deleteApp(ctx, appId) {
133+
try {
134+
checkUserPermission(ctx, 'delete')
135+
136+
validateId(ctx, appId)
137+
138+
await checkAppExists(ctx, appId)
139+
140+
await AppModelAPI.deleteOne({_id: appId}).then(() => {
141+
logger.info(`User ${ctx.authenticated.email} deleted app ${appId}`)
142+
143+
ctx.status = 200
144+
ctx.body = {
145+
success: true
146+
}
147+
})
148+
} catch (e) {
149+
createErrorResponse(ctx, 'delete', e)
150+
}
151+
}

src/koaApi.js

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import session from 'koa-session'
88
import compose from 'koa-compose'
99

1010
import * as about from './api/about'
11+
import * as apps from './api/apps'
1112
import * as audits from './api/audits'
1213
import * as authentication from './api/authentication'
1314
import * as certificateAuthority from './api/certificateAuthority'
@@ -136,6 +137,12 @@ export function setupApp(done) {
136137
app.use(route.get('/logout', users.logout))
137138

138139
// Define the api routes
140+
app.use(route.get('/apps', apps.getApps))
141+
app.use(route.get('/apps/:appId', apps.getApp))
142+
app.use(route.put('/apps/:appId', apps.updateApp))
143+
app.use(route.post('/apps', apps.addApp))
144+
app.use(route.delete('/apps/:appId', apps.deleteApp))
145+
139146
app.use(route.get('/users', users.getUsers))
140147
app.use(route.get('/users/:email', users.getUser))
141148
app.use(route.post('/users', users.addUser))

src/model/apps.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict'
2+
3+
import {Schema} from 'mongoose'
4+
5+
import {connectionAPI, connectionDefault} from '../config'
6+
7+
const AppSchema = new Schema({
8+
name: {
9+
type: String,
10+
unique: true,
11+
required: true
12+
},
13+
description: String,
14+
icon: {
15+
data: Buffer,
16+
contentType: String
17+
},
18+
category: String,
19+
access_roles: [String],
20+
url: {
21+
type: String,
22+
unique: true,
23+
required: true
24+
},
25+
showInPortal: {
26+
type: Boolean,
27+
default: true
28+
},
29+
showInSideBar: Boolean
30+
})
31+
32+
export const AppModelAPI = connectionAPI.model('App', AppSchema)
33+
export const AppModel = connectionDefault.model('App', AppSchema)

0 commit comments

Comments
 (0)