Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"moment-duration-format": "^2.3.2",
"moment-timezone": "^0.5.33",
"mui-color-input": "^9.0.0",
"openstack-uicore-foundation": "5.0.38",
"openstack-uicore-foundation": "5.0.39",
"p-limit": "^6.1.0",
"path-browserify": "^1.0.1",
"postcss-loader": "^6.2.1",
Expand Down
186 changes: 92 additions & 94 deletions src/actions/media-upload-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@ import {
createAction,
stopLoading,
startLoading,
showMessage,
showSuccessMessage,
authErrorHandler,
snackbarErrorHandler,
snackbarSuccessHandler,
escapeFilterValue,
fetchResponseHandler,
fetchErrorHandler
} from "openstack-uicore-foundation/lib/utils/actions";
import debounce from "lodash/debounce"
import history from "../history";
import debounce from "lodash/debounce";
import { getAccessTokenSafely } from "../utils/methods";
import { DEBOUNCE_WAIT, DEFAULT_PER_PAGE } from "../utils/constants";

Expand Down Expand Up @@ -89,9 +87,9 @@ export const getMediaUploads =
createAction(REQUEST_MEDIA_UPLOADS),
createAction(RECEIVE_MEDIA_UPLOADS),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types`,
authErrorHandler,
{ order, orderDir, term }
)(params)(dispatch).then(() => {
snackbarErrorHandler,
{ order, orderDir, term, perPage }
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -111,94 +109,89 @@ export const getMediaUpload = (mediaUploadId) => async (dispatch, getState) => {
null,
createAction(RECEIVE_MEDIA_UPLOAD),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types/${mediaUploadId}`,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};

export const queryMediaUploads = debounce(
async (summitId, input, callback) => {
const accessToken = await getAccessTokenSafely();
const apiUrl = URI(
`${window.API_BASE_URL}/api/v1/summits/${summitId}/media-upload-types`
);

apiUrl.addQuery("access_token", accessToken);
apiUrl.addQuery("order", "name");
apiUrl.addQuery("expand", "type");
apiUrl.addQuery("per_page", DEFAULT_PER_PAGE);

if (input) {
input = escapeFilterValue(input);
apiUrl.addQuery("filter[]", `name=@${input}`);
}
export const queryMediaUploads = debounce(async (summitId, input, callback) => {
const accessToken = await getAccessTokenSafely();
const apiUrl = URI(
`${window.API_BASE_URL}/api/v1/summits/${summitId}/media-upload-types`
);

apiUrl.addQuery("access_token", accessToken);
apiUrl.addQuery("order", "name");
apiUrl.addQuery("expand", "type");
apiUrl.addQuery("per_page", DEFAULT_PER_PAGE);

if (input) {
input = escapeFilterValue(input);
apiUrl.addQuery("filter[]", `name=@${input}`);
}

fetch(apiUrl.toString())
.then(fetchResponseHandler)
.then((json) => {
const options = [...json.data];
callback(options);
})
.catch(fetchErrorHandler);
},
DEBOUNCE_WAIT
);
fetch(apiUrl.toString())
.then(fetchResponseHandler)
.then((json) => {
const options = [...json.data];
callback(options);
})
.catch(fetchErrorHandler);
}, DEBOUNCE_WAIT);

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

