Compare commits

...

6 Commits

Author SHA1 Message Date
Leangseu Kim
54c6c57b42 feat: upgraded to node v18, added .nvmrc and updated workflows 2023-06-09 09:19:12 +02:00
Adolfo R. Brandes
6722dbdce5 Merge pull request #185 from arbrandes/runtime-config-palm 2023-06-05 16:40:57 +01:00
Adolfo R. Brandes
fed06641df refactor: use getConfig 2023-05-31 12:46:23 -03:00
Adolfo R. Brandes
689b8b48f0 feat: Support runtime configuration
frontend-platform supports runtime configuration since 2.5.0 (see the PR
that introduced it[1], but it requires MFE cooperation.  This implements
just that: by avoiding making configuration values constant, it should
now be possible to change them after initialization.

Almost all changes here relate to the `LMS_BASE_URL` setting, which in
most places was treated as a constant.

[1] openedx/frontend-platform#335
2023-05-31 12:46:23 -03:00
Adolfo R. Brandes
258f4377d8 Merge pull request #181 from raccoongang/palm/fix-location-id 2023-05-15 10:37:48 -03:00
Eugene Dyudyunov
dcdc96778c fix: BadOraLocationResponse error
Refactor the locationId constant for the subdirectory-based
deployments support.

Exclude the MFE's `PUBLIC_PATH` from the constant.

The `window.location.pathname` example:
```
<PUBLIC_PATH>block-v1:oragrading+oragrading+oragrading+type@openassessment+block@ee217e897a954c1faa3b29317da0f2e7
```
Where the `PUBLIC_PATH` could be:
- `'/'` - for subdomain-based deployments (default)
- `'/mfe-specifix-public-path/'` - for subdirectory-based deployments
2023-05-12 15:37:12 +03:00
15 changed files with 60 additions and 76 deletions

View File

@@ -11,17 +11,16 @@ on:
jobs:
tests:
runs-on: ubuntu-20.04
strategy:
matrix:
node: [16]
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- name: Setup Nodejs
uses: actions/setup-node@v2
uses: actions/setup-node@v3
# Because of node 18 bug (https://github.com/nodejs/node/issues/47563), Pinning node version 18.15 until the next release of node
with:
node-version: ${{ matrix.node }}
node-version: 18.15
- name: Install dependencies
run: npm ci

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18.15

View File

@@ -1,16 +0,0 @@
const configuration = {
// BASE_URL: process.env.BASE_URL,
LMS_BASE_URL: process.env.LMS_BASE_URL,
// LOGIN_URL: process.env.LOGIN_URL,
// LOGOUT_URL: process.env.LOGOUT_URL,
// CSRF_TOKEN_API_PATH: process.env.CSRF_TOKEN_API_PATH,
// REFRESH_ACCESS_TOKEN_ENDPOINT: process.env.REFRESH_ACCESS_TOKEN_ENDPOINT,
// DATA_API_BASE_URL: process.env.DATA_API_BASE_URL,
// SECURE_COOKIES: process.env.NODE_ENV !== 'development',
// SEGMENT_KEY: process.env.SEGMENT_KEY,
// ACCESS_TOKEN_COOKIE_NAME: process.env.ACCESS_TOKEN_COOKIE_NAME,
};
const features = {};
export { configuration, features };

View File

@@ -22,7 +22,7 @@ export const ListViewBreadcrumb = ({ courseId, oraName }) => (
</Hyperlink>
<p className="py-4">
<span className="h3">{oraName}</span>
<Hyperlink className="align-middle" destination={urls.ora(courseId, locationId)}>
<Hyperlink className="align-middle" destination={urls.ora(courseId, locationId())}>
<Icon src={Launch} className="d-inline-block" />
</Hyperlink>
</p>

View File

@@ -50,7 +50,7 @@ describe('ListViewBreadcrumb component', () => {
test('ora destination', () => {
expect(
el.find(Hyperlink).at(1).props().destination,
).toEqual(urls.ora(props.courseId, constants.locationId));
).toEqual(urls.ora(props.courseId, constants.locationId()));
});
});
describe('mapStateToProps', () => {

View File

@@ -1,4 +1,4 @@
import { getConfig } from '@edx/frontend-platform';
export const routePath = `${getConfig().PUBLIC_PATH}:courseId`;
export const locationId = window.location.pathname.slice(1);
export const routePath = () => `${getConfig().PUBLIC_PATH}:courseId`;
export const locationId = () => window.location.pathname.replace(getConfig().PUBLIC_PATH, '');

View File

@@ -4,7 +4,7 @@ import * as constants from './app';
jest.unmock('./app');
jest.mock('@edx/frontend-platform', () => {
const PUBLIC_PATH = 'test-public-path';
const PUBLIC_PATH = '/test-public-path/';
return {
getConfig: () => ({ PUBLIC_PATH }),
PUBLIC_PATH,
@@ -13,12 +13,12 @@ jest.mock('@edx/frontend-platform', () => {
describe('app constants', () => {
test('route path draws from public path and adds courseId', () => {
expect(constants.routePath).toEqual(`${platform.PUBLIC_PATH}:courseId`);
expect(constants.routePath()).toEqual(`${platform.PUBLIC_PATH}:courseId`);
});
test('locationId returns trimmed pathname', () => {
const old = window.location;
window.location = { pathName: '/somePath.jpg' };
expect(constants.locationId).toEqual(window.location.pathname.slice(1));
window.location = { pathName: `${platform.PUBLIC_PATH}somePath.jpg` };
expect(constants.locationId()).toEqual(window.location.pathname.replace(platform.PUBLIC_PATH, ''));
window.location = old;
});
});

View File

@@ -15,7 +15,7 @@ import * as module from './app';
*/
export const initialize = () => (dispatch) => {
dispatch(initializeApp({
locationId,
locationId: locationId(),
onSuccess: (response) => {
dispatch(actions.app.loadIsEnabled(response.isEnabled));
dispatch(actions.app.loadOraMetadata(response.oraMetadata));

View File

@@ -25,7 +25,7 @@ describe('app thunkActions', () => {
[[dispatchedAction]] = dispatch.mock.calls;
});
it('dispatches initializeApp with locationId and onSuccess', () => {
expect(dispatchedAction.initializeApp.locationId).toEqual(locationId);
expect(dispatchedAction.initializeApp.locationId).toEqual(locationId());
expect(typeof dispatchedAction.initializeApp.onSuccess).toEqual('function');
});
describe('on success', () => {

View File

@@ -47,7 +47,7 @@ export const zipFiles = async (files, blobs, username) => {
}
const zipFile = await zipWriter.close();
const zipName = `${username}-${locationId}.zip`;
const zipName = `${username}-${locationId()}.zip`;
FileSaver.saveAs(zipFile, zipName);
};

View File

@@ -32,8 +32,8 @@ import {
* }
*/
const initializeApp = () => get(
stringifyUrl(urls.oraInitializeUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.oraInitializeUrl(), {
[paramKeys.oraLocation]: locationId(),
}),
).then(response => response.data);
@@ -48,8 +48,8 @@ const initializeApp = () => get(
* }
*/
const fetchSubmission = (submissionUUID) => get(
stringifyUrl(urls.fetchSubmissionUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.fetchSubmissionUrl(), {
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
}),
).then(response => response.data);
@@ -61,8 +61,8 @@ const fetchSubmission = (submissionUUID) => get(
* }
*/
const fetchSubmissionFiles = (submissionUUID) => get(
stringifyUrl(urls.fetchSubmissionFilesUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.fetchSubmissionFilesUrl(), {
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
}),
).then(response => response.data);
@@ -78,8 +78,8 @@ const fetchSubmissionFiles = (submissionUUID) => get(
* }
*/
const fetchSubmissionStatus = (submissionUUID) => get(
stringifyUrl(urls.fetchSubmissionStatusUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.fetchSubmissionStatusUrl(), {
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
}),
).then(response => response.data);
@@ -89,8 +89,8 @@ const fetchSubmissionStatus = (submissionUUID) => get(
* @param {string} submissionUUID
*/
const lockSubmission = (submissionUUID) => post(
stringifyUrl(urls.fetchSubmissionLockUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.fetchSubmissionLockUrl(), {
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
}),
).then(response => response.data);
@@ -100,8 +100,8 @@ const lockSubmission = (submissionUUID) => post(
* @param {string} submissionUUID
*/
const unlockSubmission = (submissionUUID) => client().delete(
stringifyUrl(urls.fetchSubmissionLockUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.fetchSubmissionLockUrl(), {
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
}),
).then(response => response.data);
@@ -112,8 +112,8 @@ const unlockSubmission = (submissionUUID) => client().delete(
*/
const batchUnlockSubmissions = (submissionUUIDs) => post(
stringifyUrl(
urls.batchUnlockSubmissionsUrl,
{ [paramKeys.oraLocation]: locationId },
urls.batchUnlockSubmissionsUrl(),
{ [paramKeys.oraLocation]: locationId() },
),
{ submissionUUIDs },
).then(response => response.data);
@@ -123,8 +123,8 @@ const batchUnlockSubmissions = (submissionUUIDs) => post(
* @param {object} gradeData - full grading submission data
*/
const updateGrade = (submissionUUID, gradeData) => post(
stringifyUrl(urls.updateSubmissionGradeUrl, {
[paramKeys.oraLocation]: locationId,
stringifyUrl(urls.updateSubmissionGradeUrl(), {
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
}),
gradeData,

View File

@@ -13,7 +13,7 @@ jest.mock('./utils', () => ({
}));
jest.mock('data/constants/app', () => ({
locationId: 'test-location-id',
locationId: () => 'test-location-id',
}));
const gradeData = 'test-grade-data';
@@ -37,10 +37,10 @@ const testAPI = ({
...otherExpected
},
}) => {
it(`returns ${method}(${urlKey}) with correct args and reoslves with response data`, () => (
it(`returns ${method}(${urlKey}) with correct args and resolves with response data`, () => (
promise.then((data) => {
expect(data[method]).toEqual({
url: stringifyUrl(urls[urlKey], urlParams),
url: stringifyUrl(urls[urlKey](), urlParams),
...otherExpected,
});
})
@@ -54,7 +54,7 @@ describe('lms service api methods', () => {
method: methodKeys.get,
expected: {
urlKey: urlKeys.oraInitializeUrl,
urlParams: { [paramKeys.oraLocation]: locationId },
urlParams: { [paramKeys.oraLocation]: locationId() },
},
});
});
@@ -65,7 +65,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.fetchSubmissionUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
},
},
@@ -78,7 +78,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.fetchSubmissionFilesUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
},
},
@@ -91,7 +91,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.fetchSubmissionStatusUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
},
},
@@ -104,7 +104,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.fetchSubmissionLockUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
},
},
@@ -117,7 +117,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.fetchSubmissionLockUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
},
},
@@ -130,7 +130,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.batchUnlockSubmissionsUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
},
data: { submissionUUIDs },
},
@@ -143,7 +143,7 @@ describe('lms service api methods', () => {
expected: {
urlKey: urlKeys.updateSubmissionGradeUrl,
urlParams: {
[paramKeys.oraLocation]: locationId,
[paramKeys.oraLocation]: locationId(),
[paramKeys.submissionUUID]: submissionUUID,
},
data: gradeData,

View File

@@ -1,20 +1,20 @@
import { StrictDict } from 'utils';
import { configuration } from 'config';
import { getConfig } from '@edx/frontend-platform';
const baseUrl = `${configuration.LMS_BASE_URL}`;
const baseUrl = () => getConfig().LMS_BASE_URL;
const api = `${baseUrl}/api/`;
const baseEsgUrl = `${api}ora_staff_grader/`;
const api = () => `${baseUrl()}/api/`;
const baseEsgUrl = () => `${api()}ora_staff_grader/`;
const oraInitializeUrl = `${baseEsgUrl}initialize`;
const fetchSubmissionUrl = `${baseEsgUrl}submission`;
const fetchSubmissionFilesUrl = `${baseEsgUrl}submission/files`;
const fetchSubmissionStatusUrl = `${baseEsgUrl}submission/status`;
const fetchSubmissionLockUrl = `${baseEsgUrl}submission/lock`;
const batchUnlockSubmissionsUrl = `${baseEsgUrl}submission/batch/unlock`;
const updateSubmissionGradeUrl = `${baseEsgUrl}submission/grade`;
const oraInitializeUrl = () => `${baseEsgUrl()}initialize`;
const fetchSubmissionUrl = () => `${baseEsgUrl()}submission`;
const fetchSubmissionFilesUrl = () => `${baseEsgUrl()}submission/files`;
const fetchSubmissionStatusUrl = () => `${baseEsgUrl()}submission/status`;
const fetchSubmissionLockUrl = () => `${baseEsgUrl()}submission/lock`;
const batchUnlockSubmissionsUrl = () => `${baseEsgUrl()}submission/batch/unlock`;
const updateSubmissionGradeUrl = () => `${baseEsgUrl()}submission/grade`;
const course = (courseId) => `${baseUrl}/courses/${courseId}`;
const course = (courseId) => `${baseUrl()}/courses/${courseId}`;
const openResponse = (courseId) => (
`${course(courseId)}/instructor#view-open_response_assessment`

View File

@@ -1,6 +1,6 @@
// The code in this file is from Segment's website:
// https://segment.com/docs/sources/website/analytics.js/quickstart/
import { configuration } from './config';
import { getConfig } from '@edx/frontend-platform';
(function () {
// Create a queue, but don't obliterate an existing one!
@@ -81,5 +81,5 @@ import { configuration } from './config';
// Load Analytics.js with your key, which will automatically
// load the tools you've enabled for your account. Boosh!
analytics.load(configuration.SEGMENT_KEY);
analytics.load(getConfig().SEGMENT_KEY);
}());

View File

@@ -113,7 +113,7 @@ jest.mock('@edx/paragon/icons', () => ({
}));
jest.mock('data/constants/app', () => ({
locationId: 'fake-location-id',
locationId: () => 'fake-location-id',
}));
jest.mock('hooks', () => ({