diff --git a/app/api/documents.js b/app/api/documents.js index 5eedf21..9d2550d 100644 --- a/app/api/documents.js +++ b/app/api/documents.js @@ -9,11 +9,12 @@ const packageBookmark = require("./packageBookmark"); const { getArrayQueryParam } = require("../utils/object"); const { getSearch } = require("../utils/search"); +const { optionalQuery } = require("../utils/optionalQuery"); function getContentTypes(query) { const list = getArrayQueryParam(query.content_types); if (list && list.length) { - return "&" + list.map(t => `content_types[]=${t}`).join("&"); + return "&" + list.map((t) => `content_types[]=${t}`).join("&"); } return ""; } @@ -21,7 +22,7 @@ function getContentTypes(query) { function getNotContentTypes(query) { const list = getArrayQueryParam(query.exclude_content_types); if (list && list.length) { - return "&" + list.map(t => `exclude_content_types[]=${t}`).join("&"); + return "&" + list.map((t) => `exclude_content_types[]=${t}`).join("&"); } return ""; } @@ -46,7 +47,7 @@ const validSortFields = [ "created_at", "updated_at", "content_type", - "user" + "user", ]; function getSort(query) { @@ -71,8 +72,9 @@ function canvasPath(request) { let search = getSearch(request.query); let context = getContext(request.query); let preview = getPreview(request.query); + const category = optionalQuery(request.query, "category"); - return `/api/v1/${context}/${request.query.contextId}/files?per_page=${request.query.per_page}&use_verifiers=0${content_types}${exclude_content_types}${sort}${search}${preview}`; + return `/api/v1/${context}/${request.query.contextId}/files?per_page=${request.query.per_page}&use_verifiers=0${content_types}${exclude_content_types}${sort}${search}${preview}${category}`; } const svg_re = /image\/svg/; @@ -80,7 +82,7 @@ function canvasResponseHandler(request, response, canvasResponse) { response.status(canvasResponse.statusCode); if (canvasResponse.statusCode === 200) { const files = canvasResponse.body; - const transformedFiles = files.map(file => { + const transformedFiles = files.map((file) => { // svg files come back from canvas without a thumbnail // let's use the file's url let thumbnail_url = file.thumbnail_url; @@ -103,13 +105,13 @@ function canvasResponseHandler(request, response, canvasResponse) { unlock_at: file.unlock_at, lock_at: file.lock_at, date: file.created_at, - uuid: file.uuid + uuid: file.uuid, }; }); response.send({ files: transformedFiles, - bookmark: packageBookmark(request, canvasResponse.bookmark) + bookmark: packageBookmark(request, canvasResponse.bookmark), }); } else { response.send(canvasResponse.body); diff --git a/app/api/files.js b/app/api/files.js index 6315b73..0aec18b 100644 --- a/app/api/files.js +++ b/app/api/files.js @@ -23,8 +23,9 @@ function canvasResponseHandler(request, response, canvasResponse) { response.status(canvasResponse.statusCode); if (canvasResponse.statusCode === 200) { const files = canvasResponse.body; + response.send({ - files: files.map(file => { + files: files.map((file) => { return { createdAt: file.created_at, id: file.id, @@ -35,10 +36,10 @@ function canvasResponseHandler(request, response, canvasResponse) { embed: fileEmbed(file), folderId: file.folder_id, iframeUrl: file.embedded_iframe_url, - thumbnailUrl: file.thumbnail_url + thumbnailUrl: file.thumbnail_url || file.url, }; }), - bookmark: packageBookmark(request, canvasResponse.bookmark) + bookmark: packageBookmark(request, canvasResponse.bookmark), }); } else { response.send(canvasResponse.body); diff --git a/app/utils/optionalQuery.js b/app/utils/optionalQuery.js new file mode 100644 index 0000000..0d5bade --- /dev/null +++ b/app/utils/optionalQuery.js @@ -0,0 +1,7 @@ +"use strict"; + +function optionalQuery(query, name) { + return query[name] ? `&${name}=${query[name]}` : ""; +} + +module.exports = { optionalQuery }; diff --git a/test/service/api/documents.test.js b/test/service/api/documents.test.js index e05fd3e..7862016 100644 --- a/test/service/api/documents.test.js +++ b/test/service/api/documents.test.js @@ -4,7 +4,7 @@ const { strictEqual } = require("assert"); const sinon = require("sinon"); const { canvasPath, - canvasResponseHandler + canvasResponseHandler, } = require("../../../app/api/documents"); describe("Documents API", () => { @@ -49,7 +49,7 @@ describe("Documents API", () => { contextId: id, contextType: "course", content_types: "text,application", - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -65,7 +65,7 @@ describe("Documents API", () => { contextId: id, contextType: "course", content_types: ["text", "application"], - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -81,7 +81,7 @@ describe("Documents API", () => { contextId: id, contextType: "course", exclude_content_types: "text,application", - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -97,7 +97,7 @@ describe("Documents API", () => { contextId: id, contextType: "course", exclude_content_types: ["text", "application"], - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -113,7 +113,7 @@ describe("Documents API", () => { contextId: id, contextType: "course", sort: "name", - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -130,7 +130,7 @@ describe("Documents API", () => { contextType: "course", sort: "name", order: "desc", - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -148,7 +148,7 @@ describe("Documents API", () => { sort: "name", order: "desc", search_term: "foo%20bar", // search comes in uri encoded - per_page: 50 + per_page: 50, }; const path = canvasPath({ params, query }); strictEqual( @@ -156,6 +156,32 @@ describe("Documents API", () => { `/api/v1/courses/${id}/files?per_page=50&use_verifiers=0&sort=name&order=desc&search_term=foo%20bar` ); }); + + describe("with 'category' query", () => { + let id, params, query; + + beforeEach(() => { + id = 47; + params = {}; + query = { + contextId: id, + contextType: "course", + sort: "name", + order: "desc", + search_term: "foo%20bar", // search comes in uri encoded + per_page: 50, + category: "uncategorized", + }; + }); + + it("sets the 'category' query param", () => { + const path = canvasPath({ params, query }); + strictEqual( + path, + `/api/v1/courses/47/files?per_page=50&use_verifiers=0&sort=name&order=desc&search_term=foo%20bar&category=uncategorized` + ); + }); + }); }); describe("canvasResponseHandler()", () => { @@ -167,16 +193,16 @@ describe("Documents API", () => { get: () => "canvashost", query: { contextType: "course", - contextId: "17" - } + contextId: "17", + }, }; response = { status: sinon.spy(), - send: sinon.spy() + send: sinon.spy(), }; canvasResponse = { statusCode: 200, - body: [] + body: [], }; }); @@ -208,15 +234,15 @@ describe("Documents API", () => { unlock_at: "tomorrow", lock_at: "next week", created_at: "last week", - uuid: "xyzzy" - } + uuid: "xyzzy", + }, ]; }); it("transforms the API response for course files", () => { canvasResponseHandler(request, response, canvasResponse); - sinon.assert.calledWithMatch(response.send, val => { + sinon.assert.calledWithMatch(response.send, (val) => { return ( val.bookmark === null && val.files[0].id === 1 && @@ -242,7 +268,7 @@ describe("Documents API", () => { request.query.contextType = "user"; canvasResponseHandler(request, response, canvasResponse); - sinon.assert.calledWithMatch(response.send, val => { + sinon.assert.calledWithMatch(response.send, (val) => { return ( val.bookmark === null && val.files[0].id === 1 && diff --git a/test/service/api/files.test.js b/test/service/api/files.test.js index 2ad181b..a3696a8 100644 --- a/test/service/api/files.test.js +++ b/test/service/api/files.test.js @@ -13,7 +13,7 @@ describe("Files API", () => { const query = { contextType: "course", contextId: "nomatter", - per_page: 50 + per_page: 50, }; const expectedPath = `/api/v1/folders/${id}/files?per_page=50&include[]=preview_url&use_verifiers=0`; assert.equal(canvasPath({ params, query }), expectedPath); @@ -33,7 +33,7 @@ describe("Files API", () => { contextType: "course", contextId: "nomatter", per_page: 50, - search_term: "banana" + search_term: "banana", }; const expectedPath = `/api/v1/folders/${id}/files?per_page=50&include[]=preview_url&use_verifiers=0&search_term=banana`; assert.equal(canvasPath({ params, query }), expectedPath); @@ -47,7 +47,7 @@ describe("Files API", () => { contextId: "nomatter", per_page: 50, sort: "created_at", - order: "desc" + order: "desc", }; const expectedPath = `/api/v1/folders/${id}/files?per_page=50&include[]=preview_url&use_verifiers=0&sort=created_at&order=desc`; assert.equal(canvasPath({ params, query }), expectedPath); @@ -62,11 +62,11 @@ describe("Files API", () => { request = { get: () => {} }; response = { status: sinon.spy(), - send: sinon.spy() + send: sinon.spy(), }; canvasResponse = { status: 200, - body: [] + body: [], }; }); @@ -96,14 +96,38 @@ describe("Files API", () => { url: "someurl", folder_id: 1, embedded_iframe_url: "https://canvas.com/foo/bar", - thumbnail_url: "https://canvas.com/foo/bar/thumbnail" + thumbnail_url: "https://canvas.com/foo/bar/thumbnail", }; }); + describe("when no thumbnail_url is given", () => { + beforeEach(() => { + file.thumbnail_url = undefined; + }); + + it("uses the url", () => { + canvasResponse.body = [file]; + canvasResponse.statusCode = 200; + canvasResponseHandler(request, response, canvasResponse); + assert.deepStrictEqual(response.send.firstCall.args[0].files[0], { + createdAt: "2021-08-12T18:30:53Z", + id: 47, + uuid: "123123123asdf", + type: "text/plain", + name: "Foo", + url: "someurl", + embed: { type: "scribd" }, + folderId: 1, + iframeUrl: "https://canvas.com/foo/bar", + thumbnailUrl: "someurl", + }); + }); + }); + it("creates files array property with items from response body", () => { canvasResponse.body = [{}, {}, {}]; canvasResponseHandler(request, response, canvasResponse); - response.send.calledWithMatch(val => { + response.send.calledWithMatch((val) => { return ( Array.isArray(val.files) && val.files.length === canvasResponse.body.length @@ -125,7 +149,7 @@ describe("Files API", () => { embed: { type: "scribd" }, folderId: 1, iframeUrl: "https://canvas.com/foo/bar", - thumbnailUrl: "https://canvas.com/foo/bar/thumbnail" + thumbnailUrl: "https://canvas.com/foo/bar/thumbnail", }); }); @@ -133,7 +157,7 @@ describe("Files API", () => { file.display_name = undefined; canvasResponse.body = [file]; canvasResponseHandler(request, response, canvasResponse); - response.send.calledWithMatch(val => { + response.send.calledWithMatch((val) => { return sinon.match({ name: file.filename }, val[0]); }); }); @@ -141,7 +165,7 @@ describe("Files API", () => { it("has bookmark from canvasResponse", () => { canvasResponse.bookmark = "foo"; canvasResponseHandler(request, response, canvasResponse); - response.send.calledWithMatch(val => { + response.send.calledWithMatch((val) => { return /foo/.test(val.bookmark); }); }); @@ -149,7 +173,7 @@ describe("Files API", () => { it("has null bookmark if canvasResponse does not have one", () => { canvasResponse.bookmark = undefined; canvasResponseHandler(request, response, canvasResponse); - response.send.calledWithMatch(val => { + response.send.calledWithMatch((val) => { return val.bookmark === null; }); });