Skip to content

Release 0.32.0 #2218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fab0c32
UAI Dashboard (#2189)
ChristopherChudzicki Apr 10, 2025
70b73c4
backpopulate edx files by learning_resource_id (#2174)
abeglova Apr 11, 2025
5b2569f
Add OpenTelemetry Config (#2194)
shaidar Apr 11, 2025
65811d5
enrollments dashboard expand collapse (#2195)
gumaerc Apr 11, 2025
10ccd37
fix(deps): update dependency ruff to v0.11.5 (#2197)
renovate[bot] Apr 12, 2025
d129de8
fix(deps): update dependency litellm to v1.65.7 (#2199)
renovate[bot] Apr 12, 2025
4f82990
multi page marketing site scraping (#2196)
shanbady Apr 14, 2025
ec555e8
fix(deps): update dependency html2text to v2025 (#2207)
renovate[bot] Apr 15, 2025
7ed922a
fix some hardcoded dates (#2208)
ChristopherChudzicki Apr 15, 2025
2a73c52
fix(deps): update dependency litellm to v1.66.1 (#2205)
renovate[bot] Apr 16, 2025
620e825
Rename the realm file to work under keycloak 26.2+ (#2209)
rhysyngsun Apr 17, 2025
c518814
add email settings and unenroll dialogs as default context menu items…
gumaerc Apr 22, 2025
938c265
Reduce memory usage for nextjs in dev mode (#2211)
rhysyngsun Apr 22, 2025
01ece7a
redirect new users to onboarding (#2204)
shanbady Apr 25, 2025
a06ae69
chore(deps): update yarn to v4.9.0 (#2198)
renovate[bot] Apr 25, 2025
0b72b05
Drawer/chat accessibility fixes (#2214)
jonkafton Apr 25, 2025
2d3e2ec
Increase local dev container session lifespan to 2 weeks (#2212)
mbertrand Apr 28, 2025
b7148ea
[pre-commit.ci] pre-commit autoupdate (#2203)
pre-commit-ci[bot] Apr 28, 2025
e4b8e3d
add a loading state to DashboardCard (#2216)
gumaerc Apr 29, 2025
e8596b6
Merge branch 'release'
odlbot Apr 29, 2025
103adbe
Release 0.32.0
odlbot Apr 29, 2025
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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ repos:
- --exclude-files
- "config/keycloak/tls/*"
- --exclude-files
- "config/keycloak/realms/default-realm.json"
- "config/keycloak/realms/ol-local-realm.json"
additional_dependencies: ["gibberish-detector"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.11.4"
rev: "v0.11.6"
hooks:
- id: ruff-format
- id: ruff
Expand Down
935 changes: 0 additions & 935 deletions .yarn/releases/yarn-4.8.1.cjs

This file was deleted.

948 changes: 948 additions & 0 deletions .yarn/releases/yarn-4.9.0.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ enableGlobalCache: false

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.8.1.cjs
yarnPath: .yarn/releases/yarn-4.9.0.cjs

# https://github.com/vitejs/vite-plugin-react-swc/issues/74#issuecomment-1520484130
# https://github.com/swc-project/swc/issues/5616#issuecomment-1265639797
Expand Down
52 changes: 39 additions & 13 deletions README-keycloak.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
# Keycloak and APISIX Integration

The "docker-compose.services.yml" file includes Keycloak and APISIX containers that you can use for authentication instead of spinning up separate ones or using the deployed instances. It's not enabled by default, but you can run it if you prefer not to run your own Keycloak/APISIX instances.
The "docker-compose.services.yml" file includes Keycloak and APISIX containers
that you can use for authentication instead of spinning up separate ones or
using the deployed instances. It's not enabled by default, but you can run it if
you prefer not to run your own Keycloak/APISIX instances.

## Default Settings

There are some defaults that are part of this.

_SSL Certificate_: There's a self-signed cert that's in `config/keycloak/tls` - if you'd rather set up your own (or you have a real cert or something to use), you can drop the PEM files in there. See the README there for info.
_SSL Certificate_: There's a self-signed cert that's in `config/keycloak/tls` -
if you'd rather set up your own (or you have a real cert or something to use),
you can drop the PEM files in there. See the README there for info.

_Realm_: There's a `default-realm.json` in `config/keycloak` that will get loaded by Keycloak when it starts up, and will set up a realm for you with some users and a client so you don't have to set it up yourself. The realm it creates is called `ol-local`.
_Realm_: There's a `ol-local-realm.json` in `config/keycloak` that will get
loaded by Keycloak when it starts up, and will set up a realm for you with some
users and a client so you don't have to set it up yourself. The realm it creates
is called `ol-local`.

The users it sets up are:

Expand All @@ -18,19 +26,37 @@ The users it sets up are:
| `[email protected]` | `prof` |
| `[email protected]` | `admin` |

The client it sets up is called `apisix`. You can change the passwords and get the secret in the admin.
The client it sets up is called `apisix`. You can change the passwords and get
the secret in the admin.

## Making it Work

The Keycloak instance is part of the `keycloak` profile in the Composer file, so if you want to interact with it, you'll need to run `COMPOSE_PROFILES=backend,frontend,keycloak,apisix docker compose up`. (If you start the app without the profile, you can still start Keycloak later by specifying the profile.)
The Keycloak instance is part of the `keycloak` profile in the Composer file, so
if you want to interact with it, you'll need to run
`COMPOSE_PROFILES=backend,frontend,keycloak,apisix docker compose up`. (If you
start the app without the profile, you can still start Keycloak later by
specifying the profile.)

If you want to use the Keycloak and APISIX instances, follow these steps:

1. Change the value of `MITOL_API_BASE_URL` to `http://api.open.odl.local:8065` in your `shared.local.env` file.
2. Add `MITOL_NEW_USER_LOGIN_URL=http://open.odl.local:8062/onboarding` to your `shared.local.env` file
3. Copy all the env values under the "# APISIX/Keycloak " section of `backend.local.example.env` to your `backend.local.env` file. You can leave all the values as is.
Remove `DISABLE_APISIX_USER_MIDDLEWARE=False` if present in your backend.local.env file.
4. Keycloak needs to create its own database, which will only happen if you first destroy your current mit-learn database container: `docker compose down db`. If you prefer not to do this, you can manually create it by running the SQL in `config/postgres/init-keycloak.sql` in a postgres shell.
5. Start containers with the command `COMPOSE_PROFILES=backend,frontend,keycloak,apisix docker compose up`

The Keycloak and APISIX containers should start up and stay running. APISIX is on port 8065, Keycloak on port 8066. Now you should be able to log in at `https://open.odl.local:8065/login` with one of the users mentioned above, or just click "Log in" from the home page at http://open.odl.local:8062. Try logging out and back in a couple times to make sure it works.
1. Change the value of `MITOL_API_BASE_URL` to `http://api.open.odl.local:8065`
in your `shared.local.env` file.
2. Add `MITOL_NEW_USER_LOGIN_URL=http://open.odl.local:8062/onboarding` to your
`shared.local.env` file
3. Copy all the env values under the "# APISIX/Keycloak " section of
`backend.local.example.env` to your `backend.local.env` file. You can leave
all the values as is. Remove `DISABLE_APISIX_USER_MIDDLEWARE=False` if
present in your backend.local.env file.
4. Keycloak needs to create its own database, which will only happen if you
first destroy your current mit-learn database container:
`docker compose down db`. If you prefer not to do this, you can manually
create it by running the SQL in `config/postgres/init-keycloak.sql` in a
postgres shell.
5. Start containers with the command
`COMPOSE_PROFILES=backend,frontend,keycloak,apisix docker compose up`

The Keycloak and APISIX containers should start up and stay running. APISIX is
on port 8065, Keycloak on port 8066. Now you should be able to log in at
`https://open.odl.local:8065/login` with one of the users mentioned above, or
just click "Log in" from the home page at http://open.odl.local:8062. Try
logging out and back in a couple times to make sure it works.
23 changes: 23 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
Release Notes
=============

Version 0.32.0
--------------

- add a loading state to DashboardCard (#2216)
- [pre-commit.ci] pre-commit autoupdate (#2203)
- Increase local dev container session lifespan to 2 weeks (#2212)
- Drawer/chat accessibility fixes (#2214)
- chore(deps): update yarn to v4.9.0 (#2198)
- redirect new users to onboarding (#2204)
- Reduce memory usage for nextjs in dev mode (#2211)
- add email settings and unenroll dialogs as default context menu items in dashboard cards (#2210)
- Rename the realm file to work under keycloak 26.2+ (#2209)
- fix(deps): update dependency litellm to v1.66.1 (#2205)
- fix some hardcoded dates (#2208)
- fix(deps): update dependency html2text to v2025 (#2207)
- multi page marketing site scraping (#2196)
- fix(deps): update dependency litellm to v1.65.7 (#2199)
- fix(deps): update dependency ruff to v0.11.5 (#2197)
- enrollments dashboard expand collapse (#2195)
- Add OpenTelemetry Config (#2194)
- backpopulate edx files by learning_resource_id (#2174)
- UAI Dashboard (#2189)

Version 0.31.1 (Released April 29, 2025)
--------------

Expand Down
15 changes: 13 additions & 2 deletions authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from django.contrib.auth import logout
from django.shortcuts import redirect
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.http import url_has_allowed_host_and_scheme, urlencode
from django.views import View

from main import settings
Expand Down Expand Up @@ -73,4 +73,15 @@ def get(
"""
GET endpoint for logging a user in.
"""
return redirect(get_redirect_url(request))
redirect_url = get_redirect_url(request)
if not request.user.is_anonymous:
profile = request.user.profile
if (
not profile.completed_onboarding
and request.GET.get("skip_onboarding", "0") == "0"
):
params = urlencode({"next": redirect_url})
redirect_url = f"{settings.MITOL_NEW_USER_LOGIN_URL}?{params}"
profile.completed_onboarding = True
profile.save()
return redirect(redirect_url)
69 changes: 68 additions & 1 deletion authentication/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import json
from base64 import b64encode
from unittest.mock import MagicMock

import pytest
from django.conf import settings
from django.test import RequestFactory
from django.urls import reverse

from authentication.views import get_redirect_url
from authentication.views import CustomLoginView, get_redirect_url


@pytest.mark.parametrize(
Expand Down Expand Up @@ -108,3 +111,67 @@ def test_custom_logout_view(mocker, client, user, is_authenticated, has_next):
client.force_login(user)
resp = client.get(f"/logout/?next={next_url}", request=mock_request)
assert resp.url == (next_url if has_next else "/app")


def test_custom_login_view_authenticated_user_with_onboarding(mocker):
"""Test CustomLoginView for an authenticated user with incomplete onboarding"""
factory = RequestFactory()
request = factory.get(reverse("login"), {"next": "/dashboard"})
request.user = MagicMock(is_anonymous=False)
request.user.profile = MagicMock(completed_onboarding=False)
mocker.patch("authentication.views.get_redirect_url", return_value="/dashboard")
mocker.patch("authentication.views.urlencode", return_value="next=/dashboard")
mocker.patch(
"authentication.views.settings.MITOL_NEW_USER_LOGIN_URL", "/onboarding"
)

response = CustomLoginView().get(request)

assert response.status_code == 302
assert response.url == "/onboarding?next=/dashboard"


def test_custom_login_view_authenticated_user_skip_onboarding(mocker):
"""Test skip_onboarding flag skips redirect to onboarding and sets completed_onboarding"""
factory = RequestFactory()
request = factory.get(
reverse("login"), {"next": "/dashboard", "skip_onboarding": "1"}
)
request.user = MagicMock(is_anonymous=False)
request.user.profile = MagicMock(completed_onboarding=False)
mocker.patch("authentication.views.get_redirect_url", return_value="/dashboard")

response = CustomLoginView().get(request)
request.user.profile.refresh_from_db()
# user should not be marked as completed onboarding
assert request.user.profile.completed_onboarding is False

assert response.status_code == 302
assert response.url == "/dashboard"


def test_custom_login_view_authenticated_user_with_completed_onboarding(mocker):
"""Test test that user who has completed onboarding is redirected to next url"""
factory = RequestFactory()
request = factory.get(reverse("login"), {"next": "/dashboard"})
request.user = MagicMock(is_anonymous=False)
request.user.profile = MagicMock(completed_onboarding=True)
mocker.patch("authentication.views.get_redirect_url", return_value="/dashboard")

response = CustomLoginView().get(request)

assert response.status_code == 302
assert response.url == "/dashboard"


def test_custom_login_view_anonymous_user(mocker):
"""Test redirect for anonymous user"""
factory = RequestFactory()
request = factory.get(reverse("login"), {"next": "/dashboard"})
request.user = MagicMock(is_anonymous=True)
mocker.patch("authentication.views.get_redirect_url", return_value="/dashboard")

response = CustomLoginView().get(request)

assert response.status_code == 302
assert response.url == "/dashboard"
2 changes: 2 additions & 0 deletions config/apisix/apisix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ routes:
ssl_verify: false
session:
secret: ${{APISIX_SESSION_SECRET_KEY}}
cookie: { lifetime = 1209600 }
logout_path: "/logout/oidc"
post_logout_redirect_uri: ${{APISIX_LOGOUT_URL}}
unauth_action: "pass"
Expand Down Expand Up @@ -52,6 +53,7 @@ routes:
ssl_verify: false
session:
secret: ${{APISIX_SESSION_SECRET_KEY}}
cookie: { lifetime = 1209600 }
logout_path: "/logout/oidc"
post_logout_redirect_uri: ${{APISIX_LOGOUT_URL}}
unauth_action: "auth"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"refreshTokenMaxReuse": 0,
"accessTokenLifespan": 300,
"accessTokenLifespanForImplicitFlow": 900,
"ssoSessionIdleTimeout": 1800,
"ssoSessionMaxLifespan": 36000,
"ssoSessionIdleTimeout": 1209600,
"ssoSessionMaxLifespan": 1209600,
"ssoSessionIdleTimeoutRememberMe": 0,
"ssoSessionMaxLifespanRememberMe": 0,
"offlineSessionIdleTimeout": 2592000,
Expand All @@ -21,8 +21,8 @@
"clientOfflineSessionIdleTimeout": 0,
"clientOfflineSessionMaxLifespan": 0,
"accessCodeLifespan": 60,
"accessCodeLifespanUserAction": 300,
"accessCodeLifespanLogin": 1800,
"accessCodeLifespanUserAction": 1209600,
"accessCodeLifespanLogin": 1209600,
"actionTokenGeneratedByAdminLifespan": 43200,
"actionTokenGeneratedByUserLifespan": 300,
"oauth2DeviceCodeLifespan": 600,
Expand Down
1 change: 0 additions & 1 deletion docker-compose.apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ services:
command:
- |
yarn install --immutable
yarn workspace ol-components storybook --no-open &
yarn watch
ports:
- "8062:8062"
Expand Down
10 changes: 6 additions & 4 deletions frontends/api/src/mitxonline/clients.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { EnrollmentsApi } from "./generated/v0/api"
import { EnrollmentsApi, ProgramsApi, CoursesApi } from "./generated/v0/api"
import axios from "axios"

const instance = axios.create({})
const axiosInstance = axios.create({})

const BASE_PATH =
process.env.NEXT_PUBLIC_MITXONLINE_API_BASE_URL?.replace(/\/+$/, "") ?? ""

const enrollmentsApi = new EnrollmentsApi(undefined, BASE_PATH, instance)
const enrollmentsApi = new EnrollmentsApi(undefined, BASE_PATH, axiosInstance)
const programsApi = new ProgramsApi(undefined, BASE_PATH, axiosInstance)
const coursesApi = new CoursesApi(undefined, BASE_PATH, axiosInstance)

export { enrollmentsApi }
export { enrollmentsApi, programsApi, coursesApi, axiosInstance }
Loading
Loading