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

chore: merge master into next #740

Merged
merged 9 commits into from
Jan 3, 2024
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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# [2.33.0](https://github.com/dhis2/app-hub/compare/v2.32.2...v2.33.0) (2023-08-01)


### Bug Fixes

* add docs for slack arguments ([de11f5b](https://github.com/dhis2/app-hub/commit/de11f5bb899f6ecf616c9b81076961c95b7e62f5))
* debug log ([5188baf](https://github.com/dhis2/app-hub/commit/5188baf7dfb569e989e590b5d46b633fc02243aa))
* **slackmessager:** update review now link ([4b9875d](https://github.com/dhis2/app-hub/commit/4b9875dba49b60370ff86023244121da8da9e121))
* slackwebhookmessager rename ([d86ba80](https://github.com/dhis2/app-hub/commit/d86ba80fc6fbc4d6e317b4ca4371c52056a45f1b))


### Features

* **notifications:** add slack webhook for new apps ([2f222ee](https://github.com/dhis2/app-hub/commit/2f222ee7a89c3259889d956444cbd2ba91aaa8f6))

## [2.32.2](https://github.com/dhis2/app-hub/compare/v2.32.1...v2.32.2) (2023-05-16)


Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "client",
"version": "2.32.2",
"version": "2.33.0",
"description": "The App Hub Client",
"repository": "https://github.com/dhis2/app-hub",
"author": "Birk Johansson <[email protected]>",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@
"cypress-cucumber-preprocessor": {
"nonGlobalStepDefinitions": true
},
"version": "2.32.2"
"version": "2.33.0"
}
3 changes: 2 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "server",
"version": "2.32.2",
"version": "2.33.0",
"description": "The App Hub Server",
"main": "src/main.js",
"repository": "https://github.com/dhis2/app-hub",
Expand Down Expand Up @@ -29,6 +29,7 @@
"@hapi/inert": "^7.1.0",
"@hapi/vision": "^7.0.1",
"@hapipal/schmervice": "^2.1.0",
"@slack/webhook": "^6.1.0",
"adm-zip": "^0.5.5",
"auth0": "^2.36.1",
"aws-sdk": "^2.981.0",
Expand Down
11 changes: 10 additions & 1 deletion server/src/routes/v1/apps/formatting/convertAppsToApiV1Format.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,17 @@ const convertDbAppViewRowToAppApiV1Object = (app) => ({
reviews: [],
})

const getMediaUrl = ({ serverUrl, organisationSlug, appId, mediaId }) =>
`${serverUrl}/v1/apps/media/${organisationSlug}/${appId}/${mediaId}`

const convertAppToV1Media = (app, serverUrl) => {
return {
imageUrl: `${serverUrl}/v1/apps/media/${app.organisation_slug}/${app.app_id}/${app.media_id}`,
imageUrl: getMediaUrl({
serverUrl,
organisationSlug: app.organisation_slug,
appId: app.app_id,
mediaId: app.media_id,
}),
caption: '',
created: +new Date(app.media_created_at),
description: '',
Expand Down Expand Up @@ -122,4 +130,5 @@ const convertAll = (apps, request) => {
module.exports = {
convertAppsToApiV1Format: convertAll,
convertAppToV1AppVersion,
getMediaUrl,
}
13 changes: 9 additions & 4 deletions server/src/routes/v1/apps/formatting/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
const {
convertAppToV1AppVersion,
convertAppsToApiV1Format,
getMediaUrl,
} = require('./convertAppsToApiV1Format')

module.exports = {
convertAppToV1AppVersion: require('./convertAppsToApiV1Format')
.convertAppToV1AppVersion,
convertAppsToApiV1Format: require('./convertAppsToApiV1Format')
.convertAppsToApiV1Format,
convertAppToV1AppVersion,
convertAppsToApiV1Format,
getMediaUrl,
}
36 changes: 31 additions & 5 deletions server/src/routes/v1/apps/handlers/createApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ const {
} = require('../../../../security')
const App = require('../../../../services/app')
const Organisation = require('../../../../services/organisation')
const { saveFile } = require('../../../../utils')
const { saveFile, getServerUrl } = require('../../../../utils')
const { validateImageMetadata } = require('../../../../utils/validateMime')
const { getMediaUrl } = require('../../apps/formatting')
const { server } = require('@hapi/hapi')

module.exports = {
method: 'POST',
Expand Down Expand Up @@ -39,6 +41,7 @@ module.exports = {
if (!canCreateApp(request, h)) {
throw Boom.unauthorized()
}
const { notificationService } = request.services(true)

const { db } = h.context
const { id: currentUserId } = await getCurrentUserFromRequest(
Expand Down Expand Up @@ -77,11 +80,12 @@ module.exports = {
)
}

const app = await db.transaction(async trx => {
const { appType } = appJsonPayload
const { appType } = appJsonPayload
const { name, description, sourceUrl } = appJsonPayload
let logoMediaId
const app = await db.transaction(async (trx) => {
const { file } = payload

const { name, description, sourceUrl } = appJsonPayload
const {
version,
demoUrl,
Expand Down Expand Up @@ -142,7 +146,7 @@ module.exports = {
const logoMetadata = logo.hapi
validateImageMetadata(request.server.mime, logoMetadata)

const { id: logoId } = await App.createMediaForApp(
const { id: logoId, media_id } = await App.createMediaForApp(
app.id,
{
userId: currentUserId,
Expand All @@ -154,6 +158,7 @@ module.exports = {
},
trx
)
logoMediaId = logoId

const appUpload = saveFile(
`${app.id}/${appVersion.id}`,
Expand All @@ -166,6 +171,27 @@ module.exports = {
return app
})

const imageUrl = getMediaUrl({
serverUrl: getServerUrl(request),
organisationSlug: organisation.slug,
appId: app.id,
mediaId: logoMediaId,
})
notificationService
.sendNewAppNotifications({
appName: name,
organisationName: organisation.name,
imageUrl,
sourceUrl,
link: `${getServerUrl(request, { base: true })}/user/app/${
app.id
}`,
})
.catch((e) => {
//.catch() so we don't have to await
request.logger.error('Failed to send notification %o', e)
})

return h.response(app).created(`/v2/apps/${app.id}`)
},
}
2 changes: 1 addition & 1 deletion server/src/routes/v1/apps/handlers/getSingleApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module.exports = {
db
)
isDeveloper =
appsUserCanEdit.map(app => app.app_id).indexOf(appId) !== -1
appsUserCanEdit.map((app) => app.app_id).indexOf(appId) !== -1
} catch (err) {
//no user on request
debug('No user in request')
Expand Down
3 changes: 3 additions & 0 deletions server/src/server/env-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ const config = {
dsn: process.env.SENTRY_DSN,
environment: process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV,
},
slack: {
webhookUrl: process.env.SLACK_WEBHOOK_URL,
},
auth: {
noAuthUserIdMapping: process.env.NO_AUTH_MAPPED_USER_ID,
config: {
Expand Down
4 changes: 4 additions & 0 deletions server/src/server/init-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const staticFrontendRoutes = require('../plugins/staticFrontendRoutes')
const { getUserDecoration } = require('../security')
const { createAppVersionService } = require('../services/appVersion')
const { createEmailService } = require('../services/EmailService')
const {
createNotificationService,
} = require('../services/NotificationService/NotificationService.js')

exports.init = async (knex, config) => {
debug('Starting server...')
Expand Down Expand Up @@ -109,6 +112,7 @@ exports.init = async (knex, config) => {

await server.registerService(createEmailService)
await server.registerService(createAppVersionService)
await server.registerService(createNotificationService)

await server.register({
plugin: staticFrontendRoutes,
Expand Down
25 changes: 25 additions & 0 deletions server/src/services/NotificationService/NotificationMessager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const messagerTypes = {
WEBHOOK: 'webhook',
email: 'email',
}

class NotificationMessager {
constructor(name, { type } = {}) {
if (this.constructor === NotificationMessager) {
throw new TypeError(
'Class "NotificationMessager" cannot be instantiated directly.'
)
}
this.name = name
this.type = type
}

send() {
throw new Error('Method "sendNotification" must be implemented.')
}
}

module.exports = {
messagerTypes,
NotificationMessager,
}
72 changes: 72 additions & 0 deletions server/src/services/NotificationService/NotificationService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const Schmervice = require('@hapipal/schmervice')
const { SlackWebhookMessager } = require('./SlackWebhookMessager.js')

class NotificationService extends Schmervice.Service {
constructor(server, schmerviceOptions, { messagers }) {
super(server, schmerviceOptions)

if (!messagers || messagers.length < 1) {
server.logger.warn(
'No messagers provided to NotificationService, notifications will not be sent.'
)
} else {
const messagersName = messagers.map((m) => m.name).join(', ')
server.logger.info(
`Init NotificationService with messagers: ${messagersName}`
)
}

this.messagers = messagers
}

async sendNewAppNotifications({
appName,
imageUrl,
link,
organisationName,
sourceUrl,
}) {
const newAppMessagers = this.messagers.filter(
(m) => !!m.sendNewAppNotification
)
if (newAppMessagers.length < 1) {
return Promise.resolve(null)
}

const promises = newAppMessagers.map((messager) => {
this.server.logger.info(
`Sending new app notification, using messager: ${messager.name}`
)
return messager.sendNewAppNotification({
appName,
imageUrl,
link,
organisationName,
sourceUrl,
})
})
return Promise.all(promises)
}
}

const createNotificationService = (server, schmerviceOptions) => {
const service = new NotificationService(server, schmerviceOptions, {
messagers: createMessagers(server),
})
return Schmervice.withName('notificationService', service)
}

const createMessagers = (server) => {
const messagers = []
const { config } = server.realm.settings.bind
if (config.slack?.webhookUrl) {
const slackMessager = new SlackWebhookMessager(
'slack',
config.slack.webhookUrl
)
messagers.push(slackMessager)
}
return messagers
}

module.exports = { NotificationService, createNotificationService }
Loading
Loading