]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - shared/server-commands/requests/requests.ts
Add ability to customize token lifetime
[github/Chocobozzz/PeerTube.git] / shared / server-commands / requests / requests.ts
CommitLineData
c0e8b12e 1/* eslint-disable @typescript-eslint/no-floating-promises */
a1587156 2
9107d791 3import { decode } from 'querystring'
41fb13c3 4import request from 'supertest'
2d1ad5b9 5import { URL } from 'url'
3545e72c 6import { buildAbsoluteFixturePath, pick } from '@shared/core-utils'
c0e8b12e 7import { HttpStatusCode } from '@shared/models'
09209296 8
c0e8b12e 9export type CommonRequestParams = {
a1587156
C
10 url: string
11 path?: string
a1587156 12 contentType?: string
4c280004 13 range?: string
9107d791 14 redirects?: number
012580d9 15 accept?: string
41d1d075 16 host?: string
c0e8b12e
C
17 token?: string
18 headers?: { [ name: string ]: string }
19 type?: string
20 xForwardedFor?: string
21 expectedStatus?: HttpStatusCode
22}
eec63bbc 23
3545e72c
C
24function makeRawRequest (options: {
25 url: string
26 token?: string
27 expectedStatus?: HttpStatusCode
28 range?: string
29 query?: { [ id: string ]: string }
30}) {
31 const { host, protocol, pathname } = new URL(options.url)
32
33 return makeGetRequest({
34 url: `${protocol}//${host}`,
35 path: pathname,
e57a840e 36 contentType: undefined,
eec63bbc 37
3545e72c
C
38 ...pick(options, [ 'expectedStatus', 'range', 'token', 'query' ])
39 })
eec63bbc
C
40}
41
c0e8b12e
C
42function makeGetRequest (options: CommonRequestParams & {
43 query?: any
fb72d2e1 44 rawQuery?: string
eec63bbc 45}) {
c0e8b12e 46 const req = request(options.url).get(options.path)
fb72d2e1
C
47
48 if (options.query) req.query(options.query)
49 if (options.rawQuery) req.query(options.rawQuery)
eec63bbc 50
c0e8b12e
C
51 return buildRequest(req, { contentType: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
52}
eec63bbc 53
c0e8b12e
C
54function makeHTMLRequest (url: string, path: string) {
55 return makeGetRequest({
56 url,
57 path,
58 accept: 'text/html',
59 expectedStatus: HttpStatusCode.OK_200
60 })
61}
eec63bbc 62
c0e8b12e
C
63function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
64 return makeGetRequest({
65 url,
66 path,
ba2684ce 67 expectedStatus,
c0e8b12e
C
68 accept: 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8'
69 })
0e1dc3e7
C
70}
71
790c2837
C
72function makeDeleteRequest (options: CommonRequestParams & {
73 query?: any
74 rawQuery?: string
75}) {
c0e8b12e
C
76 const req = request(options.url).delete(options.path)
77
790c2837
C
78 if (options.query) req.query(options.query)
79 if (options.rawQuery) req.query(options.rawQuery)
80
c0e8b12e
C
81 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
82}
83
84function makeUploadRequest (options: CommonRequestParams & {
a1587156 85 method?: 'POST' | 'PUT'
d23dd9fb 86
a1587156 87 fields: { [ fieldName: string ]: any }
77e9f859 88 attaches?: { [ attachName: string ]: any | any[] }
0e1dc3e7 89}) {
c0e8b12e
C
90 let req = options.method === 'PUT'
91 ? request(options.url).put(options.path)
92 : request(options.url).post(options.path)
0e1dc3e7 93
c0e8b12e 94 req = buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
ac81d1a0 95
c0e8b12e 96 buildFields(req, options.fields)
0e1dc3e7 97
77e9f859 98 Object.keys(options.attaches || {}).forEach(attach => {
0e1dc3e7 99 const value = options.attaches[attach]
5cf027bd 100 if (!value) return
c0e8b12e 101
2769e297
C
102 if (Array.isArray(value)) {
103 req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1])
104 } else {
105 req.attach(attach, buildAbsoluteFixturePath(value))
106 }
0e1dc3e7
C
107 })
108
d23dd9fb 109 return req
0e1dc3e7
C
110}
111
c0e8b12e 112function makePostBodyRequest (options: CommonRequestParams & {
a1587156 113 fields?: { [ fieldName: string ]: any }
0e1dc3e7 114}) {
c0e8b12e
C
115 const req = request(options.url).post(options.path)
116 .send(options.fields)
0e1dc3e7 117
c0e8b12e 118 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
0e1dc3e7
C
119}
120
121function makePutBodyRequest (options: {
a1587156
C
122 url: string
123 path: string
124 token?: string
125 fields: { [ fieldName: string ]: any }
c0e8b12e 126 expectedStatus?: HttpStatusCode
4bbfc6c6 127}) {
c0e8b12e
C
128 const req = request(options.url).put(options.path)
129 .send(options.fields)
4bbfc6c6 130
c0e8b12e 131 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
4bbfc6c6
C
132}
133
9107d791
C
134function decodeQueryString (path: string) {
135 return decode(path.split('?')[1])
136}
137
c1bc8ee4 138function unwrapBody <T> (test: request.Test): Promise<T> {
e8bd7ce7
C
139 return test.then(res => res.body)
140}
141
c1bc8ee4
C
142function unwrapText (test: request.Test): Promise<string> {
143 return test.then(res => res.text)
144}
145
0305db28
JB
146function unwrapBodyOrDecodeToJSON <T> (test: request.Test): Promise<T> {
147 return test.then(res => {
148 if (res.body instanceof Buffer) {
7dd7ff4c
C
149 try {
150 return JSON.parse(new TextDecoder().decode(res.body))
151 } catch (err) {
152 console.error('Cannot decode JSON.', res.body)
153 throw err
154 }
0305db28
JB
155 }
156
157 return res.body
158 })
159}
160
161function unwrapTextOrDecode (test: request.Test): Promise<string> {
162 return test.then(res => res.text || new TextDecoder().decode(res.body))
163}
164
0e1dc3e7
C
165// ---------------------------------------------------------------------------
166
167export {
e032aec9 168 makeHTMLRequest,
0e1dc3e7 169 makeGetRequest,
9107d791 170 decodeQueryString,
ac81d1a0 171 makeUploadRequest,
0e1dc3e7 172 makePostBodyRequest,
eec63bbc 173 makePutBodyRequest,
4bbfc6c6 174 makeDeleteRequest,
09209296 175 makeRawRequest,
2d1ad5b9 176 makeActivityPubGetRequest,
c1bc8ee4 177 unwrapBody,
0305db28
JB
178 unwrapTextOrDecode,
179 unwrapBodyOrDecodeToJSON,
c0e8b12e
C
180 unwrapText
181}
182
183// ---------------------------------------------------------------------------
184
185function buildRequest (req: request.Test, options: CommonRequestParams) {
186 if (options.contentType) req.set('Accept', options.contentType)
187 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
188 if (options.range) req.set('Range', options.range)
189 if (options.accept) req.set('Accept', options.accept)
190 if (options.host) req.set('Host', options.host)
191 if (options.redirects) req.redirects(options.redirects)
c0e8b12e
C
192 if (options.xForwardedFor) req.set('X-Forwarded-For', options.xForwardedFor)
193 if (options.type) req.type(options.type)
194
195 Object.keys(options.headers || {}).forEach(name => {
196 req.set(name, options.headers[name])
197 })
198
cbdbee80
F
199 return req.expect((res) => {
200 if (options.expectedStatus && res.status !== options.expectedStatus) {
201 throw new Error(`Expected status ${options.expectedStatus}, got ${res.status}. ` +
b65f5367 202 `\nThe server responded: "${res.body?.error ?? res.text}".\n` +
cbdbee80
F
203 'You may take a closer look at the logs. To see how to do so, check out this page: ' +
204 'https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/development/tests.md#debug-server-logs')
205 }
206 return res
207 })
c0e8b12e
C
208}
209
210function buildFields (req: request.Test, fields: { [ fieldName: string ]: any }, namespace?: string) {
211 if (!fields) return
212
213 let formKey: string
214
215 for (const key of Object.keys(fields)) {
216 if (namespace) formKey = `${namespace}[${key}]`
217 else formKey = key
218
219 if (fields[key] === undefined) continue
220
221 if (Array.isArray(fields[key]) && fields[key].length === 0) {
63436fc5 222 req.field(key, [])
c0e8b12e
C
223 continue
224 }
225
226 if (fields[key] !== null && typeof fields[key] === 'object') {
227 buildFields(req, fields[key], formKey)
228 } else {
229 req.field(formKey, fields[key])
230 }
231 }
0e1dc3e7 232}