mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-02 22:00:34 +01:00
feat: closes #9048, tests for topic thumbs routes, write API schema
This commit is contained in:
@@ -76,6 +76,8 @@ paths:
|
||||
$ref: 'write/topics/tid/ignore.yaml'
|
||||
/topics/{tid}/tags:
|
||||
$ref: 'write/topics/tid/tags.yaml'
|
||||
/topics/{tid}/thumbs:
|
||||
$ref: 'write/topics/tid/thumbs.yaml'
|
||||
/posts/{pid}:
|
||||
$ref: 'write/posts/pid.yaml'
|
||||
/posts/{pid}/state:
|
||||
|
||||
146
public/openapi/write/topics/tid/thumbs.yaml
Normal file
146
public/openapi/write/topics/tid/thumbs.yaml
Normal file
@@ -0,0 +1,146 @@
|
||||
get:
|
||||
tags:
|
||||
- topics
|
||||
summary: get topic thumbnails
|
||||
description: This operation retrieves a topic's uploaded thumbnails
|
||||
parameters:
|
||||
- in: path
|
||||
name: tid
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
description: a valid topic id
|
||||
example: 1
|
||||
responses:
|
||||
'200':
|
||||
description: Thumbnails successfully retrieved
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||
response:
|
||||
type: object
|
||||
properties: {}
|
||||
post:
|
||||
tags:
|
||||
- topics
|
||||
summary: add topic thumbnail
|
||||
description: This operation adds a thumbnail to an existing topic or a draft (via a composer `uuid`)
|
||||
parameters:
|
||||
- in: path
|
||||
name: tid
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
description: a valid topic id
|
||||
example: 1
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
files:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
'200':
|
||||
description: Thumbnail successfully added
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
put:
|
||||
tags:
|
||||
- topics
|
||||
summary: migrate topic thumbnail
|
||||
description: This operation migrates a thumbnails from a topic or draft, to another tid or draft.
|
||||
parameters:
|
||||
- in: path
|
||||
name: tid
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
description: a valid topic id or draft uuid
|
||||
example: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
tid:
|
||||
type: string
|
||||
description: a valid topic id or draft uuid
|
||||
example: '1'
|
||||
responses:
|
||||
'200':
|
||||
description: Topic thumbnails migrated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||
response:
|
||||
type: object
|
||||
properties: {}
|
||||
delete:
|
||||
tags:
|
||||
- topics
|
||||
summary: remove topic thumbnail
|
||||
description: This operation removes a topic thumbnail.
|
||||
parameters:
|
||||
- in: path
|
||||
name: tid
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
description: a valid topic id
|
||||
example: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
description: Relative path to the topic thumbnail
|
||||
example: files/test.png
|
||||
responses:
|
||||
'200':
|
||||
description: Topic thumbnail removed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||
response:
|
||||
type: array
|
||||
description: A list of the topic thumbnails that still remain
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: Path to a topic thumbnail
|
||||
@@ -16,10 +16,10 @@ const uploadsController = module.exports;
|
||||
uploadsController.upload = async function (req, res, filesIterator) {
|
||||
let files = req.files.files;
|
||||
|
||||
// These checks added because of odd behaviour by request: https://github.com/request/request/issues/2445
|
||||
if (!Array.isArray(files)) {
|
||||
return res.status(500).json('invalid files');
|
||||
}
|
||||
|
||||
if (Array.isArray(files[0])) {
|
||||
files = files[0];
|
||||
}
|
||||
|
||||
54
test/api.js
54
test/api.js
@@ -99,6 +99,7 @@ describe('API', async () => {
|
||||
timestamp: Date.now(),
|
||||
}],
|
||||
});
|
||||
meta.config.allowTopicsThumbnail = 1;
|
||||
|
||||
// Create a category
|
||||
const testCategory = await categories.create({ name: 'test' });
|
||||
@@ -123,8 +124,9 @@ describe('API', async () => {
|
||||
// Create a new chat room
|
||||
await messaging.newRoom(1, [2]);
|
||||
|
||||
// Create an empty file to test DELETE /files
|
||||
// Create an empty file to test DELETE /files and thumb deletion
|
||||
fs.closeSync(fs.openSync(path.resolve(nconf.get('upload_path'), 'files/test.txt'), 'w'));
|
||||
fs.closeSync(fs.openSync(path.resolve(nconf.get('upload_path'), 'files/test.png'), 'w'));
|
||||
|
||||
const socketUser = require('../src/socket.io/user');
|
||||
const socketAdmin = require('../src/socket.io/admin');
|
||||
@@ -172,6 +174,7 @@ describe('API', async () => {
|
||||
|
||||
function generateTests(api, paths, prefix) {
|
||||
// Iterate through all documented paths, make a call to it, and compare the result body with what is defined in the spec
|
||||
const pathLib = path; // for calling path module from inside this forEach
|
||||
paths.forEach((path) => {
|
||||
const context = api.paths[path];
|
||||
let schema;
|
||||
@@ -224,13 +227,20 @@ describe('API', async () => {
|
||||
url = nconf.get('url') + (prefix || '') + testPath;
|
||||
});
|
||||
|
||||
it('should contain a valid request body (if present) with application/json type if POST/PUT/DELETE', () => {
|
||||
it('should contain a valid request body (if present) with application/json or multipart/form-data type if POST/PUT/DELETE', () => {
|
||||
if (['post', 'put', 'delete'].includes(method) && context[method].hasOwnProperty('requestBody')) {
|
||||
assert(context[method].requestBody);
|
||||
assert(context[method].requestBody.content);
|
||||
assert(context[method].requestBody.content['application/json']);
|
||||
assert(context[method].requestBody.content['application/json'].schema);
|
||||
assert(context[method].requestBody.content['application/json'].schema.properties);
|
||||
|
||||
if (context[method].requestBody.content.hasOwnProperty('application/json')) {
|
||||
assert(context[method].requestBody.content['application/json']);
|
||||
assert(context[method].requestBody.content['application/json'].schema);
|
||||
assert(context[method].requestBody.content['application/json'].schema.properties);
|
||||
} else if (context[method].requestBody.content.hasOwnProperty('multipart/form-data')) {
|
||||
assert(context[method].requestBody.content['multipart/form-data']);
|
||||
assert(context[method].requestBody.content['multipart/form-data'].schema);
|
||||
assert(context[method].requestBody.content['multipart/form-data'].schema.properties);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -242,20 +252,34 @@ describe('API', async () => {
|
||||
}
|
||||
|
||||
let body = {};
|
||||
if (context[method].hasOwnProperty('requestBody')) {
|
||||
let type = 'json';
|
||||
if (context[method].hasOwnProperty('requestBody') && context[method].requestBody.content['application/json']) {
|
||||
body = buildBody(context[method].requestBody.content['application/json'].schema.properties);
|
||||
} else if (context[method].hasOwnProperty('requestBody') && context[method].requestBody.content['multipart/form-data']) {
|
||||
type = 'form';
|
||||
}
|
||||
|
||||
try {
|
||||
// console.log(`calling ${method} ${url} with`, body);
|
||||
response = await request(url, {
|
||||
method: method,
|
||||
jar: !unauthenticatedRoutes.includes(path) ? jar : undefined,
|
||||
json: true,
|
||||
headers: headers,
|
||||
qs: qs,
|
||||
body: body,
|
||||
});
|
||||
if (type === 'json') {
|
||||
// console.log(`calling ${method} ${url} with`, body);
|
||||
response = await request(url, {
|
||||
method: method,
|
||||
jar: !unauthenticatedRoutes.includes(path) ? jar : undefined,
|
||||
json: true,
|
||||
headers: headers,
|
||||
qs: qs,
|
||||
body: body,
|
||||
});
|
||||
} else if (type === 'form') {
|
||||
response = await new Promise((resolve, reject) => {
|
||||
helpers.uploadFile(url, pathLib.join(__dirname, './files/test.png'), {}, jar, csrfToken, function (err, res, body) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(body);
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
assert(!e, `${method.toUpperCase()} ${path} resolved with ${e.message}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user