@things-factory/notification 8.0.0-beta.9 → 8.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/actions/notification-fcm.ts +148 -0
- package/client/bootstrap.ts +135 -0
- package/client/index.ts +6 -0
- package/client/pages/notification/notification-list-page.ts +258 -0
- package/client/pages/notification-rule/notification-rule-importer.ts +87 -0
- package/client/pages/notification-rule/notification-rule-list-page.ts +386 -0
- package/client/reducers/notification.ts +27 -0
- package/client/route.ts +10 -0
- package/client/tsconfig.json +13 -0
- package/client/viewparts/notification-badge.ts +54 -0
- package/client/viewparts/notification-item.ts +246 -0
- package/client/viewparts/notification-list.ts +160 -0
- package/client/viewparts/notification-sender.ts +142 -0
- package/client/viewparts/notification-setting-let.ts +222 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/docs/images/config-app-1.png +0 -0
- package/docs/images/config-app-2.png +0 -0
- package/docs/images/config-server-key.png +0 -0
- package/docs/images/config-service-account.png +0 -0
- package/docs/images/config-vapidkey-1.png +0 -0
- package/docs/images/config-vapidkey-2-get-public-key.png +0 -0
- package/docs/images/config-vapidkey-3-get-private-key.png +0 -0
- package/docs/images/element-notification-badge.png +0 -0
- package/docs/images/element-notification-list.png +0 -0
- package/docs/images/element-notification-setting-let.png +0 -0
- package/docs/images/push-test-on-chrome-1.png +0 -0
- package/docs/images/push-test-on-chrome-2.png +0 -0
- package/docs/images/push-test-on-firebase-1.png +0 -0
- package/docs/images/push-test-on-firebase-2.png +0 -0
- package/docs/images/push-test-on-firebase-3.png +0 -0
- package/docs/images/push-test-on-firebase-4.png +0 -0
- package/package.json +9 -9
- package/server/controllers/fcm.ts +214 -0
- package/server/controllers/index.ts +1 -0
- package/server/index.ts +5 -0
- package/server/middlewares/index.ts +5 -0
- package/server/middlewares/notification-middleware.ts +73 -0
- package/server/routers/notification-router.ts +67 -0
- package/server/routes.ts +11 -0
- package/server/service/index.ts +42 -0
- package/server/service/notification/directive-notification.ts +71 -0
- package/server/service/notification/index.ts +14 -0
- package/server/service/notification/notification-mutation.ts +119 -0
- package/server/service/notification/notification-query.ts +76 -0
- package/server/service/notification/notification-subscription.ts +44 -0
- package/server/service/notification/notification-type.ts +55 -0
- package/server/service/notification/notification.ts +105 -0
- package/server/service/notification-rule/event-subscriber.ts +20 -0
- package/server/service/notification-rule/index.ts +9 -0
- package/server/service/notification-rule/notification-rule-history.ts +136 -0
- package/server/service/notification-rule/notification-rule-mutation.ts +203 -0
- package/server/service/notification-rule/notification-rule-query.ts +65 -0
- package/server/service/notification-rule/notification-rule-type.ts +71 -0
- package/server/service/notification-rule/notification-rule.ts +125 -0
- package/server/tsconfig.json +9 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/notification",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.2",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@material/web": "^2.0.0",
|
|
31
|
-
"@operato/layout": "^8.0.0
|
|
32
|
-
"@things-factory/auth-base": "^8.0.
|
|
33
|
-
"@things-factory/codelingua": "^8.0.
|
|
34
|
-
"@things-factory/i18n-base": "^8.0.
|
|
35
|
-
"@things-factory/organization": "^8.0.
|
|
36
|
-
"@things-factory/setting-base": "^8.0.
|
|
37
|
-
"@things-factory/shell": "^8.0.
|
|
31
|
+
"@operato/layout": "^8.0.0",
|
|
32
|
+
"@things-factory/auth-base": "^8.0.2",
|
|
33
|
+
"@things-factory/codelingua": "^8.0.2",
|
|
34
|
+
"@things-factory/i18n-base": "^8.0.2",
|
|
35
|
+
"@things-factory/organization": "^8.0.2",
|
|
36
|
+
"@things-factory/setting-base": "^8.0.2",
|
|
37
|
+
"@things-factory/shell": "^8.0.2",
|
|
38
38
|
"clipboard": "^2.0.6",
|
|
39
39
|
"firebase": "^9.14.0",
|
|
40
40
|
"firebase-admin": "^11.3.0",
|
|
41
41
|
"google": "^2.1.0"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "39d60f56e142561233ddf6d47b539c637971357c"
|
|
44
44
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import fetch from 'node-fetch'
|
|
2
|
+
|
|
3
|
+
import { config, logger } from '@things-factory/env'
|
|
4
|
+
|
|
5
|
+
import { initializeApp, cert } from 'firebase-admin/app'
|
|
6
|
+
import { getMessaging } from 'firebase-admin/messaging'
|
|
7
|
+
|
|
8
|
+
const notificationConfig = config.get('notification') || {}
|
|
9
|
+
const { fcm, vapidKey } = notificationConfig
|
|
10
|
+
const { serviceAccount, appConfig, serverKey } = fcm || {}
|
|
11
|
+
const { publicKey } = vapidKey || {}
|
|
12
|
+
|
|
13
|
+
var messaging
|
|
14
|
+
|
|
15
|
+
if (serviceAccount) {
|
|
16
|
+
try {
|
|
17
|
+
const app = initializeApp({
|
|
18
|
+
credential: cert(serviceAccount)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
messaging = getMessaging(app)
|
|
22
|
+
} catch (err) {
|
|
23
|
+
logger.error('incorrect notification configuration')
|
|
24
|
+
logger.error(err)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getConfig() {
|
|
29
|
+
return messaging
|
|
30
|
+
? {
|
|
31
|
+
appConfig,
|
|
32
|
+
vapidPublicKey: publicKey
|
|
33
|
+
}
|
|
34
|
+
: {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* sendNotification
|
|
39
|
+
*
|
|
40
|
+
* @param receiver user.id who will receive notification
|
|
41
|
+
* @param message message object to be sent
|
|
42
|
+
*/
|
|
43
|
+
export async function sendNotification({ receivers, message }) {
|
|
44
|
+
if (!messaging) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
notify({
|
|
49
|
+
receivers,
|
|
50
|
+
...message
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function register(user, { subscription }) {
|
|
55
|
+
if (!messaging || !subscription || !user) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
var notification_key = await getDeviceGroup(user.id)
|
|
61
|
+
|
|
62
|
+
if (!notification_key) {
|
|
63
|
+
await postDeviceGroup({
|
|
64
|
+
operation: 'create',
|
|
65
|
+
notification_key_name: user.id,
|
|
66
|
+
registration_ids: [subscription]
|
|
67
|
+
})
|
|
68
|
+
} else {
|
|
69
|
+
await postDeviceGroup({
|
|
70
|
+
operation: 'add',
|
|
71
|
+
notification_key_name: user.id,
|
|
72
|
+
notification_key,
|
|
73
|
+
registration_ids: [subscription]
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true
|
|
78
|
+
} catch (err) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function unregister(user, { subscription }) {
|
|
84
|
+
if (!messaging || !subscription || !user) {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
var notification_key = await getDeviceGroup(user.id)
|
|
90
|
+
|
|
91
|
+
if (notification_key) {
|
|
92
|
+
await postDeviceGroup({
|
|
93
|
+
operation: 'remove',
|
|
94
|
+
notification_key_name: user.id,
|
|
95
|
+
notification_key,
|
|
96
|
+
registration_ids: [subscription]
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return true
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function notify({ receivers, privileges, tokens, topic, title, body, data, image, actions }) {
|
|
107
|
+
if (!messaging) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Caution: non-string attributes are not allowed in FCM payload validation
|
|
112
|
+
var notification = {
|
|
113
|
+
title: title || '',
|
|
114
|
+
body: body || '',
|
|
115
|
+
image: image || ''
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (tokens && tokens instanceof Array && tokens.length > 0) {
|
|
119
|
+
if (tokens.length > 1) {
|
|
120
|
+
const response = await messaging.sendMulticast({
|
|
121
|
+
notification,
|
|
122
|
+
data,
|
|
123
|
+
tokens
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (response.failureCount > 0) {
|
|
127
|
+
const failedTokens = []
|
|
128
|
+
response.responses.forEach((resp, idx) => {
|
|
129
|
+
if (!resp.success) {
|
|
130
|
+
failedTokens.push(tokens[idx])
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
logger.error('List of tokens that caused failures: ' + failedTokens)
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const token = tokens[0]
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const response = await messaging.send({
|
|
140
|
+
notification,
|
|
141
|
+
data,
|
|
142
|
+
token
|
|
143
|
+
})
|
|
144
|
+
logger.log('Successfully sent message:', response)
|
|
145
|
+
} catch (err) {
|
|
146
|
+
logger.error('Error sending message:', err)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (topic) {
|
|
152
|
+
try {
|
|
153
|
+
const response = await messaging.send({
|
|
154
|
+
notification,
|
|
155
|
+
data,
|
|
156
|
+
topic
|
|
157
|
+
})
|
|
158
|
+
logger.log('Successfully sent message:', response)
|
|
159
|
+
} catch (err) {
|
|
160
|
+
logger.error('Error sending message:', err)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (receivers && receivers instanceof Array) {
|
|
165
|
+
for (let receiver of receivers) {
|
|
166
|
+
const notification_key = await getDeviceGroup(receiver)
|
|
167
|
+
|
|
168
|
+
if (notification_key) {
|
|
169
|
+
await messaging.sendToDeviceGroup(notification_key, {
|
|
170
|
+
notification,
|
|
171
|
+
data
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function postDeviceGroup(body) {
|
|
179
|
+
var response = await fetch('https://fcm.googleapis.com/fcm/notification', {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: {
|
|
182
|
+
'Content-Type': 'application/json',
|
|
183
|
+
Authorization: `key=${serverKey}`,
|
|
184
|
+
project_id: appConfig.messagingSenderId
|
|
185
|
+
},
|
|
186
|
+
body: JSON.stringify(body)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
if (response.ok) {
|
|
190
|
+
const { notification_key } = await response.json()
|
|
191
|
+
return notification_key
|
|
192
|
+
} else {
|
|
193
|
+
console.error('postDeviceGroup-notok', response.status, await response.text())
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function getDeviceGroup(notificationKeyName) {
|
|
198
|
+
const response = await fetch(
|
|
199
|
+
`https://fcm.googleapis.com/fcm/notification?notification_key_name=${notificationKeyName}`,
|
|
200
|
+
{
|
|
201
|
+
method: 'GET',
|
|
202
|
+
headers: {
|
|
203
|
+
'Content-Type': 'application/json',
|
|
204
|
+
Authorization: `key=${serverKey}`,
|
|
205
|
+
project_id: appConfig.messagingSenderId
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
if (response.ok) {
|
|
211
|
+
const { notification_key } = await response.json()
|
|
212
|
+
return notification_key
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './fcm'
|
package/server/index.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { pubsub } from '@things-factory/shell'
|
|
2
|
+
import { logger } from '@things-factory/env'
|
|
3
|
+
|
|
4
|
+
import { notify } from '../controllers/fcm'
|
|
5
|
+
|
|
6
|
+
const debug = require('debug')('things-factory:notification:notification-middleware')
|
|
7
|
+
|
|
8
|
+
function notifier(context) {
|
|
9
|
+
return async function ({
|
|
10
|
+
receivers,
|
|
11
|
+
privileges,
|
|
12
|
+
tokens,
|
|
13
|
+
topic,
|
|
14
|
+
type,
|
|
15
|
+
subject = 'info',
|
|
16
|
+
title,
|
|
17
|
+
body,
|
|
18
|
+
image,
|
|
19
|
+
url,
|
|
20
|
+
actions,
|
|
21
|
+
timestamp = Date.now(),
|
|
22
|
+
mode = 'background'
|
|
23
|
+
}) {
|
|
24
|
+
const { domain, user } = context.state
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
if (mode === 'background') {
|
|
28
|
+
/* for send webpush notification message */
|
|
29
|
+
|
|
30
|
+
await notify({
|
|
31
|
+
receivers,
|
|
32
|
+
privileges,
|
|
33
|
+
tokens,
|
|
34
|
+
topic,
|
|
35
|
+
title,
|
|
36
|
+
body,
|
|
37
|
+
data: {
|
|
38
|
+
type,
|
|
39
|
+
url,
|
|
40
|
+
timestamp: String(timestamp)
|
|
41
|
+
},
|
|
42
|
+
image,
|
|
43
|
+
actions
|
|
44
|
+
})
|
|
45
|
+
} else {
|
|
46
|
+
/* for send publish notification to clients */
|
|
47
|
+
// TODO CONFIRM data format
|
|
48
|
+
await pubsub.publish('notification', {
|
|
49
|
+
notification: {
|
|
50
|
+
domain,
|
|
51
|
+
subject,
|
|
52
|
+
type,
|
|
53
|
+
title,
|
|
54
|
+
body,
|
|
55
|
+
image,
|
|
56
|
+
url,
|
|
57
|
+
timestamp
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
logger.error(err)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function notificationMiddleware(context: any, next: any) {
|
|
68
|
+
if (!context.state.notify) {
|
|
69
|
+
context.state.notify = notifier(context)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await next()
|
|
73
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Router from 'koa-router'
|
|
2
|
+
import { ILike } from 'typeorm'
|
|
3
|
+
|
|
4
|
+
import { User } from '@things-factory/auth-base'
|
|
5
|
+
import { getRepository } from '@things-factory/shell'
|
|
6
|
+
|
|
7
|
+
import { getConfig, register, unregister } from '../controllers/fcm'
|
|
8
|
+
|
|
9
|
+
const debug = require('debug')('things-factory:notification:notification-router')
|
|
10
|
+
|
|
11
|
+
export const notificationRouter = new Router({
|
|
12
|
+
prefix: '/notification'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
notificationRouter.get('/config', async (context, next) => {
|
|
16
|
+
const config = getConfig()
|
|
17
|
+
|
|
18
|
+
context.type = 'application/json'
|
|
19
|
+
context.body = config
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
notificationRouter.post('/register', async (context, next) => {
|
|
23
|
+
const { user } = context.state
|
|
24
|
+
const { body } = context.request
|
|
25
|
+
context.body = await register(user, body)
|
|
26
|
+
context.status = 200
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
notificationRouter.post('/unregister', async (context, next) => {
|
|
30
|
+
const { user } = context.state
|
|
31
|
+
const { body } = context.request
|
|
32
|
+
context.body = await unregister(user, body)
|
|
33
|
+
context.status = 200
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
notificationRouter.post('/notify', async (context, next) => {
|
|
37
|
+
const { user, domain, notify } = context.state
|
|
38
|
+
const repository = getRepository(User)
|
|
39
|
+
|
|
40
|
+
var { receivers, ...options } = context.request.body
|
|
41
|
+
|
|
42
|
+
debug('post:/notify', receivers, receivers.split(','), options)
|
|
43
|
+
|
|
44
|
+
// TODO filter only users having current domain privilege
|
|
45
|
+
receivers = (
|
|
46
|
+
await Promise.all(
|
|
47
|
+
receivers
|
|
48
|
+
.split(',')
|
|
49
|
+
.map(email => email.trim())
|
|
50
|
+
.map(async email => {
|
|
51
|
+
var receiver: User = await repository.findOneBy({ email: ILike(email) })
|
|
52
|
+
return receiver && receiver.id
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
).filter(receiver => !!receiver)
|
|
56
|
+
|
|
57
|
+
debug('post:/notify', receivers, user.id)
|
|
58
|
+
|
|
59
|
+
await notify({
|
|
60
|
+
receivers: receivers.length > 0 ? receivers : [user.id],
|
|
61
|
+
...options
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
context.body = {
|
|
65
|
+
success: true
|
|
66
|
+
}
|
|
67
|
+
})
|
package/server/routes.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { notificationRouter } from './routers/notification-router'
|
|
2
|
+
|
|
3
|
+
process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {})
|
|
4
|
+
|
|
5
|
+
process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {})
|
|
6
|
+
|
|
7
|
+
process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {})
|
|
8
|
+
|
|
9
|
+
process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
|
|
10
|
+
domainPrivateRouter.use('', notificationRouter.routes(), notificationRouter.allowedMethods())
|
|
11
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* EXPORT ENTITY TYPES */
|
|
2
|
+
export * from './notification-rule/notification-rule'
|
|
3
|
+
export * from './notification/notification'
|
|
4
|
+
|
|
5
|
+
/* IMPORT ENTITIES AND RESOLVERS */
|
|
6
|
+
import {
|
|
7
|
+
entities as NotificationRuleEntities,
|
|
8
|
+
resolvers as NotificationRuleResolvers,
|
|
9
|
+
subscribers as NotificationRuleSubscribers
|
|
10
|
+
} from './notification-rule'
|
|
11
|
+
import {
|
|
12
|
+
entities as NotificationEntities,
|
|
13
|
+
typeDefs as NotificationTypeDefs,
|
|
14
|
+
resolvers as NotificationResolvers,
|
|
15
|
+
directives as NotificationDirectives
|
|
16
|
+
} from './notification'
|
|
17
|
+
|
|
18
|
+
export const entities = [
|
|
19
|
+
/* ENTITIES */
|
|
20
|
+
...NotificationRuleEntities,
|
|
21
|
+
...NotificationEntities
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
export const subscribers = [
|
|
25
|
+
/* SUBSCRIBERS */
|
|
26
|
+
...NotificationRuleSubscribers
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
export const schema = {
|
|
30
|
+
typeDefs: {
|
|
31
|
+
...NotificationTypeDefs
|
|
32
|
+
},
|
|
33
|
+
resolverClasses: [
|
|
34
|
+
/* RESOLVER CLASSES */
|
|
35
|
+
...NotificationRuleResolvers,
|
|
36
|
+
...NotificationResolvers,
|
|
37
|
+
...NotificationResolvers
|
|
38
|
+
],
|
|
39
|
+
directives: {
|
|
40
|
+
...NotificationDirectives
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { defaultFieldResolver, GraphQLSchema } from 'graphql'
|
|
2
|
+
import gql from 'graphql-tag'
|
|
3
|
+
|
|
4
|
+
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'
|
|
5
|
+
|
|
6
|
+
const debug = require('debug')('things-factory:notification:directive-notification')
|
|
7
|
+
|
|
8
|
+
const DIRECTIVE = 'notification'
|
|
9
|
+
|
|
10
|
+
export const notificationDirectiveTypeDefs = gql`
|
|
11
|
+
directive @notification(
|
|
12
|
+
receivers: String
|
|
13
|
+
topic: String
|
|
14
|
+
subject: String # info
|
|
15
|
+
title: String
|
|
16
|
+
body: String
|
|
17
|
+
image: String
|
|
18
|
+
url: String
|
|
19
|
+
# timestamp = String(Date.now())
|
|
20
|
+
mode: String # background
|
|
21
|
+
) on FIELD_DEFINITION
|
|
22
|
+
`
|
|
23
|
+
export const directiveNotification = (schema: GraphQLSchema) =>
|
|
24
|
+
mapSchema(schema, {
|
|
25
|
+
[MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName, schema) => {
|
|
26
|
+
const notificationDirective = getDirective(schema, fieldConfig, DIRECTIVE)?.[0]
|
|
27
|
+
if (notificationDirective) {
|
|
28
|
+
const { resolve = defaultFieldResolver, args } = fieldConfig
|
|
29
|
+
|
|
30
|
+
if (!args) {
|
|
31
|
+
throw new Error(`Unexpected Error. args should be defined in @notification directive for field ${fieldName}.`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
receivers,
|
|
36
|
+
topic,
|
|
37
|
+
subject = 'info',
|
|
38
|
+
title,
|
|
39
|
+
body,
|
|
40
|
+
image,
|
|
41
|
+
url,
|
|
42
|
+
mode = 'background'
|
|
43
|
+
} = notificationDirective
|
|
44
|
+
const timestamp = Date.now()
|
|
45
|
+
|
|
46
|
+
fieldConfig.resolve = async function (source, args, context, info) {
|
|
47
|
+
const result = await resolve.call(this, source, args, context, info)
|
|
48
|
+
|
|
49
|
+
const { domain, user, notify } = context.state
|
|
50
|
+
|
|
51
|
+
if (!notify) {
|
|
52
|
+
debug('notify not set')
|
|
53
|
+
} else {
|
|
54
|
+
try {
|
|
55
|
+
await notify({
|
|
56
|
+
receivers: [user.id],
|
|
57
|
+
notificationDirective,
|
|
58
|
+
timestamp: new Date()
|
|
59
|
+
})
|
|
60
|
+
} catch (err) {
|
|
61
|
+
debug(err)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return fieldConfig
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Notification } from './notification'
|
|
2
|
+
import { NotificationQuery } from './notification-query'
|
|
3
|
+
import { NotificationMutation } from './notification-mutation'
|
|
4
|
+
import { NotificationSubscription } from './notification-subscription'
|
|
5
|
+
import { notificationDirectiveTypeDefs, directiveNotification } from './directive-notification'
|
|
6
|
+
|
|
7
|
+
export const typeDefs = {
|
|
8
|
+
notificationDirectiveTypeDefs
|
|
9
|
+
}
|
|
10
|
+
export const entities = [Notification]
|
|
11
|
+
export const resolvers = [NotificationQuery, NotificationMutation, NotificationSubscription]
|
|
12
|
+
export const directives = {
|
|
13
|
+
notification: directiveNotification
|
|
14
|
+
}
|