aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/extra-utils/users
diff options
context:
space:
mode:
Diffstat (limited to 'shared/extra-utils/users')
-rw-r--r--shared/extra-utils/users/accounts-command.ts56
-rw-r--r--shared/extra-utils/users/accounts.ts87
-rw-r--r--shared/extra-utils/users/actors.ts73
-rw-r--r--shared/extra-utils/users/blocklist-command.ts139
-rw-r--r--shared/extra-utils/users/blocklist.ts238
-rw-r--r--shared/extra-utils/users/index.ts9
-rw-r--r--shared/extra-utils/users/login-command.ts132
-rw-r--r--shared/extra-utils/users/login.ts124
-rw-r--r--shared/extra-utils/users/notifications-command.ts86
-rw-r--r--shared/extra-utils/users/notifications.ts (renamed from shared/extra-utils/users/user-notifications.ts)628
-rw-r--r--shared/extra-utils/users/subscriptions-command.ts99
-rw-r--r--shared/extra-utils/users/user-subscriptions.ts93
-rw-r--r--shared/extra-utils/users/users-command.ts415
-rw-r--r--shared/extra-utils/users/users.ts415
14 files changed, 1321 insertions, 1273 deletions
diff --git a/shared/extra-utils/users/accounts-command.ts b/shared/extra-utils/users/accounts-command.ts
new file mode 100644
index 000000000..2f586104e
--- /dev/null
+++ b/shared/extra-utils/users/accounts-command.ts
@@ -0,0 +1,56 @@
1import { HttpStatusCode, ResultList } from '@shared/models'
2import { Account } from '../../models/actors'
3import { AccountVideoRate, VideoRateType } from '../../models/videos'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class AccountsCommand extends AbstractCommand {
7
8 list (options: OverrideCommandOptions & {
9 sort?: string // default -createdAt
10 } = {}) {
11 const { sort = '-createdAt' } = options
12 const path = '/api/v1/accounts'
13
14 return this.getRequestBody<ResultList<Account>>({
15 ...options,
16
17 path,
18 query: { sort },
19 implicitToken: false,
20 defaultExpectedStatus: HttpStatusCode.OK_200
21 })
22 }
23
24 get (options: OverrideCommandOptions & {
25 accountName: string
26 }) {
27 const path = '/api/v1/accounts/' + options.accountName
28
29 return this.getRequestBody<Account>({
30 ...options,
31
32 path,
33 implicitToken: false,
34 defaultExpectedStatus: HttpStatusCode.OK_200
35 })
36 }
37
38 listRatings (options: OverrideCommandOptions & {
39 accountName: string
40 rating?: VideoRateType
41 }) {
42 const { rating, accountName } = options
43 const path = '/api/v1/accounts/' + accountName + '/ratings'
44
45 const query = { rating }
46
47 return this.getRequestBody<ResultList<AccountVideoRate>>({
48 ...options,
49
50 path,
51 query,
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56}
diff --git a/shared/extra-utils/users/accounts.ts b/shared/extra-utils/users/accounts.ts
deleted file mode 100644
index 4ea7f1402..000000000
--- a/shared/extra-utils/users/accounts.ts
+++ /dev/null
@@ -1,87 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as request from 'supertest'
4import { expect } from 'chai'
5import { existsSync, readdir } from 'fs-extra'
6import { join } from 'path'
7import { Account } from '../../models/actors'
8import { root } from '../miscs/miscs'
9import { makeGetRequest } from '../requests/requests'
10import { VideoRateType } from '../../models/videos'
11import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
12
13function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = HttpStatusCode.OK_200) {
14 const path = '/api/v1/accounts'
15
16 return makeGetRequest({
17 url,
18 query: { sort },
19 path,
20 statusCodeExpected
21 })
22}
23
24function getAccount (url: string, accountName: string, statusCodeExpected = HttpStatusCode.OK_200) {
25 const path = '/api/v1/accounts/' + accountName
26
27 return makeGetRequest({
28 url,
29 path,
30 statusCodeExpected
31 })
32}
33
34async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) {
35 const res = await getAccountsList(url)
36 const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain)
37
38 const message = `${nameWithDomain} on ${url}`
39 expect(account.followersCount).to.equal(followersCount, message)
40 expect(account.followingCount).to.equal(followingCount, message)
41}
42
43async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
44 const testDirectory = 'test' + serverNumber
45
46 for (const directory of [ 'avatars' ]) {
47 const directoryPath = join(root(), testDirectory, directory)
48
49 const directoryExists = existsSync(directoryPath)
50 expect(directoryExists).to.be.true
51
52 const files = await readdir(directoryPath)
53 for (const file of files) {
54 expect(file).to.not.contain(filename)
55 }
56 }
57}
58
59function getAccountRatings (
60 url: string,
61 accountName: string,
62 accessToken: string,
63 rating?: VideoRateType,
64 statusCodeExpected = HttpStatusCode.OK_200
65) {
66 const path = '/api/v1/accounts/' + accountName + '/ratings'
67
68 const query = rating ? { rating } : {}
69
70 return request(url)
71 .get(path)
72 .query(query)
73 .set('Accept', 'application/json')
74 .set('Authorization', 'Bearer ' + accessToken)
75 .expect(statusCodeExpected)
76 .expect('Content-Type', /json/)
77}
78
79// ---------------------------------------------------------------------------
80
81export {
82 getAccount,
83 expectAccountFollows,
84 getAccountsList,
85 checkActorFilesWereRemoved,
86 getAccountRatings
87}
diff --git a/shared/extra-utils/users/actors.ts b/shared/extra-utils/users/actors.ts
new file mode 100644
index 000000000..cfcc7d0a7
--- /dev/null
+++ b/shared/extra-utils/users/actors.ts
@@ -0,0 +1,73 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { Account, VideoChannel } from '@shared/models'
8import { PeerTubeServer } from '../server'
9
10async function expectChannelsFollows (options: {
11 server: PeerTubeServer
12 handle: string
13 followers: number
14 following: number
15}) {
16 const { server } = options
17 const { data } = await server.channels.list()
18
19 return expectActorFollow({ ...options, data })
20}
21
22async function expectAccountFollows (options: {
23 server: PeerTubeServer
24 handle: string
25 followers: number
26 following: number
27}) {
28 const { server } = options
29 const { data } = await server.accounts.list()
30
31 return expectActorFollow({ ...options, data })
32}
33
34async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
35 const testDirectory = 'test' + serverNumber
36
37 for (const directory of [ 'avatars' ]) {
38 const directoryPath = join(root(), testDirectory, directory)
39
40 const directoryExists = await pathExists(directoryPath)
41 expect(directoryExists).to.be.true
42
43 const files = await readdir(directoryPath)
44 for (const file of files) {
45 expect(file).to.not.contain(filename)
46 }
47 }
48}
49
50export {
51 expectAccountFollows,
52 expectChannelsFollows,
53 checkActorFilesWereRemoved
54}
55
56// ---------------------------------------------------------------------------
57
58function expectActorFollow (options: {
59 server: PeerTubeServer
60 data: (Account | VideoChannel)[]
61 handle: string
62 followers: number
63 following: number
64}) {
65 const { server, data, handle, followers, following } = options
66
67 const actor = data.find(a => a.name + '@' + a.host === handle)
68 const message = `${handle} on ${server.url}`
69
70 expect(actor, message).to.exist
71 expect(actor.followersCount).to.equal(followers, message)
72 expect(actor.followingCount).to.equal(following, message)
73}
diff --git a/shared/extra-utils/users/blocklist-command.ts b/shared/extra-utils/users/blocklist-command.ts
new file mode 100644
index 000000000..14491a1ae
--- /dev/null
+++ b/shared/extra-utils/users/blocklist-command.ts
@@ -0,0 +1,139 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { AccountBlock, HttpStatusCode, ResultList, ServerBlock } from '@shared/models'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6type ListBlocklistOptions = OverrideCommandOptions & {
7 start: number
8 count: number
9 sort: string // default -createdAt
10}
11
12export class BlocklistCommand extends AbstractCommand {
13
14 listMyAccountBlocklist (options: ListBlocklistOptions) {
15 const path = '/api/v1/users/me/blocklist/accounts'
16
17 return this.listBlocklist<AccountBlock>(options, path)
18 }
19
20 listMyServerBlocklist (options: ListBlocklistOptions) {
21 const path = '/api/v1/users/me/blocklist/servers'
22
23 return this.listBlocklist<ServerBlock>(options, path)
24 }
25
26 listServerAccountBlocklist (options: ListBlocklistOptions) {
27 const path = '/api/v1/server/blocklist/accounts'
28
29 return this.listBlocklist<AccountBlock>(options, path)
30 }
31
32 listServerServerBlocklist (options: ListBlocklistOptions) {
33 const path = '/api/v1/server/blocklist/servers'
34
35 return this.listBlocklist<ServerBlock>(options, path)
36 }
37
38 // ---------------------------------------------------------------------------
39
40 addToMyBlocklist (options: OverrideCommandOptions & {
41 account?: string
42 server?: string
43 }) {
44 const { account, server } = options
45
46 const path = account
47 ? '/api/v1/users/me/blocklist/accounts'
48 : '/api/v1/users/me/blocklist/servers'
49
50 return this.postBodyRequest({
51 ...options,
52
53 path,
54 fields: {
55 accountName: account,
56 host: server
57 },
58 implicitToken: true,
59 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
60 })
61 }
62
63 addToServerBlocklist (options: OverrideCommandOptions & {
64 account?: string
65 server?: string
66 }) {
67 const { account, server } = options
68
69 const path = account
70 ? '/api/v1/server/blocklist/accounts'
71 : '/api/v1/server/blocklist/servers'
72
73 return this.postBodyRequest({
74 ...options,
75
76 path,
77 fields: {
78 accountName: account,
79 host: server
80 },
81 implicitToken: true,
82 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
83 })
84 }
85
86 // ---------------------------------------------------------------------------
87
88 removeFromMyBlocklist (options: OverrideCommandOptions & {
89 account?: string
90 server?: string
91 }) {
92 const { account, server } = options
93
94 const path = account
95 ? '/api/v1/users/me/blocklist/accounts/' + account
96 : '/api/v1/users/me/blocklist/servers/' + server
97
98 return this.deleteRequest({
99 ...options,
100
101 path,
102 implicitToken: true,
103 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
104 })
105 }
106
107 removeFromServerBlocklist (options: OverrideCommandOptions & {
108 account?: string
109 server?: string
110 }) {
111 const { account, server } = options
112
113 const path = account
114 ? '/api/v1/server/blocklist/accounts/' + account
115 : '/api/v1/server/blocklist/servers/' + server
116
117 return this.deleteRequest({
118 ...options,
119
120 path,
121 implicitToken: true,
122 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
123 })
124 }
125
126 private listBlocklist <T> (options: ListBlocklistOptions, path: string) {
127 const { start, count, sort = '-createdAt' } = options
128
129 return this.getRequestBody<ResultList<T>>({
130 ...options,
131
132 path,
133 query: { start, count, sort },
134 implicitToken: true,
135 defaultExpectedStatus: HttpStatusCode.OK_200
136 })
137 }
138
139}
diff --git a/shared/extra-utils/users/blocklist.ts b/shared/extra-utils/users/blocklist.ts
deleted file mode 100644
index bdf7ee58a..000000000
--- a/shared/extra-utils/users/blocklist.ts
+++ /dev/null
@@ -1,238 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { makeGetRequest, makeDeleteRequest, makePostBodyRequest } from '../requests/requests'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function getAccountBlocklistByAccount (
7 url: string,
8 token: string,
9 start: number,
10 count: number,
11 sort = '-createdAt',
12 statusCodeExpected = HttpStatusCode.OK_200
13) {
14 const path = '/api/v1/users/me/blocklist/accounts'
15
16 return makeGetRequest({
17 url,
18 token,
19 query: { start, count, sort },
20 path,
21 statusCodeExpected
22 })
23}
24
25function addAccountToAccountBlocklist (
26 url: string,
27 token: string,
28 accountToBlock: string,
29 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
30) {
31 const path = '/api/v1/users/me/blocklist/accounts'
32
33 return makePostBodyRequest({
34 url,
35 path,
36 token,
37 fields: {
38 accountName: accountToBlock
39 },
40 statusCodeExpected
41 })
42}
43
44function removeAccountFromAccountBlocklist (
45 url: string,
46 token: string,
47 accountToUnblock: string,
48 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
49) {
50 const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
51
52 return makeDeleteRequest({
53 url,
54 path,
55 token,
56 statusCodeExpected
57 })
58}
59
60function getServerBlocklistByAccount (
61 url: string,
62 token: string,
63 start: number,
64 count: number,
65 sort = '-createdAt',
66 statusCodeExpected = HttpStatusCode.OK_200
67) {
68 const path = '/api/v1/users/me/blocklist/servers'
69
70 return makeGetRequest({
71 url,
72 token,
73 query: { start, count, sort },
74 path,
75 statusCodeExpected
76 })
77}
78
79function addServerToAccountBlocklist (
80 url: string,
81 token: string,
82 serverToBlock: string,
83 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
84) {
85 const path = '/api/v1/users/me/blocklist/servers'
86
87 return makePostBodyRequest({
88 url,
89 path,
90 token,
91 fields: {
92 host: serverToBlock
93 },
94 statusCodeExpected
95 })
96}
97
98function removeServerFromAccountBlocklist (
99 url: string,
100 token: string,
101 serverToBlock: string,
102 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
103) {
104 const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
105
106 return makeDeleteRequest({
107 url,
108 path,
109 token,
110 statusCodeExpected
111 })
112}
113
114function getAccountBlocklistByServer (
115 url: string,
116 token: string,
117 start: number,
118 count: number,
119 sort = '-createdAt',
120 statusCodeExpected = HttpStatusCode.OK_200
121) {
122 const path = '/api/v1/server/blocklist/accounts'
123
124 return makeGetRequest({
125 url,
126 token,
127 query: { start, count, sort },
128 path,
129 statusCodeExpected
130 })
131}
132
133function addAccountToServerBlocklist (
134 url: string,
135 token: string,
136 accountToBlock: string,
137 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
138) {
139 const path = '/api/v1/server/blocklist/accounts'
140
141 return makePostBodyRequest({
142 url,
143 path,
144 token,
145 fields: {
146 accountName: accountToBlock
147 },
148 statusCodeExpected
149 })
150}
151
152function removeAccountFromServerBlocklist (
153 url: string,
154 token: string,
155 accountToUnblock: string,
156 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
157) {
158 const path = '/api/v1/server/blocklist/accounts/' + accountToUnblock
159
160 return makeDeleteRequest({
161 url,
162 path,
163 token,
164 statusCodeExpected
165 })
166}
167
168function getServerBlocklistByServer (
169 url: string,
170 token: string,
171 start: number,
172 count: number,
173 sort = '-createdAt',
174 statusCodeExpected = HttpStatusCode.OK_200
175) {
176 const path = '/api/v1/server/blocklist/servers'
177
178 return makeGetRequest({
179 url,
180 token,
181 query: { start, count, sort },
182 path,
183 statusCodeExpected
184 })
185}
186
187function addServerToServerBlocklist (
188 url: string,
189 token: string,
190 serverToBlock: string,
191 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
192) {
193 const path = '/api/v1/server/blocklist/servers'
194
195 return makePostBodyRequest({
196 url,
197 path,
198 token,
199 fields: {
200 host: serverToBlock
201 },
202 statusCodeExpected
203 })
204}
205
206function removeServerFromServerBlocklist (
207 url: string,
208 token: string,
209 serverToBlock: string,
210 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
211) {
212 const path = '/api/v1/server/blocklist/servers/' + serverToBlock
213
214 return makeDeleteRequest({
215 url,
216 path,
217 token,
218 statusCodeExpected
219 })
220}
221
222// ---------------------------------------------------------------------------
223
224export {
225 getAccountBlocklistByAccount,
226 addAccountToAccountBlocklist,
227 removeAccountFromAccountBlocklist,
228 getServerBlocklistByAccount,
229 addServerToAccountBlocklist,
230 removeServerFromAccountBlocklist,
231
232 getAccountBlocklistByServer,
233 addAccountToServerBlocklist,
234 removeAccountFromServerBlocklist,
235 getServerBlocklistByServer,
236 addServerToServerBlocklist,
237 removeServerFromServerBlocklist
238}
diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts
new file mode 100644
index 000000000..460a06f70
--- /dev/null
+++ b/shared/extra-utils/users/index.ts
@@ -0,0 +1,9 @@
1export * from './accounts-command'
2export * from './actors'
3export * from './blocklist-command'
4export * from './login'
5export * from './login-command'
6export * from './notifications'
7export * from './notifications-command'
8export * from './subscriptions-command'
9export * from './users-command'
diff --git a/shared/extra-utils/users/login-command.ts b/shared/extra-utils/users/login-command.ts
new file mode 100644
index 000000000..143f72a59
--- /dev/null
+++ b/shared/extra-utils/users/login-command.ts
@@ -0,0 +1,132 @@
1import { HttpStatusCode, PeerTubeProblemDocument } from '@shared/models'
2import { unwrapBody } from '../requests'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class LoginCommand extends AbstractCommand {
6
7 login (options: OverrideCommandOptions & {
8 client?: { id?: string, secret?: string }
9 user?: { username: string, password?: string }
10 } = {}) {
11 const { client = this.server.store.client, user = this.server.store.user } = options
12 const path = '/api/v1/users/token'
13
14 const body = {
15 client_id: client.id,
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password ?? 'password',
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23
24 return unwrapBody<{ access_token: string, refresh_token: string } & PeerTubeProblemDocument>(this.postBodyRequest({
25 ...options,
26
27 path,
28 requestType: 'form',
29 fields: body,
30 implicitToken: false,
31 defaultExpectedStatus: HttpStatusCode.OK_200
32 }))
33 }
34
35 getAccessToken (arg1?: { username: string, password?: string }): Promise<string>
36 getAccessToken (arg1: string, password?: string): Promise<string>
37 async getAccessToken (arg1?: { username: string, password?: string } | string, password?: string) {
38 let user: { username: string, password?: string }
39
40 if (!arg1) user = this.server.store.user
41 else if (typeof arg1 === 'object') user = arg1
42 else user = { username: arg1, password }
43
44 try {
45 const body = await this.login({ user })
46
47 return body.access_token
48 } catch (err) {
49 throw new Error(`Cannot authenticate. Please check your username/password. (${err})`)
50 }
51 }
52
53 loginUsingExternalToken (options: OverrideCommandOptions & {
54 username: string
55 externalAuthToken: string
56 }) {
57 const { username, externalAuthToken } = options
58 const path = '/api/v1/users/token'
59
60 const body = {
61 client_id: this.server.store.client.id,
62 client_secret: this.server.store.client.secret,
63 username: username,
64 response_type: 'code',
65 grant_type: 'password',
66 scope: 'upload',
67 externalAuthToken
68 }
69
70 return this.postBodyRequest({
71 ...options,
72
73 path,
74 requestType: 'form',
75 fields: body,
76 implicitToken: false,
77 defaultExpectedStatus: HttpStatusCode.OK_200
78 })
79 }
80
81 logout (options: OverrideCommandOptions & {
82 token: string
83 }) {
84 const path = '/api/v1/users/revoke-token'
85
86 return unwrapBody<{ redirectUrl: string }>(this.postBodyRequest({
87 ...options,
88
89 path,
90 requestType: 'form',
91 implicitToken: false,
92 defaultExpectedStatus: HttpStatusCode.OK_200
93 }))
94 }
95
96 refreshToken (options: OverrideCommandOptions & {
97 refreshToken: string
98 }) {
99 const path = '/api/v1/users/token'
100
101 const body = {
102 client_id: this.server.store.client.id,
103 client_secret: this.server.store.client.secret,
104 refresh_token: options.refreshToken,
105 response_type: 'code',
106 grant_type: 'refresh_token'
107 }
108
109 return this.postBodyRequest({
110 ...options,
111
112 path,
113 requestType: 'form',
114 fields: body,
115 implicitToken: false,
116 defaultExpectedStatus: HttpStatusCode.OK_200
117 })
118 }
119
120 getClient (options: OverrideCommandOptions = {}) {
121 const path = '/api/v1/oauth-clients/local'
122
123 return this.getRequestBody<{ client_id: string, client_secret: string }>({
124 ...options,
125
126 path,
127 host: this.server.host,
128 implicitToken: false,
129 defaultExpectedStatus: HttpStatusCode.OK_200
130 })
131 }
132}
diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts
index 39e1a2747..f1df027d3 100644
--- a/shared/extra-utils/users/login.ts
+++ b/shared/extra-utils/users/login.ts
@@ -1,133 +1,19 @@
1import * as request from 'supertest' 1import { PeerTubeServer } from '../server/server'
2 2
3import { ServerInfo } from '../server/servers' 3function setAccessTokensToServers (servers: PeerTubeServer[]) {
4import { getClient } from '../server/clients'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7type Client = { id: string, secret: string }
8type User = { username: string, password: string }
9type Server = { url: string, client: Client, user: User }
10
11function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) {
12 const path = '/api/v1/users/token'
13
14 const body = {
15 client_id: client.id,
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password,
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23
24 return request(url)
25 .post(path)
26 .type('form')
27 .send(body)
28 .expect(expectedStatus)
29}
30
31function logout (url: string, token: string, expectedStatus = HttpStatusCode.OK_200) {
32 const path = '/api/v1/users/revoke-token'
33
34 return request(url)
35 .post(path)
36 .set('Authorization', 'Bearer ' + token)
37 .type('form')
38 .expect(expectedStatus)
39}
40
41async function serverLogin (server: Server) {
42 const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200)
43
44 return res.body.access_token as string
45}
46
47function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus = HttpStatusCode.OK_200) {
48 const path = '/api/v1/users/token'
49
50 const body = {
51 client_id: server.client.id,
52 client_secret: server.client.secret,
53 refresh_token: refreshToken,
54 response_type: 'code',
55 grant_type: 'refresh_token'
56 }
57
58 return request(server.url)
59 .post(path)
60 .type('form')
61 .send(body)
62 .expect(expectedStatus)
63}
64
65async function userLogin (server: Server, user: User, expectedStatus = HttpStatusCode.OK_200) {
66 const res = await login(server.url, server.client, user, expectedStatus)
67
68 return res.body.access_token as string
69}
70
71async function getAccessToken (url: string, username: string, password: string) {
72 const resClient = await getClient(url)
73 const client = {
74 id: resClient.body.client_id,
75 secret: resClient.body.client_secret
76 }
77
78 const user = { username, password }
79
80 try {
81 const res = await login(url, client, user)
82 return res.body.access_token
83 } catch (err) {
84 throw new Error('Cannot authenticate. Please check your username/password.')
85 }
86}
87
88function setAccessTokensToServers (servers: ServerInfo[]) {
89 const tasks: Promise<any>[] = [] 4 const tasks: Promise<any>[] = []
90 5
91 for (const server of servers) { 6 for (const server of servers) {
92 const p = serverLogin(server).then(t => { server.accessToken = t }) 7 const p = server.login.getAccessToken()
8 .then(t => { server.accessToken = t })
93 tasks.push(p) 9 tasks.push(p)
94 } 10 }
95 11
96 return Promise.all(tasks) 12 return Promise.all(tasks)
97} 13}
98 14
99function loginUsingExternalToken (server: Server, username: string, externalAuthToken: string, expectedStatus = HttpStatusCode.OK_200) {
100 const path = '/api/v1/users/token'
101
102 const body = {
103 client_id: server.client.id,
104 client_secret: server.client.secret,
105 username: username,
106 response_type: 'code',
107 grant_type: 'password',
108 scope: 'upload',
109 externalAuthToken
110 }
111
112 return request(server.url)
113 .post(path)
114 .type('form')
115 .send(body)
116 .expect(expectedStatus)
117}
118
119// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
120 16
121export { 17export {
122 login, 18 setAccessTokensToServers
123 logout,
124 serverLogin,
125 refreshToken,
126 userLogin,
127 getAccessToken,
128 setAccessTokensToServers,
129 Server,
130 Client,
131 User,
132 loginUsingExternalToken
133} 19}
diff --git a/shared/extra-utils/users/notifications-command.ts b/shared/extra-utils/users/notifications-command.ts
new file mode 100644
index 000000000..2d79a3747
--- /dev/null
+++ b/shared/extra-utils/users/notifications-command.ts
@@ -0,0 +1,86 @@
1import { HttpStatusCode, ResultList } from '@shared/models'
2import { UserNotification, UserNotificationSetting } from '../../models/users'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class NotificationsCommand extends AbstractCommand {
6
7 updateMySettings (options: OverrideCommandOptions & {
8 settings: UserNotificationSetting
9 }) {
10 const path = '/api/v1/users/me/notification-settings'
11
12 return this.putBodyRequest({
13 ...options,
14
15 path,
16 fields: options.settings,
17 implicitToken: true,
18 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
19 })
20 }
21
22 list (options: OverrideCommandOptions & {
23 start?: number
24 count?: number
25 unread?: boolean
26 sort?: string
27 }) {
28 const { start, count, unread, sort = '-createdAt' } = options
29 const path = '/api/v1/users/me/notifications'
30
31 return this.getRequestBody<ResultList<UserNotification>>({
32 ...options,
33
34 path,
35 query: {
36 start,
37 count,
38 sort,
39 unread
40 },
41 implicitToken: true,
42 defaultExpectedStatus: HttpStatusCode.OK_200
43 })
44 }
45
46 markAsRead (options: OverrideCommandOptions & {
47 ids: number[]
48 }) {
49 const { ids } = options
50 const path = '/api/v1/users/me/notifications/read'
51
52 return this.postBodyRequest({
53 ...options,
54
55 path,
56 fields: { ids },
57 implicitToken: true,
58 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
59 })
60 }
61
62 markAsReadAll (options: OverrideCommandOptions) {
63 const path = '/api/v1/users/me/notifications/read-all'
64
65 return this.postBodyRequest({
66 ...options,
67
68 path,
69 implicitToken: true,
70 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
71 })
72 }
73
74 async getLastest (options: OverrideCommandOptions = {}) {
75 const { total, data } = await this.list({
76 ...options,
77 start: 0,
78 count: 1,
79 sort: '-createdAt'
80 })
81
82 if (total === 0) return undefined
83
84 return data[0]
85 }
86}
diff --git a/shared/extra-utils/users/user-notifications.ts b/shared/extra-utils/users/notifications.ts
index 844f4442d..7db4bfd3f 100644
--- a/shared/extra-utils/users/user-notifications.ts
+++ b/shared/extra-utils/users/notifications.ts
@@ -3,91 +3,15 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { inspect } from 'util' 4import { inspect } from 'util'
5import { AbuseState, PluginType } from '@shared/models' 5import { AbuseState, PluginType } from '@shared/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users' 6import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users'
8import { MockSmtpServer } from '../miscs/email' 7import { MockSmtpServer } from '../mock-servers/mock-email'
9import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' 8import { PeerTubeServer } from '../server'
10import { doubleFollow } from '../server/follows' 9import { doubleFollow } from '../server/follows'
11import { flushAndRunMultipleServers, ServerInfo } from '../server/servers' 10import { createMultipleServers } from '../server/servers'
12import { getUserNotificationSocket } from '../socket/socket-io' 11import { setAccessTokensToServers } from './login'
13import { setAccessTokensToServers, userLogin } from './login'
14import { createUser, getMyUserInformation } from './users'
15
16function updateMyNotificationSettings (
17 url: string,
18 token: string,
19 settings: UserNotificationSetting,
20 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
21) {
22 const path = '/api/v1/users/me/notification-settings'
23
24 return makePutBodyRequest({
25 url,
26 path,
27 token,
28 fields: settings,
29 statusCodeExpected
30 })
31}
32
33async function getUserNotifications (
34 url: string,
35 token: string,
36 start: number,
37 count: number,
38 unread?: boolean,
39 sort = '-createdAt',
40 statusCodeExpected = HttpStatusCode.OK_200
41) {
42 const path = '/api/v1/users/me/notifications'
43
44 return makeGetRequest({
45 url,
46 path,
47 token,
48 query: {
49 start,
50 count,
51 sort,
52 unread
53 },
54 statusCodeExpected
55 })
56}
57
58function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
59 const path = '/api/v1/users/me/notifications/read'
60
61 return makePostBodyRequest({
62 url,
63 path,
64 token,
65 fields: { ids },
66 statusCodeExpected
67 })
68}
69
70function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
71 const path = '/api/v1/users/me/notifications/read-all'
72
73 return makePostBodyRequest({
74 url,
75 path,
76 token,
77 statusCodeExpected
78 })
79}
80
81async function getLastNotification (serverUrl: string, accessToken: string) {
82 const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
83
84 if (res.body.total === 0) return undefined
85
86 return res.body.data[0] as UserNotification
87}
88 12
89type CheckerBaseParams = { 13type CheckerBaseParams = {
90 server: ServerInfo 14 server: PeerTubeServer
91 emails: any[] 15 emails: any[]
92 socketNotifications: UserNotification[] 16 socketNotifications: UserNotification[]
93 token: string 17 token: string
@@ -96,91 +20,41 @@ type CheckerBaseParams = {
96 20
97type CheckerType = 'presence' | 'absence' 21type CheckerType = 'presence' | 'absence'
98 22
99async function checkNotification ( 23function getAllNotificationsSettings (): UserNotificationSetting {
100 base: CheckerBaseParams, 24 return {
101 notificationChecker: (notification: UserNotification, type: CheckerType) => void, 25 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
102 emailNotificationFinder: (email: object) => boolean, 26 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
103 checkType: CheckerType 27 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
104) { 28 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
105 const check = base.check || { web: true, mail: true } 29 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
106 30 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
107 if (check.web) { 31 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
108 const notification = await getLastNotification(base.server.url, base.token) 32 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
109 33 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
110 if (notification || checkType !== 'absence') { 34 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
111 notificationChecker(notification, checkType) 35 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
112 } 36 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
113 37 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
114 const socketNotification = base.socketNotifications.find(n => { 38 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
115 try { 39 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
116 notificationChecker(n, 'presence') 40 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
117 return true
118 } catch {
119 return false
120 }
121 })
122
123 if (checkType === 'presence') {
124 const obj = inspect(base.socketNotifications, { depth: 5 })
125 expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
126 } else {
127 const obj = inspect(socketNotification, { depth: 5 })
128 expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
129 }
130 }
131
132 if (check.mail) {
133 // Last email
134 const email = base.emails
135 .slice()
136 .reverse()
137 .find(e => emailNotificationFinder(e))
138
139 if (checkType === 'presence') {
140 const emails = base.emails.map(e => e.text)
141 expect(email, 'The email is absent when is should be present. ' + inspect(emails)).to.not.be.undefined
142 } else {
143 expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
144 }
145 }
146}
147
148function checkVideo (video: any, videoName?: string, videoUUID?: string) {
149 if (videoName) {
150 expect(video.name).to.be.a('string')
151 expect(video.name).to.not.be.empty
152 expect(video.name).to.equal(videoName)
153 }
154
155 if (videoUUID) {
156 expect(video.uuid).to.be.a('string')
157 expect(video.uuid).to.not.be.empty
158 expect(video.uuid).to.equal(videoUUID)
159 } 41 }
160
161 expect(video.id).to.be.a('number')
162} 42}
163 43
164function checkActor (actor: any) { 44async function checkNewVideoFromSubscription (options: CheckerBaseParams & {
165 expect(actor.displayName).to.be.a('string') 45 videoName: string
166 expect(actor.displayName).to.not.be.empty 46 shortUUID: string
167 expect(actor.host).to.not.be.undefined 47 checkType: CheckerType
168} 48}) {
169 49 const { videoName, shortUUID } = options
170function checkComment (comment: any, commentId: number, threadId: number) {
171 expect(comment.id).to.equal(commentId)
172 expect(comment.threadId).to.equal(threadId)
173}
174
175async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
176 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION 50 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
177 51
178 function notificationChecker (notification: UserNotification, type: CheckerType) { 52 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
179 if (type === 'presence') { 53 if (checkType === 'presence') {
180 expect(notification).to.not.be.undefined 54 expect(notification).to.not.be.undefined
181 expect(notification.type).to.equal(notificationType) 55 expect(notification.type).to.equal(notificationType)
182 56
183 checkVideo(notification.video, videoName, videoUUID) 57 checkVideo(notification.video, videoName, shortUUID)
184 checkActor(notification.video.channel) 58 checkActor(notification.video.channel)
185 } else { 59 } else {
186 expect(notification).to.satisfy((n: UserNotification) => { 60 expect(notification).to.satisfy((n: UserNotification) => {
@@ -191,21 +65,26 @@ async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName
191 65
192 function emailNotificationFinder (email: object) { 66 function emailNotificationFinder (email: object) {
193 const text = email['text'] 67 const text = email['text']
194 return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1 68 return text.indexOf(shortUUID) !== -1 && text.indexOf('Your subscription') !== -1
195 } 69 }
196 70
197 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 71 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
198} 72}
199 73
200async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { 74async function checkVideoIsPublished (options: CheckerBaseParams & {
75 videoName: string
76 shortUUID: string
77 checkType: CheckerType
78}) {
79 const { videoName, shortUUID } = options
201 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED 80 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
202 81
203 function notificationChecker (notification: UserNotification, type: CheckerType) { 82 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
204 if (type === 'presence') { 83 if (checkType === 'presence') {
205 expect(notification).to.not.be.undefined 84 expect(notification).to.not.be.undefined
206 expect(notification.type).to.equal(notificationType) 85 expect(notification.type).to.equal(notificationType)
207 86
208 checkVideo(notification.video, videoName, videoUUID) 87 checkVideo(notification.video, videoName, shortUUID)
209 checkActor(notification.video.channel) 88 checkActor(notification.video.channel)
210 } else { 89 } else {
211 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) 90 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
@@ -214,30 +93,31 @@ async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string
214 93
215 function emailNotificationFinder (email: object) { 94 function emailNotificationFinder (email: object) {
216 const text: string = email['text'] 95 const text: string = email['text']
217 return text.includes(videoUUID) && text.includes('Your video') 96 return text.includes(shortUUID) && text.includes('Your video')
218 } 97 }
219 98
220 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 99 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
221} 100}
222 101
223async function checkMyVideoImportIsFinished ( 102async function checkMyVideoImportIsFinished (options: CheckerBaseParams & {
224 base: CheckerBaseParams, 103 videoName: string
225 videoName: string, 104 shortUUID: string
226 videoUUID: string, 105 url: string
227 url: string, 106 success: boolean
228 success: boolean, 107 checkType: CheckerType
229 type: CheckerType 108}) {
230) { 109 const { videoName, shortUUID, url, success } = options
110
231 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR 111 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
232 112
233 function notificationChecker (notification: UserNotification, type: CheckerType) { 113 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
234 if (type === 'presence') { 114 if (checkType === 'presence') {
235 expect(notification).to.not.be.undefined 115 expect(notification).to.not.be.undefined
236 expect(notification.type).to.equal(notificationType) 116 expect(notification.type).to.equal(notificationType)
237 117
238 expect(notification.videoImport.targetUrl).to.equal(url) 118 expect(notification.videoImport.targetUrl).to.equal(url)
239 119
240 if (success) checkVideo(notification.videoImport.video, videoName, videoUUID) 120 if (success) checkVideo(notification.videoImport.video, videoName, shortUUID)
241 } else { 121 } else {
242 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url) 122 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
243 } 123 }
@@ -250,14 +130,18 @@ async function checkMyVideoImportIsFinished (
250 return text.includes(url) && text.includes(toFind) 130 return text.includes(url) && text.includes(toFind)
251 } 131 }
252 132
253 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 133 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
254} 134}
255 135
256async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) { 136async function checkUserRegistered (options: CheckerBaseParams & {
137 username: string
138 checkType: CheckerType
139}) {
140 const { username } = options
257 const notificationType = UserNotificationType.NEW_USER_REGISTRATION 141 const notificationType = UserNotificationType.NEW_USER_REGISTRATION
258 142
259 function notificationChecker (notification: UserNotification, type: CheckerType) { 143 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
260 if (type === 'presence') { 144 if (checkType === 'presence') {
261 expect(notification).to.not.be.undefined 145 expect(notification).to.not.be.undefined
262 expect(notification.type).to.equal(notificationType) 146 expect(notification.type).to.equal(notificationType)
263 147
@@ -274,21 +158,21 @@ async function checkUserRegistered (base: CheckerBaseParams, username: string, t
274 return text.includes(' registered.') && text.includes(username) 158 return text.includes(' registered.') && text.includes(username)
275 } 159 }
276 160
277 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 161 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
278} 162}
279 163
280async function checkNewActorFollow ( 164async function checkNewActorFollow (options: CheckerBaseParams & {
281 base: CheckerBaseParams, 165 followType: 'channel' | 'account'
282 followType: 'channel' | 'account', 166 followerName: string
283 followerName: string, 167 followerDisplayName: string
284 followerDisplayName: string, 168 followingDisplayName: string
285 followingDisplayName: string, 169 checkType: CheckerType
286 type: CheckerType 170}) {
287) { 171 const { followType, followerName, followerDisplayName, followingDisplayName } = options
288 const notificationType = UserNotificationType.NEW_FOLLOW 172 const notificationType = UserNotificationType.NEW_FOLLOW
289 173
290 function notificationChecker (notification: UserNotification, type: CheckerType) { 174 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
291 if (type === 'presence') { 175 if (checkType === 'presence') {
292 expect(notification).to.not.be.undefined 176 expect(notification).to.not.be.undefined
293 expect(notification.type).to.equal(notificationType) 177 expect(notification.type).to.equal(notificationType)
294 178
@@ -314,14 +198,18 @@ async function checkNewActorFollow (
314 return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName) 198 return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
315 } 199 }
316 200
317 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 201 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
318} 202}
319 203
320async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) { 204async function checkNewInstanceFollower (options: CheckerBaseParams & {
205 followerHost: string
206 checkType: CheckerType
207}) {
208 const { followerHost } = options
321 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER 209 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
322 210
323 function notificationChecker (notification: UserNotification, type: CheckerType) { 211 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
324 if (type === 'presence') { 212 if (checkType === 'presence') {
325 expect(notification).to.not.be.undefined 213 expect(notification).to.not.be.undefined
326 expect(notification.type).to.equal(notificationType) 214 expect(notification.type).to.equal(notificationType)
327 215
@@ -343,14 +231,19 @@ async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost:
343 return text.includes('instance has a new follower') && text.includes(followerHost) 231 return text.includes('instance has a new follower') && text.includes(followerHost)
344 } 232 }
345 233
346 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 234 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
347} 235}
348 236
349async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) { 237async function checkAutoInstanceFollowing (options: CheckerBaseParams & {
238 followerHost: string
239 followingHost: string
240 checkType: CheckerType
241}) {
242 const { followerHost, followingHost } = options
350 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING 243 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING
351 244
352 function notificationChecker (notification: UserNotification, type: CheckerType) { 245 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
353 if (type === 'presence') { 246 if (checkType === 'presence') {
354 expect(notification).to.not.be.undefined 247 expect(notification).to.not.be.undefined
355 expect(notification.type).to.equal(notificationType) 248 expect(notification.type).to.equal(notificationType)
356 249
@@ -374,21 +267,21 @@ async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost
374 return text.includes(' automatically followed a new instance') && text.includes(followingHost) 267 return text.includes(' automatically followed a new instance') && text.includes(followingHost)
375 } 268 }
376 269
377 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 270 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
378} 271}
379 272
380async function checkCommentMention ( 273async function checkCommentMention (options: CheckerBaseParams & {
381 base: CheckerBaseParams, 274 shortUUID: string
382 uuid: string, 275 commentId: number
383 commentId: number, 276 threadId: number
384 threadId: number, 277 byAccountDisplayName: string
385 byAccountDisplayName: string, 278 checkType: CheckerType
386 type: CheckerType 279}) {
387) { 280 const { shortUUID, commentId, threadId, byAccountDisplayName } = options
388 const notificationType = UserNotificationType.COMMENT_MENTION 281 const notificationType = UserNotificationType.COMMENT_MENTION
389 282
390 function notificationChecker (notification: UserNotification, type: CheckerType) { 283 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
391 if (type === 'presence') { 284 if (checkType === 'presence') {
392 expect(notification).to.not.be.undefined 285 expect(notification).to.not.be.undefined
393 expect(notification.type).to.equal(notificationType) 286 expect(notification.type).to.equal(notificationType)
394 287
@@ -396,7 +289,7 @@ async function checkCommentMention (
396 checkActor(notification.comment.account) 289 checkActor(notification.comment.account)
397 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName) 290 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
398 291
399 checkVideo(notification.comment.video, undefined, uuid) 292 checkVideo(notification.comment.video, undefined, shortUUID)
400 } else { 293 } else {
401 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId) 294 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
402 } 295 }
@@ -405,25 +298,31 @@ async function checkCommentMention (
405 function emailNotificationFinder (email: object) { 298 function emailNotificationFinder (email: object) {
406 const text: string = email['text'] 299 const text: string = email['text']
407 300
408 return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName) 301 return text.includes(' mentioned ') && text.includes(shortUUID) && text.includes(byAccountDisplayName)
409 } 302 }
410 303
411 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 304 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
412} 305}
413 306
414let lastEmailCount = 0 307let lastEmailCount = 0
415 308
416async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) { 309async function checkNewCommentOnMyVideo (options: CheckerBaseParams & {
310 shortUUID: string
311 commentId: number
312 threadId: number
313 checkType: CheckerType
314}) {
315 const { server, shortUUID, commentId, threadId, checkType, emails } = options
417 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO 316 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
418 317
419 function notificationChecker (notification: UserNotification, type: CheckerType) { 318 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
420 if (type === 'presence') { 319 if (checkType === 'presence') {
421 expect(notification).to.not.be.undefined 320 expect(notification).to.not.be.undefined
422 expect(notification.type).to.equal(notificationType) 321 expect(notification.type).to.equal(notificationType)
423 322
424 checkComment(notification.comment, commentId, threadId) 323 checkComment(notification.comment, commentId, threadId)
425 checkActor(notification.comment.account) 324 checkActor(notification.comment.account)
426 checkVideo(notification.comment.video, undefined, uuid) 325 checkVideo(notification.comment.video, undefined, shortUUID)
427 } else { 326 } else {
428 expect(notification).to.satisfy((n: UserNotification) => { 327 expect(notification).to.satisfy((n: UserNotification) => {
429 return n === undefined || n.comment === undefined || n.comment.id !== commentId 328 return n === undefined || n.comment === undefined || n.comment.id !== commentId
@@ -431,51 +330,62 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string,
431 } 330 }
432 } 331 }
433 332
434 const commentUrl = `http://localhost:${base.server.port}/w/${uuid};threadId=${threadId}` 333 const commentUrl = `http://localhost:${server.port}/w/${shortUUID};threadId=${threadId}`
435 334
436 function emailNotificationFinder (email: object) { 335 function emailNotificationFinder (email: object) {
437 return email['text'].indexOf(commentUrl) !== -1 336 return email['text'].indexOf(commentUrl) !== -1
438 } 337 }
439 338
440 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 339 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
441 340
442 if (type === 'presence') { 341 if (checkType === 'presence') {
443 // We cannot detect email duplicates, so check we received another email 342 // We cannot detect email duplicates, so check we received another email
444 expect(base.emails).to.have.length.above(lastEmailCount) 343 expect(emails).to.have.length.above(lastEmailCount)
445 lastEmailCount = base.emails.length 344 lastEmailCount = emails.length
446 } 345 }
447} 346}
448 347
449async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { 348async function checkNewVideoAbuseForModerators (options: CheckerBaseParams & {
349 shortUUID: string
350 videoName: string
351 checkType: CheckerType
352}) {
353 const { shortUUID, videoName } = options
450 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS 354 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
451 355
452 function notificationChecker (notification: UserNotification, type: CheckerType) { 356 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
453 if (type === 'presence') { 357 if (checkType === 'presence') {
454 expect(notification).to.not.be.undefined 358 expect(notification).to.not.be.undefined
455 expect(notification.type).to.equal(notificationType) 359 expect(notification.type).to.equal(notificationType)
456 360
457 expect(notification.abuse.id).to.be.a('number') 361 expect(notification.abuse.id).to.be.a('number')
458 checkVideo(notification.abuse.video, videoName, videoUUID) 362 checkVideo(notification.abuse.video, videoName, shortUUID)
459 } else { 363 } else {
460 expect(notification).to.satisfy((n: UserNotification) => { 364 expect(notification).to.satisfy((n: UserNotification) => {
461 return n === undefined || n.abuse === undefined || n.abuse.video.uuid !== videoUUID 365 return n === undefined || n.abuse === undefined || n.abuse.video.shortUUID !== shortUUID
462 }) 366 })
463 } 367 }
464 } 368 }
465 369
466 function emailNotificationFinder (email: object) { 370 function emailNotificationFinder (email: object) {
467 const text = email['text'] 371 const text = email['text']
468 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 372 return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1
469 } 373 }
470 374
471 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 375 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
472} 376}
473 377
474async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, message: string, toEmail: string, type: CheckerType) { 378async function checkNewAbuseMessage (options: CheckerBaseParams & {
379 abuseId: number
380 message: string
381 toEmail: string
382 checkType: CheckerType
383}) {
384 const { abuseId, message, toEmail } = options
475 const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE 385 const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE
476 386
477 function notificationChecker (notification: UserNotification, type: CheckerType) { 387 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
478 if (type === 'presence') { 388 if (checkType === 'presence') {
479 expect(notification).to.not.be.undefined 389 expect(notification).to.not.be.undefined
480 expect(notification.type).to.equal(notificationType) 390 expect(notification.type).to.equal(notificationType)
481 391
@@ -494,14 +404,19 @@ async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, m
494 return text.indexOf(message) !== -1 && to.length !== 0 404 return text.indexOf(message) !== -1 && to.length !== 0
495 } 405 }
496 406
497 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 407 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
498} 408}
499 409
500async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number, state: AbuseState, type: CheckerType) { 410async function checkAbuseStateChange (options: CheckerBaseParams & {
411 abuseId: number
412 state: AbuseState
413 checkType: CheckerType
414}) {
415 const { abuseId, state } = options
501 const notificationType = UserNotificationType.ABUSE_STATE_CHANGE 416 const notificationType = UserNotificationType.ABUSE_STATE_CHANGE
502 417
503 function notificationChecker (notification: UserNotification, type: CheckerType) { 418 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
504 if (type === 'presence') { 419 if (checkType === 'presence') {
505 expect(notification).to.not.be.undefined 420 expect(notification).to.not.be.undefined
506 expect(notification.type).to.equal(notificationType) 421 expect(notification.type).to.equal(notificationType)
507 422
@@ -524,39 +439,48 @@ async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number,
524 return text.indexOf(contains) !== -1 439 return text.indexOf(contains) !== -1
525 } 440 }
526 441
527 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 442 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
528} 443}
529 444
530async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { 445async function checkNewCommentAbuseForModerators (options: CheckerBaseParams & {
446 shortUUID: string
447 videoName: string
448 checkType: CheckerType
449}) {
450 const { shortUUID, videoName } = options
531 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS 451 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
532 452
533 function notificationChecker (notification: UserNotification, type: CheckerType) { 453 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
534 if (type === 'presence') { 454 if (checkType === 'presence') {
535 expect(notification).to.not.be.undefined 455 expect(notification).to.not.be.undefined
536 expect(notification.type).to.equal(notificationType) 456 expect(notification.type).to.equal(notificationType)
537 457
538 expect(notification.abuse.id).to.be.a('number') 458 expect(notification.abuse.id).to.be.a('number')
539 checkVideo(notification.abuse.comment.video, videoName, videoUUID) 459 checkVideo(notification.abuse.comment.video, videoName, shortUUID)
540 } else { 460 } else {
541 expect(notification).to.satisfy((n: UserNotification) => { 461 expect(notification).to.satisfy((n: UserNotification) => {
542 return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID 462 return n === undefined || n.abuse === undefined || n.abuse.comment.video.shortUUID !== shortUUID
543 }) 463 })
544 } 464 }
545 } 465 }
546 466
547 function emailNotificationFinder (email: object) { 467 function emailNotificationFinder (email: object) {
548 const text = email['text'] 468 const text = email['text']
549 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 469 return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1
550 } 470 }
551 471
552 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 472 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
553} 473}
554 474
555async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) { 475async function checkNewAccountAbuseForModerators (options: CheckerBaseParams & {
476 displayName: string
477 checkType: CheckerType
478}) {
479 const { displayName } = options
556 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS 480 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
557 481
558 function notificationChecker (notification: UserNotification, type: CheckerType) { 482 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
559 if (type === 'presence') { 483 if (checkType === 'presence') {
560 expect(notification).to.not.be.undefined 484 expect(notification).to.not.be.undefined
561 expect(notification.type).to.equal(notificationType) 485 expect(notification.type).to.equal(notificationType)
562 486
@@ -574,40 +498,45 @@ async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displ
574 return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1 498 return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1
575 } 499 }
576 500
577 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 501 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
578} 502}
579 503
580async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { 504async function checkVideoAutoBlacklistForModerators (options: CheckerBaseParams & {
505 shortUUID: string
506 videoName: string
507 checkType: CheckerType
508}) {
509 const { shortUUID, videoName } = options
581 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS 510 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
582 511
583 function notificationChecker (notification: UserNotification, type: CheckerType) { 512 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
584 if (type === 'presence') { 513 if (checkType === 'presence') {
585 expect(notification).to.not.be.undefined 514 expect(notification).to.not.be.undefined
586 expect(notification.type).to.equal(notificationType) 515 expect(notification.type).to.equal(notificationType)
587 516
588 expect(notification.videoBlacklist.video.id).to.be.a('number') 517 expect(notification.videoBlacklist.video.id).to.be.a('number')
589 checkVideo(notification.videoBlacklist.video, videoName, videoUUID) 518 checkVideo(notification.videoBlacklist.video, videoName, shortUUID)
590 } else { 519 } else {
591 expect(notification).to.satisfy((n: UserNotification) => { 520 expect(notification).to.satisfy((n: UserNotification) => {
592 return n === undefined || n.video === undefined || n.video.uuid !== videoUUID 521 return n === undefined || n.video === undefined || n.video.shortUUID !== shortUUID
593 }) 522 })
594 } 523 }
595 } 524 }
596 525
597 function emailNotificationFinder (email: object) { 526 function emailNotificationFinder (email: object) {
598 const text = email['text'] 527 const text = email['text']
599 return text.indexOf(videoUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1 528 return text.indexOf(shortUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1
600 } 529 }
601 530
602 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 531 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
603} 532}
604 533
605async function checkNewBlacklistOnMyVideo ( 534async function checkNewBlacklistOnMyVideo (options: CheckerBaseParams & {
606 base: CheckerBaseParams, 535 shortUUID: string
607 videoUUID: string, 536 videoName: string
608 videoName: string,
609 blacklistType: 'blacklist' | 'unblacklist' 537 blacklistType: 'blacklist' | 'unblacklist'
610) { 538}) {
539 const { videoName, shortUUID, blacklistType } = options
611 const notificationType = blacklistType === 'blacklist' 540 const notificationType = blacklistType === 'blacklist'
612 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO 541 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
613 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO 542 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
@@ -618,22 +547,30 @@ async function checkNewBlacklistOnMyVideo (
618 547
619 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video 548 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
620 549
621 checkVideo(video, videoName, videoUUID) 550 checkVideo(video, videoName, shortUUID)
622 } 551 }
623 552
624 function emailNotificationFinder (email: object) { 553 function emailNotificationFinder (email: object) {
625 const text = email['text'] 554 const text = email['text']
626 return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1 555 const blacklistText = blacklistType === 'blacklist'
556 ? 'blacklisted'
557 : 'unblacklisted'
558
559 return text.includes(shortUUID) && text.includes(blacklistText)
627 } 560 }
628 561
629 await checkNotification(base, notificationChecker, emailNotificationFinder, 'presence') 562 await checkNotification({ ...options, notificationChecker, emailNotificationFinder, checkType: 'presence' })
630} 563}
631 564
632async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion: string, type: CheckerType) { 565async function checkNewPeerTubeVersion (options: CheckerBaseParams & {
566 latestVersion: string
567 checkType: CheckerType
568}) {
569 const { latestVersion } = options
633 const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION 570 const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION
634 571
635 function notificationChecker (notification: UserNotification, type: CheckerType) { 572 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
636 if (type === 'presence') { 573 if (checkType === 'presence') {
637 expect(notification).to.not.be.undefined 574 expect(notification).to.not.be.undefined
638 expect(notification.type).to.equal(notificationType) 575 expect(notification.type).to.equal(notificationType)
639 576
@@ -652,14 +589,19 @@ async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion:
652 return text.includes(latestVersion) 589 return text.includes(latestVersion)
653 } 590 }
654 591
655 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 592 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
656} 593}
657 594
658async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: PluginType, pluginName: string, type: CheckerType) { 595async function checkNewPluginVersion (options: CheckerBaseParams & {
596 pluginType: PluginType
597 pluginName: string
598 checkType: CheckerType
599}) {
600 const { pluginName, pluginType } = options
659 const notificationType = UserNotificationType.NEW_PLUGIN_VERSION 601 const notificationType = UserNotificationType.NEW_PLUGIN_VERSION
660 602
661 function notificationChecker (notification: UserNotification, type: CheckerType) { 603 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
662 if (type === 'presence') { 604 if (checkType === 'presence') {
663 expect(notification).to.not.be.undefined 605 expect(notification).to.not.be.undefined
664 expect(notification.type).to.equal(notificationType) 606 expect(notification.type).to.equal(notificationType)
665 607
@@ -678,28 +620,7 @@ async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: Plugi
678 return text.includes(pluginName) 620 return text.includes(pluginName)
679 } 621 }
680 622
681 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 623 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
682}
683
684function getAllNotificationsSettings (): UserNotificationSetting {
685 return {
686 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
687 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
688 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
689 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
690 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
691 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
692 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
693 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
694 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
695 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
696 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
697 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
698 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
699 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
700 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
701 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
702 }
703} 624}
704 625
705async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) { 626async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) {
@@ -719,7 +640,7 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
719 limit: 20 640 limit: 20
720 } 641 }
721 } 642 }
722 const servers = await flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg)) 643 const servers = await createMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
723 644
724 await setAccessTokensToServers(servers) 645 await setAccessTokensToServers(servers)
725 646
@@ -727,42 +648,33 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
727 await doubleFollow(servers[0], servers[1]) 648 await doubleFollow(servers[0], servers[1])
728 } 649 }
729 650
730 const user = { 651 const user = { username: 'user_1', password: 'super password' }
731 username: 'user_1', 652 await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 })
732 password: 'super password' 653 const userAccessToken = await servers[0].login.getAccessToken(user)
733 }
734 await createUser({
735 url: servers[0].url,
736 accessToken: servers[0].accessToken,
737 username: user.username,
738 password: user.password,
739 videoQuota: 10 * 1000 * 1000
740 })
741 const userAccessToken = await userLogin(servers[0], user)
742 654
743 await updateMyNotificationSettings(servers[0].url, userAccessToken, getAllNotificationsSettings()) 655 await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
744 await updateMyNotificationSettings(servers[0].url, servers[0].accessToken, getAllNotificationsSettings()) 656 await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
745 657
746 if (serversCount > 1) { 658 if (serversCount > 1) {
747 await updateMyNotificationSettings(servers[1].url, servers[1].accessToken, getAllNotificationsSettings()) 659 await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
748 } 660 }
749 661
750 { 662 {
751 const socket = getUserNotificationSocket(servers[0].url, userAccessToken) 663 const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken })
752 socket.on('new-notification', n => userNotifications.push(n)) 664 socket.on('new-notification', n => userNotifications.push(n))
753 } 665 }
754 { 666 {
755 const socket = getUserNotificationSocket(servers[0].url, servers[0].accessToken) 667 const socket = servers[0].socketIO.getUserNotificationSocket()
756 socket.on('new-notification', n => adminNotifications.push(n)) 668 socket.on('new-notification', n => adminNotifications.push(n))
757 } 669 }
758 670
759 if (serversCount > 1) { 671 if (serversCount > 1) {
760 const socket = getUserNotificationSocket(servers[1].url, servers[1].accessToken) 672 const socket = servers[1].socketIO.getUserNotificationSocket()
761 socket.on('new-notification', n => adminNotificationsServer2.push(n)) 673 socket.on('new-notification', n => adminNotificationsServer2.push(n))
762 } 674 }
763 675
764 const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) 676 const { videoChannels } = await servers[0].users.getMyInfo()
765 const channelId = resChannel.body.videoChannels[0].id 677 const channelId = videoChannels[0].id
766 678
767 return { 679 return {
768 userNotifications, 680 userNotifications,
@@ -778,11 +690,10 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
778// --------------------------------------------------------------------------- 690// ---------------------------------------------------------------------------
779 691
780export { 692export {
693 getAllNotificationsSettings,
694
781 CheckerBaseParams, 695 CheckerBaseParams,
782 CheckerType, 696 CheckerType,
783 getAllNotificationsSettings,
784 checkNotification,
785 markAsReadAllNotifications,
786 checkMyVideoImportIsFinished, 697 checkMyVideoImportIsFinished,
787 checkUserRegistered, 698 checkUserRegistered,
788 checkAutoInstanceFollowing, 699 checkAutoInstanceFollowing,
@@ -792,14 +703,10 @@ export {
792 checkNewCommentOnMyVideo, 703 checkNewCommentOnMyVideo,
793 checkNewBlacklistOnMyVideo, 704 checkNewBlacklistOnMyVideo,
794 checkCommentMention, 705 checkCommentMention,
795 updateMyNotificationSettings,
796 checkNewVideoAbuseForModerators, 706 checkNewVideoAbuseForModerators,
797 checkVideoAutoBlacklistForModerators, 707 checkVideoAutoBlacklistForModerators,
798 checkNewAbuseMessage, 708 checkNewAbuseMessage,
799 checkAbuseStateChange, 709 checkAbuseStateChange,
800 getUserNotifications,
801 markAsReadNotifications,
802 getLastNotification,
803 checkNewInstanceFollower, 710 checkNewInstanceFollower,
804 prepareNotificationsTest, 711 prepareNotificationsTest,
805 checkNewCommentAbuseForModerators, 712 checkNewCommentAbuseForModerators,
@@ -807,3 +714,82 @@ export {
807 checkNewPeerTubeVersion, 714 checkNewPeerTubeVersion,
808 checkNewPluginVersion 715 checkNewPluginVersion
809} 716}
717
718// ---------------------------------------------------------------------------
719
720async function checkNotification (options: CheckerBaseParams & {
721 notificationChecker: (notification: UserNotification, checkType: CheckerType) => void
722 emailNotificationFinder: (email: object) => boolean
723 checkType: CheckerType
724}) {
725 const { server, token, checkType, notificationChecker, emailNotificationFinder, socketNotifications, emails } = options
726
727 const check = options.check || { web: true, mail: true }
728
729 if (check.web) {
730 const notification = await server.notifications.getLastest({ token: token })
731
732 if (notification || checkType !== 'absence') {
733 notificationChecker(notification, checkType)
734 }
735
736 const socketNotification = socketNotifications.find(n => {
737 try {
738 notificationChecker(n, 'presence')
739 return true
740 } catch {
741 return false
742 }
743 })
744
745 if (checkType === 'presence') {
746 const obj = inspect(socketNotifications, { depth: 5 })
747 expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
748 } else {
749 const obj = inspect(socketNotification, { depth: 5 })
750 expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
751 }
752 }
753
754 if (check.mail) {
755 // Last email
756 const email = emails
757 .slice()
758 .reverse()
759 .find(e => emailNotificationFinder(e))
760
761 if (checkType === 'presence') {
762 const texts = emails.map(e => e.text)
763 expect(email, 'The email is absent when is should be present. ' + inspect(texts)).to.not.be.undefined
764 } else {
765 expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
766 }
767 }
768}
769
770function checkVideo (video: any, videoName?: string, shortUUID?: string) {
771 if (videoName) {
772 expect(video.name).to.be.a('string')
773 expect(video.name).to.not.be.empty
774 expect(video.name).to.equal(videoName)
775 }
776
777 if (shortUUID) {
778 expect(video.shortUUID).to.be.a('string')
779 expect(video.shortUUID).to.not.be.empty
780 expect(video.shortUUID).to.equal(shortUUID)
781 }
782
783 expect(video.id).to.be.a('number')
784}
785
786function checkActor (actor: any) {
787 expect(actor.displayName).to.be.a('string')
788 expect(actor.displayName).to.not.be.empty
789 expect(actor.host).to.not.be.undefined
790}
791
792function checkComment (comment: any, commentId: number, threadId: number) {
793 expect(comment.id).to.equal(commentId)
794 expect(comment.threadId).to.equal(threadId)
795}
diff --git a/shared/extra-utils/users/subscriptions-command.ts b/shared/extra-utils/users/subscriptions-command.ts
new file mode 100644
index 000000000..edc60e612
--- /dev/null
+++ b/shared/extra-utils/users/subscriptions-command.ts
@@ -0,0 +1,99 @@
1import { HttpStatusCode, ResultList, Video, VideoChannel } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class SubscriptionsCommand extends AbstractCommand {
5
6 add (options: OverrideCommandOptions & {
7 targetUri: string
8 }) {
9 const path = '/api/v1/users/me/subscriptions'
10
11 return this.postBodyRequest({
12 ...options,
13
14 path,
15 fields: { uri: options.targetUri },
16 implicitToken: true,
17 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
18 })
19 }
20
21 list (options: OverrideCommandOptions & {
22 sort?: string // default -createdAt
23 search?: string
24 } = {}) {
25 const { sort = '-createdAt', search } = options
26 const path = '/api/v1/users/me/subscriptions'
27
28 return this.getRequestBody<ResultList<VideoChannel>>({
29 ...options,
30
31 path,
32 query: {
33 sort,
34 search
35 },
36 implicitToken: true,
37 defaultExpectedStatus: HttpStatusCode.OK_200
38 })
39 }
40
41 listVideos (options: OverrideCommandOptions & {
42 sort?: string // default -createdAt
43 } = {}) {
44 const { sort = '-createdAt' } = options
45 const path = '/api/v1/users/me/subscriptions/videos'
46
47 return this.getRequestBody<ResultList<Video>>({
48 ...options,
49
50 path,
51 query: { sort },
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56
57 get (options: OverrideCommandOptions & {
58 uri: string
59 }) {
60 const path = '/api/v1/users/me/subscriptions/' + options.uri
61
62 return this.getRequestBody<VideoChannel>({
63 ...options,
64
65 path,
66 implicitToken: true,
67 defaultExpectedStatus: HttpStatusCode.OK_200
68 })
69 }
70
71 remove (options: OverrideCommandOptions & {
72 uri: string
73 }) {
74 const path = '/api/v1/users/me/subscriptions/' + options.uri
75
76 return this.deleteRequest({
77 ...options,
78
79 path,
80 implicitToken: true,
81 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
82 })
83 }
84
85 exist (options: OverrideCommandOptions & {
86 uris: string[]
87 }) {
88 const path = '/api/v1/users/me/subscriptions/exist'
89
90 return this.getRequestBody<{ [id: string ]: boolean }>({
91 ...options,
92
93 path,
94 query: { 'uris[]': options.uris },
95 implicitToken: true,
96 defaultExpectedStatus: HttpStatusCode.OK_200
97 })
98 }
99}
diff --git a/shared/extra-utils/users/user-subscriptions.ts b/shared/extra-utils/users/user-subscriptions.ts
deleted file mode 100644
index edc7a3562..000000000
--- a/shared/extra-utils/users/user-subscriptions.ts
+++ /dev/null
@@ -1,93 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function addUserSubscription (url: string, token: string, targetUri: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
5 const path = '/api/v1/users/me/subscriptions'
6
7 return makePostBodyRequest({
8 url,
9 path,
10 token,
11 statusCodeExpected,
12 fields: { uri: targetUri }
13 })
14}
15
16function listUserSubscriptions (parameters: {
17 url: string
18 token: string
19 sort?: string
20 search?: string
21 statusCodeExpected?: number
22}) {
23 const { url, token, sort = '-createdAt', search, statusCodeExpected = HttpStatusCode.OK_200 } = parameters
24 const path = '/api/v1/users/me/subscriptions'
25
26 return makeGetRequest({
27 url,
28 path,
29 token,
30 statusCodeExpected,
31 query: {
32 sort,
33 search
34 }
35 })
36}
37
38function listUserSubscriptionVideos (url: string, token: string, sort = '-createdAt', statusCodeExpected = HttpStatusCode.OK_200) {
39 const path = '/api/v1/users/me/subscriptions/videos'
40
41 return makeGetRequest({
42 url,
43 path,
44 token,
45 statusCodeExpected,
46 query: { sort }
47 })
48}
49
50function getUserSubscription (url: string, token: string, uri: string, statusCodeExpected = HttpStatusCode.OK_200) {
51 const path = '/api/v1/users/me/subscriptions/' + uri
52
53 return makeGetRequest({
54 url,
55 path,
56 token,
57 statusCodeExpected
58 })
59}
60
61function removeUserSubscription (url: string, token: string, uri: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
62 const path = '/api/v1/users/me/subscriptions/' + uri
63
64 return makeDeleteRequest({
65 url,
66 path,
67 token,
68 statusCodeExpected
69 })
70}
71
72function areSubscriptionsExist (url: string, token: string, uris: string[], statusCodeExpected = HttpStatusCode.OK_200) {
73 const path = '/api/v1/users/me/subscriptions/exist'
74
75 return makeGetRequest({
76 url,
77 path,
78 query: { 'uris[]': uris },
79 token,
80 statusCodeExpected
81 })
82}
83
84// ---------------------------------------------------------------------------
85
86export {
87 areSubscriptionsExist,
88 addUserSubscription,
89 listUserSubscriptions,
90 getUserSubscription,
91 listUserSubscriptionVideos,
92 removeUserSubscription
93}
diff --git a/shared/extra-utils/users/users-command.ts b/shared/extra-utils/users/users-command.ts
new file mode 100644
index 000000000..ddd20d041
--- /dev/null
+++ b/shared/extra-utils/users/users-command.ts
@@ -0,0 +1,415 @@
1import { omit } from 'lodash'
2import { pick } from '@shared/core-utils'
3import {
4 HttpStatusCode,
5 MyUser,
6 ResultList,
7 User,
8 UserAdminFlag,
9 UserCreateResult,
10 UserRole,
11 UserUpdate,
12 UserUpdateMe,
13 UserVideoQuota,
14 UserVideoRate
15} from '@shared/models'
16import { ScopedToken } from '@shared/models/users/user-scoped-token'
17import { unwrapBody } from '../requests'
18import { AbstractCommand, OverrideCommandOptions } from '../shared'
19
20export class UsersCommand extends AbstractCommand {
21
22 askResetPassword (options: OverrideCommandOptions & {
23 email: string
24 }) {
25 const { email } = options
26 const path = '/api/v1/users/ask-reset-password'
27
28 return this.postBodyRequest({
29 ...options,
30
31 path,
32 fields: { email },
33 implicitToken: false,
34 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
35 })
36 }
37
38 resetPassword (options: OverrideCommandOptions & {
39 userId: number
40 verificationString: string
41 password: string
42 }) {
43 const { userId, verificationString, password } = options
44 const path = '/api/v1/users/' + userId + '/reset-password'
45
46 return this.postBodyRequest({
47 ...options,
48
49 path,
50 fields: { password, verificationString },
51 implicitToken: false,
52 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
53 })
54 }
55
56 // ---------------------------------------------------------------------------
57
58 askSendVerifyEmail (options: OverrideCommandOptions & {
59 email: string
60 }) {
61 const { email } = options
62 const path = '/api/v1/users/ask-send-verify-email'
63
64 return this.postBodyRequest({
65 ...options,
66
67 path,
68 fields: { email },
69 implicitToken: false,
70 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
71 })
72 }
73
74 verifyEmail (options: OverrideCommandOptions & {
75 userId: number
76 verificationString: string
77 isPendingEmail?: boolean // default false
78 }) {
79 const { userId, verificationString, isPendingEmail = false } = options
80 const path = '/api/v1/users/' + userId + '/verify-email'
81
82 return this.postBodyRequest({
83 ...options,
84
85 path,
86 fields: {
87 verificationString,
88 isPendingEmail
89 },
90 implicitToken: false,
91 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
92 })
93 }
94
95 // ---------------------------------------------------------------------------
96
97 banUser (options: OverrideCommandOptions & {
98 userId: number
99 reason?: string
100 }) {
101 const { userId, reason } = options
102 const path = '/api/v1/users' + '/' + userId + '/block'
103
104 return this.postBodyRequest({
105 ...options,
106
107 path,
108 fields: { reason },
109 implicitToken: true,
110 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
111 })
112 }
113
114 unbanUser (options: OverrideCommandOptions & {
115 userId: number
116 }) {
117 const { userId } = options
118 const path = '/api/v1/users' + '/' + userId + '/unblock'
119
120 return this.postBodyRequest({
121 ...options,
122
123 path,
124 implicitToken: true,
125 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
126 })
127 }
128
129 // ---------------------------------------------------------------------------
130
131 getMyScopedTokens (options: OverrideCommandOptions = {}) {
132 const path = '/api/v1/users/scoped-tokens'
133
134 return this.getRequestBody<ScopedToken>({
135 ...options,
136
137 path,
138 implicitToken: true,
139 defaultExpectedStatus: HttpStatusCode.OK_200
140 })
141 }
142
143 renewMyScopedTokens (options: OverrideCommandOptions = {}) {
144 const path = '/api/v1/users/scoped-tokens'
145
146 return this.postBodyRequest({
147 ...options,
148
149 path,
150 implicitToken: true,
151 defaultExpectedStatus: HttpStatusCode.OK_200
152 })
153 }
154
155 // ---------------------------------------------------------------------------
156
157 create (options: OverrideCommandOptions & {
158 username: string
159 password?: string
160 videoQuota?: number
161 videoQuotaDaily?: number
162 role?: UserRole
163 adminFlags?: UserAdminFlag
164 }) {
165 const {
166 username,
167 adminFlags,
168 password = 'password',
169 videoQuota = 42000000,
170 videoQuotaDaily = -1,
171 role = UserRole.USER
172 } = options
173
174 const path = '/api/v1/users'
175
176 return unwrapBody<{ user: UserCreateResult }>(this.postBodyRequest({
177 ...options,
178
179 path,
180 fields: {
181 username,
182 password,
183 role,
184 adminFlags,
185 email: username + '@example.com',
186 videoQuota,
187 videoQuotaDaily
188 },
189 implicitToken: true,
190 defaultExpectedStatus: HttpStatusCode.OK_200
191 })).then(res => res.user)
192 }
193
194 async generate (username: string) {
195 const password = 'password'
196 const user = await this.create({ username, password })
197
198 const token = await this.server.login.getAccessToken({ username, password })
199
200 const me = await this.getMyInfo({ token })
201
202 return {
203 token,
204 userId: user.id,
205 userChannelId: me.videoChannels[0].id
206 }
207 }
208
209 async generateUserAndToken (username: string) {
210 const password = 'password'
211 await this.create({ username, password })
212
213 return this.server.login.getAccessToken({ username, password })
214 }
215
216 register (options: OverrideCommandOptions & {
217 username: string
218 password?: string
219 displayName?: string
220 channel?: {
221 name: string
222 displayName: string
223 }
224 }) {
225 const { username, password = 'password', displayName, channel } = options
226 const path = '/api/v1/users/register'
227
228 return this.postBodyRequest({
229 ...options,
230
231 path,
232 fields: {
233 username,
234 password,
235 email: username + '@example.com',
236 displayName,
237 channel
238 },
239 implicitToken: false,
240 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
241 })
242 }
243
244 // ---------------------------------------------------------------------------
245
246 getMyInfo (options: OverrideCommandOptions = {}) {
247 const path = '/api/v1/users/me'
248
249 return this.getRequestBody<MyUser>({
250 ...options,
251
252 path,
253 implicitToken: true,
254 defaultExpectedStatus: HttpStatusCode.OK_200
255 })
256 }
257
258 getMyQuotaUsed (options: OverrideCommandOptions = {}) {
259 const path = '/api/v1/users/me/video-quota-used'
260
261 return this.getRequestBody<UserVideoQuota>({
262 ...options,
263
264 path,
265 implicitToken: true,
266 defaultExpectedStatus: HttpStatusCode.OK_200
267 })
268 }
269
270 getMyRating (options: OverrideCommandOptions & {
271 videoId: number | string
272 }) {
273 const { videoId } = options
274 const path = '/api/v1/users/me/videos/' + videoId + '/rating'
275
276 return this.getRequestBody<UserVideoRate>({
277 ...options,
278
279 path,
280 implicitToken: true,
281 defaultExpectedStatus: HttpStatusCode.OK_200
282 })
283 }
284
285 deleteMe (options: OverrideCommandOptions = {}) {
286 const path = '/api/v1/users/me'
287
288 return this.deleteRequest({
289 ...options,
290
291 path,
292 implicitToken: true,
293 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
294 })
295 }
296
297 updateMe (options: OverrideCommandOptions & UserUpdateMe) {
298 const path = '/api/v1/users/me'
299
300 const toSend: UserUpdateMe = omit(options, 'url', 'accessToken')
301
302 return this.putBodyRequest({
303 ...options,
304
305 path,
306 fields: toSend,
307 implicitToken: true,
308 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
309 })
310 }
311
312 updateMyAvatar (options: OverrideCommandOptions & {
313 fixture: string
314 }) {
315 const { fixture } = options
316 const path = '/api/v1/users/me/avatar/pick'
317
318 return this.updateImageRequest({
319 ...options,
320
321 path,
322 fixture,
323 fieldname: 'avatarfile',
324
325 implicitToken: true,
326 defaultExpectedStatus: HttpStatusCode.OK_200
327 })
328 }
329
330 // ---------------------------------------------------------------------------
331
332 get (options: OverrideCommandOptions & {
333 userId: number
334 withStats?: boolean // default false
335 }) {
336 const { userId, withStats } = options
337 const path = '/api/v1/users/' + userId
338
339 return this.getRequestBody<User>({
340 ...options,
341
342 path,
343 query: { withStats },
344 implicitToken: true,
345 defaultExpectedStatus: HttpStatusCode.OK_200
346 })
347 }
348
349 list (options: OverrideCommandOptions & {
350 start?: number
351 count?: number
352 sort?: string
353 search?: string
354 blocked?: boolean
355 } = {}) {
356 const path = '/api/v1/users'
357
358 return this.getRequestBody<ResultList<User>>({
359 ...options,
360
361 path,
362 query: pick(options, [ 'start', 'count', 'sort', 'search', 'blocked' ]),
363 implicitToken: true,
364 defaultExpectedStatus: HttpStatusCode.OK_200
365 })
366 }
367
368 remove (options: OverrideCommandOptions & {
369 userId: number
370 }) {
371 const { userId } = options
372 const path = '/api/v1/users/' + userId
373
374 return this.deleteRequest({
375 ...options,
376
377 path,
378 implicitToken: true,
379 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
380 })
381 }
382
383 update (options: OverrideCommandOptions & {
384 userId: number
385 email?: string
386 emailVerified?: boolean
387 videoQuota?: number
388 videoQuotaDaily?: number
389 password?: string
390 adminFlags?: UserAdminFlag
391 pluginAuth?: string
392 role?: UserRole
393 }) {
394 const path = '/api/v1/users/' + options.userId
395
396 const toSend: UserUpdate = {}
397 if (options.password !== undefined && options.password !== null) toSend.password = options.password
398 if (options.email !== undefined && options.email !== null) toSend.email = options.email
399 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend.emailVerified = options.emailVerified
400 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend.videoQuota = options.videoQuota
401 if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend.videoQuotaDaily = options.videoQuotaDaily
402 if (options.role !== undefined && options.role !== null) toSend.role = options.role
403 if (options.adminFlags !== undefined && options.adminFlags !== null) toSend.adminFlags = options.adminFlags
404 if (options.pluginAuth !== undefined) toSend.pluginAuth = options.pluginAuth
405
406 return this.putBodyRequest({
407 ...options,
408
409 path,
410 fields: toSend,
411 implicitToken: true,
412 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
413 })
414 }
415}
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts
deleted file mode 100644
index 0f15962ad..000000000
--- a/shared/extra-utils/users/users.ts
+++ /dev/null
@@ -1,415 +0,0 @@
1import { omit } from 'lodash'
2import * as request from 'supertest'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4import { UserUpdateMe } from '../../models/users'
5import { UserAdminFlag } from '../../models/users/user-flag.model'
6import { UserRegister } from '../../models/users/user-register.model'
7import { UserRole } from '../../models/users/user-role'
8import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests'
9import { ServerInfo } from '../server/servers'
10import { userLogin } from './login'
11
12function createUser (parameters: {
13 url: string
14 accessToken: string
15 username: string
16 password: string
17 videoQuota?: number
18 videoQuotaDaily?: number
19 role?: UserRole
20 adminFlags?: UserAdminFlag
21 specialStatus?: number
22}) {
23 const {
24 url,
25 accessToken,
26 username,
27 adminFlags,
28 password = 'password',
29 videoQuota = 1000000,
30 videoQuotaDaily = -1,
31 role = UserRole.USER,
32 specialStatus = HttpStatusCode.OK_200
33 } = parameters
34
35 const path = '/api/v1/users'
36 const body = {
37 username,
38 password,
39 role,
40 adminFlags,
41 email: username + '@example.com',
42 videoQuota,
43 videoQuotaDaily
44 }
45
46 return request(url)
47 .post(path)
48 .set('Accept', 'application/json')
49 .set('Authorization', 'Bearer ' + accessToken)
50 .send(body)
51 .expect(specialStatus)
52}
53
54async function generateUser (server: ServerInfo, username: string) {
55 const password = 'my super password'
56 const resCreate = await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
57
58 const token = await userLogin(server, { username, password })
59
60 const resMe = await getMyUserInformation(server.url, token)
61
62 return {
63 token,
64 userId: resCreate.body.user.id,
65 userChannelId: resMe.body.videoChannels[0].id
66 }
67}
68
69async function generateUserAccessToken (server: ServerInfo, username: string) {
70 const password = 'my super password'
71 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
72
73 return userLogin(server, { username, password })
74}
75
76function registerUser (url: string, username: string, password: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
77 const path = '/api/v1/users/register'
78 const body = {
79 username,
80 password,
81 email: username + '@example.com'
82 }
83
84 return request(url)
85 .post(path)
86 .set('Accept', 'application/json')
87 .send(body)
88 .expect(specialStatus)
89}
90
91function registerUserWithChannel (options: {
92 url: string
93 user: { username: string, password: string, displayName?: string }
94 channel: { name: string, displayName: string }
95}) {
96 const path = '/api/v1/users/register'
97 const body: UserRegister = {
98 username: options.user.username,
99 password: options.user.password,
100 email: options.user.username + '@example.com',
101 channel: options.channel
102 }
103
104 if (options.user.displayName) {
105 Object.assign(body, { displayName: options.user.displayName })
106 }
107
108 return makePostBodyRequest({
109 url: options.url,
110 path,
111 fields: body,
112 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
113 })
114}
115
116function getMyUserInformation (url: string, accessToken: string, specialStatus = HttpStatusCode.OK_200) {
117 const path = '/api/v1/users/me'
118
119 return request(url)
120 .get(path)
121 .set('Accept', 'application/json')
122 .set('Authorization', 'Bearer ' + accessToken)
123 .expect(specialStatus)
124 .expect('Content-Type', /json/)
125}
126
127function getUserScopedTokens (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
128 const path = '/api/v1/users/scoped-tokens'
129
130 return makeGetRequest({
131 url,
132 path,
133 token,
134 statusCodeExpected
135 })
136}
137
138function renewUserScopedTokens (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
139 const path = '/api/v1/users/scoped-tokens'
140
141 return makePostBodyRequest({
142 url,
143 path,
144 token,
145 statusCodeExpected
146 })
147}
148
149function deleteMe (url: string, accessToken: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
150 const path = '/api/v1/users/me'
151
152 return request(url)
153 .delete(path)
154 .set('Accept', 'application/json')
155 .set('Authorization', 'Bearer ' + accessToken)
156 .expect(specialStatus)
157}
158
159function getMyUserVideoQuotaUsed (url: string, accessToken: string, specialStatus = HttpStatusCode.OK_200) {
160 const path = '/api/v1/users/me/video-quota-used'
161
162 return request(url)
163 .get(path)
164 .set('Accept', 'application/json')
165 .set('Authorization', 'Bearer ' + accessToken)
166 .expect(specialStatus)
167 .expect('Content-Type', /json/)
168}
169
170function getUserInformation (url: string, accessToken: string, userId: number, withStats = false) {
171 const path = '/api/v1/users/' + userId
172
173 return request(url)
174 .get(path)
175 .query({ withStats })
176 .set('Accept', 'application/json')
177 .set('Authorization', 'Bearer ' + accessToken)
178 .expect(HttpStatusCode.OK_200)
179 .expect('Content-Type', /json/)
180}
181
182function getMyUserVideoRating (url: string, accessToken: string, videoId: number | string, specialStatus = HttpStatusCode.OK_200) {
183 const path = '/api/v1/users/me/videos/' + videoId + '/rating'
184
185 return request(url)
186 .get(path)
187 .set('Accept', 'application/json')
188 .set('Authorization', 'Bearer ' + accessToken)
189 .expect(specialStatus)
190 .expect('Content-Type', /json/)
191}
192
193function getUsersList (url: string, accessToken: string) {
194 const path = '/api/v1/users'
195
196 return request(url)
197 .get(path)
198 .set('Accept', 'application/json')
199 .set('Authorization', 'Bearer ' + accessToken)
200 .expect(HttpStatusCode.OK_200)
201 .expect('Content-Type', /json/)
202}
203
204function getUsersListPaginationAndSort (
205 url: string,
206 accessToken: string,
207 start: number,
208 count: number,
209 sort: string,
210 search?: string,
211 blocked?: boolean
212) {
213 const path = '/api/v1/users'
214
215 const query = {
216 start,
217 count,
218 sort,
219 search,
220 blocked
221 }
222
223 return request(url)
224 .get(path)
225 .query(query)
226 .set('Accept', 'application/json')
227 .set('Authorization', 'Bearer ' + accessToken)
228 .expect(HttpStatusCode.OK_200)
229 .expect('Content-Type', /json/)
230}
231
232function removeUser (url: string, userId: number | string, accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
233 const path = '/api/v1/users'
234
235 return request(url)
236 .delete(path + '/' + userId)
237 .set('Accept', 'application/json')
238 .set('Authorization', 'Bearer ' + accessToken)
239 .expect(expectedStatus)
240}
241
242function blockUser (
243 url: string,
244 userId: number | string,
245 accessToken: string,
246 expectedStatus = HttpStatusCode.NO_CONTENT_204,
247 reason?: string
248) {
249 const path = '/api/v1/users'
250 let body: any
251 if (reason) body = { reason }
252
253 return request(url)
254 .post(path + '/' + userId + '/block')
255 .send(body)
256 .set('Accept', 'application/json')
257 .set('Authorization', 'Bearer ' + accessToken)
258 .expect(expectedStatus)
259}
260
261function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
262 const path = '/api/v1/users'
263
264 return request(url)
265 .post(path + '/' + userId + '/unblock')
266 .set('Accept', 'application/json')
267 .set('Authorization', 'Bearer ' + accessToken)
268 .expect(expectedStatus)
269}
270
271function updateMyUser (options: { url: string, accessToken: string, statusCodeExpected?: HttpStatusCode } & UserUpdateMe) {
272 const path = '/api/v1/users/me'
273
274 const toSend: UserUpdateMe = omit(options, 'url', 'accessToken')
275
276 return makePutBodyRequest({
277 url: options.url,
278 path,
279 token: options.accessToken,
280 fields: toSend,
281 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.NO_CONTENT_204
282 })
283}
284
285function updateMyAvatar (options: {
286 url: string
287 accessToken: string
288 fixture: string
289}) {
290 const path = '/api/v1/users/me/avatar/pick'
291
292 return updateImageRequest({ ...options, path, fieldname: 'avatarfile' })
293}
294
295function updateUser (options: {
296 url: string
297 userId: number
298 accessToken: string
299 email?: string
300 emailVerified?: boolean
301 videoQuota?: number
302 videoQuotaDaily?: number
303 password?: string
304 adminFlags?: UserAdminFlag
305 pluginAuth?: string
306 role?: UserRole
307}) {
308 const path = '/api/v1/users/' + options.userId
309
310 const toSend = {}
311 if (options.password !== undefined && options.password !== null) toSend['password'] = options.password
312 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
313 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified
314 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota
315 if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend['videoQuotaDaily'] = options.videoQuotaDaily
316 if (options.role !== undefined && options.role !== null) toSend['role'] = options.role
317 if (options.adminFlags !== undefined && options.adminFlags !== null) toSend['adminFlags'] = options.adminFlags
318 if (options.pluginAuth !== undefined) toSend['pluginAuth'] = options.pluginAuth
319
320 return makePutBodyRequest({
321 url: options.url,
322 path,
323 token: options.accessToken,
324 fields: toSend,
325 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
326 })
327}
328
329function askResetPassword (url: string, email: string) {
330 const path = '/api/v1/users/ask-reset-password'
331
332 return makePostBodyRequest({
333 url,
334 path,
335 fields: { email },
336 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
337 })
338}
339
340function resetPassword (
341 url: string,
342 userId: number,
343 verificationString: string,
344 password: string,
345 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
346) {
347 const path = '/api/v1/users/' + userId + '/reset-password'
348
349 return makePostBodyRequest({
350 url,
351 path,
352 fields: { password, verificationString },
353 statusCodeExpected
354 })
355}
356
357function askSendVerifyEmail (url: string, email: string) {
358 const path = '/api/v1/users/ask-send-verify-email'
359
360 return makePostBodyRequest({
361 url,
362 path,
363 fields: { email },
364 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
365 })
366}
367
368function verifyEmail (
369 url: string,
370 userId: number,
371 verificationString: string,
372 isPendingEmail = false,
373 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
374) {
375 const path = '/api/v1/users/' + userId + '/verify-email'
376
377 return makePostBodyRequest({
378 url,
379 path,
380 fields: {
381 verificationString,
382 isPendingEmail
383 },
384 statusCodeExpected
385 })
386}
387
388// ---------------------------------------------------------------------------
389
390export {
391 createUser,
392 registerUser,
393 getMyUserInformation,
394 getMyUserVideoRating,
395 deleteMe,
396 registerUserWithChannel,
397 getMyUserVideoQuotaUsed,
398 getUsersList,
399 getUsersListPaginationAndSort,
400 removeUser,
401 updateUser,
402 updateMyUser,
403 getUserInformation,
404 blockUser,
405 unblockUser,
406 askResetPassword,
407 resetPassword,
408 renewUserScopedTokens,
409 updateMyAvatar,
410 generateUser,
411 askSendVerifyEmail,
412 generateUserAccessToken,
413 verifyEmail,
414 getUserScopedTokens
415}