Skip to content
89 changes: 87 additions & 2 deletions src/actions/__tests__/media-upload-actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
import configureStore from "redux-mock-store";
import thunk from "redux-thunk";
import flushPromises from "flush-promises";
import { getRequest } from "openstack-uicore-foundation/lib/utils/actions";
import { getMediaUpload } from "../media-upload-actions";
import {
getRequest,
snackbarErrorHandler
} from "openstack-uicore-foundation/lib/utils/actions";
import {
getAllMediaUploadTypes,
getMediaUpload
} from "../media-upload-actions";
import * as methods from "../../utils/methods";
import { MAX_PER_PAGE } from "../../utils/constants";

jest.mock("openstack-uicore-foundation/lib/utils/actions", () => ({
__esModule: true,
Expand Down Expand Up @@ -72,3 +79,81 @@ describe("getMediaUpload", () => {
expect(actionTypes).toContain("RECEIVE_MEDIA_UPLOAD");
});
});

describe("getAllMediaUploadTypes", () => {
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
const ENDPOINT = "https://api.test/api/v1/summits/42/media-upload-types";

let requestedParams;

// One row per page, named after it, so the result shows the merge order.
const mockPagedRequest = (lastPage) => {
getRequest.mockImplementation(() => (params) => () => {
requestedParams.push(params);
return Promise.resolve({
response: {
last_page: lastPage,
data: [{ id: params.page, name: `type-${params.page}` }]
}
});
});
};

beforeEach(() => {
requestedParams = [];
// getRequest is shared with the suites above; reset its call history.
getRequest.mockClear();
window.API_BASE_URL = "https://api.test";
jest.spyOn(methods, "getAccessTokenSafely").mockResolvedValue("TOKEN");
});

afterEach(() => {
jest.restoreAllMocks();
delete window.API_BASE_URL;
});

it("loads every remaining page and concatenates them in page order", async () => {
mockPagedRequest(4);
const store = mockStore({});

const result = await store.dispatch(getAllMediaUploadTypes(42));

expect(requestedParams.map((p) => p.page).sort((a, b) => a - b)).toEqual([
1, 2, 3, 4
]);
// Promise.all resolves in input order, so page order survives the fan-out.
expect(result.map((type) => type.name)).toEqual([
"type-1",
"type-2",
"type-3",
"type-4"
]);
});

it("requests only the fields the filter renders, sorted by name, through getRequest", async () => {
mockPagedRequest(1);
const store = mockStore({});

await store.dispatch(getAllMediaUploadTypes(42));

expect(requestedParams[0]).toEqual({
access_token: "TOKEN",
order: "name",
per_page: MAX_PER_PAGE,
fields: "id,name",
page: 1
});
expect(getRequest.mock.calls[0][2]).toBe(ENDPOINT);
expect(getRequest.mock.calls[0][3]).toBe(snackbarErrorHandler);
});

it("resolves to an empty list when the request fails", async () => {
getRequest.mockImplementation(() => () => () => Promise.reject());
const store = mockStore({});

const result = await store.dispatch(getAllMediaUploadTypes(42));

expect(result).toEqual([]);
});
});
51 changes: 49 additions & 2 deletions src/actions/media-upload-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* */
import T from "i18n-react/dist/i18n-react";
import URI from "urijs";
import pLimit from "p-limit";
import {
getRequest,
putRequest,
Expand All @@ -27,8 +28,14 @@ import {
fetchErrorHandler
} from "openstack-uicore-foundation/lib/utils/actions";
import debounce from "lodash/debounce";
import { getAccessTokenSafely } from "../utils/methods";
import { DEBOUNCE_WAIT, DEFAULT_PER_PAGE } from "../utils/constants";
import { getAccessTokenSafely, range } from "../utils/methods";
import {
DEBOUNCE_WAIT,
DEFAULT_PER_PAGE,
MAX_PER_PAGE,
TEN,
TWO
} from "../utils/constants";

URI.escapeQuerySpace = false;

Expand Down Expand Up @@ -142,6 +149,46 @@ export const queryMediaUploads = debounce(async (summitId, input, callback) => {
.catch(fetchErrorHandler);
}, DEBOUNCE_WAIT);

export const getAllMediaUploadTypes = (summitId) => async (dispatch) => {
const accessToken = await getAccessTokenSafely();
const endpoint = `${window.API_BASE_URL}/api/v1/summits/${summitId}/media-upload-types`;
const baseParams = {
access_token: accessToken,
order: "name",
per_page: MAX_PER_PAGE,
fields: "id,name"
};
const getPage = (page) =>
getRequest(
createAction("DUMMY"),
createAction("DUMMY"),
endpoint,
snackbarErrorHandler
)({ ...baseParams, page })(dispatch);
const limit = pLimit(TEN);

return (
getPage(1)
.then(({ response }) => {
const { last_page: lastPage, data: firstPageData } = response;
if (lastPage <= 1) return firstPageData;
// local range() is stop-INCLUSIVE: range(TWO, lastPage, 1) === [2..lastPage]
return Promise.all(
range(TWO, lastPage, 1).map((page) => limit(() => getPage(page)))
).then((responses) => {
// Promise.all preserves input order -> page-order accumulation.
const accumulated = [...firstPageData];
responses.forEach(({ response: pageResponse }) => {
accumulated.push(...pageResponse.data);
});
return accumulated;
});
})
// Swallow the rejection since message is already shown by error handler
.catch(() => [])
);
};

export const resetMediaUploadForm = () => (dispatch) => {
dispatch(createAction(RESET_MEDIA_UPLOAD_FORM)({}));
};
Expand Down
Loading
Loading