export const saveMediaUpload =
(entity, noAlert = false) =>
async (dispatch, getState) => {
const { currentSummitState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;
export const saveMediaUpload = (entity) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;

dispatch(startLoading());
dispatch(startLoading());

const normalizedEntity = normalizeEntity(entity);
const params = { access_token: accessToken };
const normalizedEntity = normalizeEntity(entity);
const params = { access_token: accessToken };

if (entity.id) {
putRequest(
createAction(UPDATE_MEDIA_UPLOAD),
createAction(MEDIA_UPLOAD_UPDATED),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types/${entity.id}`,
normalizedEntity,
authErrorHandler,
entity
)(params)(dispatch).then(() => {
if (!noAlert)
dispatch(showSuccessMessage(T.translate("media_upload.saved")));
else dispatch(stopLoading());
});
} else {
const successMessage = {
title: T.translate("general.done"),
html: T.translate("media_upload.created"),
type: "success"
};

postRequest(
createAction(UPDATE_MEDIA_UPLOAD),
createAction(MEDIA_UPLOAD_ADDED),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types`,
normalizedEntity,
authErrorHandler,
entity
)(params)(dispatch).then((payload) => {
if (entity.id) {
return putRequest(
createAction(UPDATE_MEDIA_UPLOAD),
createAction(MEDIA_UPLOAD_UPDATED),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types/${entity.id}`,
normalizedEntity,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
showMessage(successMessage, () => {
history.push(
`/app/summits/${currentSummit.id}/media-uploads/${payload.response.id}`
);
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("media_upload.saved")
})
);
});
}
};
})
.finally(() => dispatch(stopLoading()));
}

return postRequest(
createAction(UPDATE_MEDIA_UPLOAD),
createAction(MEDIA_UPLOAD_ADDED),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types`,
normalizedEntity,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("media_upload.created")
})
);
})
.finally(() => dispatch(stopLoading()));
};
Comment on lines +147 to +194

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the library's request helpers to see if the promise chain rejects after errorHandler runs
fd -a -t d 'openstack-uicore-foundation' node_modules 2>/dev/null | head -1
fd -a 'actions.js' node_modules/openstack-uicore-foundation 2>/dev/null
rg -n "export (const|function) (putRequest|postRequest|deleteRequest|getRequest)" -A 30 node_modules/openstack-uicore-foundation/lib/utils/actions.js 2>/dev/null

Repository: fntechgit/summit-admin

Length of output: 737


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== actions.js outline =="
wc -l /home/jailuser/git/node_modules/openstack-uicore-foundation/lib/utils/actions.js
sed -n '1,260p' /home/jailuser/git/node_modules/openstack-uicore-foundation/lib/utils/actions.js

echo
echo "== locate media upload caller =="
fd -a 'media-upload-list-page.js' /home/jailuser/git/src /home/jailuser/git 2>/dev/null || true
rg -n "saveMediaUpload\\(|handleSave|then\\(" /home/jailuser/git/src -g 'media-upload-list-page.js' -g '*.js' 2>/dev/null | head -200

Repository: fntechgit/summit-admin

Length of output: 31358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== actions.js outline =="
wc -l /home/jailuser/git/node_modules/openstack-uicore-foundation/lib/utils/actions.js
sed -n '1,260p' /home/jailuser/git/node_modules/openstack-uicore-foundation/lib/utils/actions.js

echo
echo "== media upload caller =="
fd -a 'media-upload-list-page.js' /home/jailuser/git/src /home/jailuser/git 2>/dev/null || true
rg -n "saveMediaUpload\\(|handleSave|then\\(" /home/jailuser/git/src -g 'media-upload-list-page.js' -g '*.js' 2>/dev/null | head -200

Repository: fntechgit/summit-admin

Length of output: 37151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' /home/jailuser/git/node_modules/openstack-uicore-foundation/lib/utils/actions.js | nl -ba | sed -n '1,220p'

Repository: fntechgit/summit-admin

Length of output: 200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== openstack-uicore-foundation/lib/utils/actions.js =="
nl -ba /home/jailuser/git/node_modules/openstack-uicore-foundation/lib/utils/actions.js | sed -n '1,260p'

echo
echo "== media-upload caller references =="
rg -n "saveMediaUpload\\(|handleSave|\\.then\\(|\\.catch\\(" /home/jailuser/git/src -g '*.js' 2>/dev/null | sed -n '1,220p'

Repository: fntechgit/summit-admin

Length of output: 255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== src/actions/media-upload-actions.js =="
nl -ba /home/jailuser/git/src/actions/media-upload-actions.js | sed -n '120,230p'

echo
echo "== caller chain =="
rg -n "saveMediaUpload\\(" /home/jailuser/git/src -g '*.js' -A 6 -B 6

Repository: fntechgit/summit-admin

Length of output: 242


🌐 Web query:

openstack-uicore-foundation lib/utils/actions.js putRequest postRequest snackbarErrorHandler reject catch

💡 Result:

In the openstack-uicore-foundation library, lib/utils/actions.js serves as a central module for managing API interactions within the application's Redux-based architecture [1]. The library implements a standardized pattern for handling asynchronous HTTP requests through helper functions such as getRequest, postRequest, and putRequest [1][2]. These helpers are designed to be used in conjunction with Redux thunks to dispatch actions for the request lifecycle (starting, success, and error) [1][2]. Regarding the specific components you mentioned: 1. postRequest and putRequest: These are API utility functions located in lib/utils/actions.js [1]. They typically accept action creators for request and success states, the API endpoint, and an error handler, returning a function that executes the request when invoked with dispatch and getState [2]. 2. snackbarErrorHandler: This is a specialized error-handling function used within this utility pattern [1]. When a request fails, it is often passed to these API helpers to intercept the error—frequently handling authentication issues (such as a 401 redirect) or triggering user-facing notifications like a snackbar [1]. 3. reject and catch: These are standard components of the promise chains used within these utility functions [2]. When an API call fails, the catch block intercepts the error, passes it to the error handler (e.g., snackbarErrorHandler), and typically triggers a reject action or promise rejection to ensure the calling thunk can handle the error state appropriately [1][2]. In practice, this pattern ensures that API requests, loading states, and error messaging are handled consistently across the application [1]. Developers typically call these helpers inside their action creators, providing the necessary dispatch and state parameters [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path

paths = [
    Path('/home/jailuser/git/src/actions/media-upload-actions.js'),
    Path('/home/jailuser/git/src/pages/media_uploads/media-upload-list-page.js'),
]
for path in paths:
    print(f'== {path} ==')
    lines = path.read_text().splitlines()
    for i, line in enumerate(lines, 1):
        if path.name == 'media-upload-actions.js' and 147 <= i <= 194:
            print(f'{i:4}: {line}')
        elif path.name == 'media-upload-list-page.js' and 70 <= i <= 90:
            print(f'{i:4}: {line}')
    print()
PY

Repository: fntechgit/summit-admin

Length of output: 2823


Handle rejected save promises in the media upload flow. putRequest/postRequest reject after snackbarErrorHandler, so saveMediaUpload(...).then(...) without a .catch() can surface unhandled promise rejections on save errors. Add a catch on the caller.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/actions/media-upload-actions.js` around lines 147 - 194, The
saveMediaUpload thunk in media-upload-actions.js can reject after
snackbarErrorHandler, so callers using saveMediaUpload(...).then(...) need to
handle failures explicitly. Update the caller(s) of saveMediaUpload to add a
.catch() (or equivalent rejection handling) alongside the existing .then()
success flow, so rejected putRequest/postRequest promises do not become
unhandled rejections. Use the saveMediaUpload symbol to find the async action
and preserve the current success snackbar behavior.


export const linkToPresentationType =
(mediaUpload, presentationTypeId) => async (dispatch, getState) => {
Expand All @@ -215,8 +208,8 @@ export const linkToPresentationType =
createAction(MEDIA_UPLOAD_LINKED)({ mediaUpload }),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types/${mediaUpload.id}/presentation-types/${presentationTypeId}`,
null,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -236,8 +229,8 @@ export const unlinkFromPresentationType =
createAction(MEDIA_UPLOAD_UNLINKED)({ mediaUploadId }),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types/${mediaUploadId}/presentation-types/${presentationTypeId}`,
null,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -257,10 +250,8 @@ export const deleteMediaUpload =
createAction(MEDIA_UPLOAD_DELETED)({ mediaUploadId }),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/media-upload-types/${mediaUploadId}`,
null,
authErrorHandler
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
snackbarErrorHandler
)(params)(dispatch);
};

export const copyMediaUploads = (summitId) => async (dispatch, getState) => {
Expand All @@ -272,16 +263,23 @@ export const copyMediaUploads = (summitId) => async (dispatch, getState) => {

const params = { access_token: accessToken };

postRequest(
return postRequest(
null,
createAction(MEDIA_UPLOADS_COPIED),
`${window.API_BASE_URL}/api/v1/summits/${summitId}/media-upload-types/all/clone/${currentSummit.id}`,
null,
authErrorHandler
)(params)(dispatch).then(() => {
dispatch(stopLoading());
dispatch(getMediaUploads());
});
snackbarErrorHandler
)(params)(dispatch)
.then(() => {
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("media_upload.media_uploads_copied")
})
);
dispatch(getMediaUploads());
})
.finally(() => dispatch(stopLoading()));
};

const normalizeEntity = (entity) => {
Expand Down
Loading
Loading