diff --git a/CHANGELOG.md b/CHANGELOG.md
index df5bbd71..e3e85605 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Split DrawMap commponents into Layers and DrawTools [#57](https://github.com/azavea/iow-boundary-tool/pull/57)
- Make `Roles` enum an `IntEnum` subclass [#74](https://github.com/azavea/iow-boundary-tool/pull/74)
- Drop `Role` table and refactor as choice field on User [#117](https://github.com/azavea/iow-boundary-tool/pull/117)
+- Return user information from login endpoint [#136](https://github.com/azavea/iow-boundary-tool/pull/136)
### Fixed
diff --git a/src/app/src/components/NavBar.js b/src/app/src/components/NavBar.js
index 0b976f88..6266f304 100644
--- a/src/app/src/components/NavBar.js
+++ b/src/app/src/components/NavBar.js
@@ -14,7 +14,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { ArrowLeftIcon, CogIcon, LogoutIcon } from '@heroicons/react/outline';
import apiClient from '../api/client';
import { API_URLS, NAVBAR_HEIGHT } from '../constants';
-import { logout, setSelectedUtility } from '../store/authSlice';
+import { logout, setUtilityByPwsid } from '../store/authSlice';
const NAVBAR_VARIANTS = {
DRAW: 'draw',
@@ -102,31 +102,33 @@ function ExitButton({ variant }) {
function UtilityControl({ variant }) {
const dispatch = useDispatch();
- // placeholders
- const utilities = ['Raleigh City of', 'Azavea Test Utility'];
- const selectedUtility =
- useSelector(state => state.auth.selectedUtility) || utilities[0];
+ const utilities = useSelector(state => state.auth.user.utilities);
+ const utility = useSelector(state => state.auth.utility);
- return variant === NAVBAR_VARIANTS.SUBMISSION && utilities.length > 1 ? (
+ if (!utility) {
+ return null;
+ }
+
+ return variant === NAVBAR_VARIANTS.SUBMISSION && utilities?.length > 1 ? (
) : (
- {selectedUtility}
+ {utility.name}
);
}
diff --git a/src/app/src/components/PrivateRoute.js b/src/app/src/components/PrivateRoute.js
index f006ecf0..cac764ad 100644
--- a/src/app/src/components/PrivateRoute.js
+++ b/src/app/src/components/PrivateRoute.js
@@ -7,7 +7,7 @@ import { API_URLS } from '../constants';
import { login, setLocationBeforeAuth } from '../store/authSlice';
export default function PrivateRoute({ children }) {
- const signedIn = useSelector(state => state.auth.signedIn);
+ const user = useSelector(state => state.auth.user);
const location = useLocation();
const navigate = useNavigate();
const dispatch = useDispatch();
@@ -17,12 +17,12 @@ export default function PrivateRoute({ children }) {
// Log in this user if they have an authenticated session
// Handles losing Redux state on refresh
useEffect(() => {
- if (!signedIn) {
+ if (!user) {
dispatch(setLocationBeforeAuth(location));
apiClient
.get(API_URLS.LOGIN)
- .then(() => {
- dispatch(login());
+ .then(({ data: user }) => {
+ dispatch(login(user));
})
.catch(() => {
if (!locationIsLogin) {
@@ -30,7 +30,7 @@ export default function PrivateRoute({ children }) {
}
});
}
- }, [signedIn, location, locationIsLogin, navigate, dispatch]);
+ }, [user, location, locationIsLogin, navigate, dispatch]);
return locationIsLogin ? null : children;
}
diff --git a/src/app/src/components/Submissions/List.js b/src/app/src/components/Submissions/List.js
index ffd72600..738e7cb5 100644
--- a/src/app/src/components/Submissions/List.js
+++ b/src/app/src/components/Submissions/List.js
@@ -113,7 +113,9 @@ function TableRows() {
}
}
-function TableRow({ boundary: { id, location, pwsid, last_modified, status } }) {
+function TableRow({
+ boundary: { id, location, pwsid, last_modified, status },
+}) {
const navigate = useNavigate();
return (
diff --git a/src/app/src/pages/Login.js b/src/app/src/pages/Login.js
index 7eb3cf07..f8854b78 100644
--- a/src/app/src/pages/Login.js
+++ b/src/app/src/pages/Login.js
@@ -22,8 +22,8 @@ export default function Login() {
email,
password,
})
- .then(() => {
- dispatch(login());
+ .then(({ data: user }) => {
+ dispatch(login(user));
})
.catch(apiError => {
if (apiError.response?.status === API_STATUSES.REDIRECT) {
@@ -34,20 +34,20 @@ export default function Login() {
});
};
- const signedIn = useSelector(state => state.auth.signedIn);
+ const user = useSelector(state => state.auth.user);
const locationBeforeAuth = useSelector(
state => state.auth.locationBeforeAuth
);
// Upon successful sign in, redirect if specified (e.g. by /login route)
useEffect(() => {
- if (signedIn) {
+ if (user) {
navigate(locationBeforeAuth, { replace: true });
}
if (forgotPassword) {
navigate('/forgot');
}
- }, [navigate, signedIn, forgotPassword, locationBeforeAuth]);
+ }, [navigate, user, forgotPassword, locationBeforeAuth]);
return (
diff --git a/src/app/src/store/authSlice.js b/src/app/src/store/authSlice.js
index 96f102b2..d1a05f76 100644
--- a/src/app/src/store/authSlice.js
+++ b/src/app/src/store/authSlice.js
@@ -1,42 +1,37 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
- signedIn: false,
locationBeforeAuth: '/welcome',
- utilities: [],
- selectedUtility: null,
+ user: false,
+ utility: null,
};
export const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
- login: state => {
- state.signedIn = true;
+ login: (state, { payload: user }) => {
+ state.user = user;
+
+ if (user.utilities) {
+ state.utility = user.utilities[0];
+ }
},
logout: state => {
- state.signedIn = false;
+ state.user = false;
},
setLocationBeforeAuth: (state, { payload: location }) => {
if (!location.pathname.startsWith('/login')) {
state.locationBeforeAuth = location;
}
},
- setUtilities: (state, { payload: utilities }) => {
- state.utilities = utilities;
- },
- setSelectedUtility: (state, { payload: selectedUtility }) => {
- state.selectedUtility = selectedUtility;
+ setUtilityByPwsid: (state, { payload: pwsid }) => {
+ state.utility = state.user.utilities.find(u => u.pwsid === pwsid);
},
},
});
-export const {
- login,
- logout,
- setLocationBeforeAuth,
- setUtilities,
- setSelectedUtility,
-} = authSlice.actions;
+export const { login, logout, setLocationBeforeAuth, setUtilityByPwsid } =
+ authSlice.actions;
export default authSlice.reducer;
diff --git a/src/django/api/models/__init__.py b/src/django/api/models/__init__.py
index e67667f3..9c6899f8 100644
--- a/src/django/api/models/__init__.py
+++ b/src/django/api/models/__init__.py
@@ -1,5 +1,5 @@
# flake8: noqa: F401
-from .user import User, EmailAsUsernameUserManager
+from .user import User, EmailAsUsernameUserManager, Roles
from .utility import Utility
from .state import State
from .boundary import Boundary
diff --git a/src/django/api/serializers/__init__.py b/src/django/api/serializers/__init__.py
index e69de29b..a632fa7f 100644
--- a/src/django/api/serializers/__init__.py
+++ b/src/django/api/serializers/__init__.py
@@ -0,0 +1,5 @@
+# flake8: noqa: F401
+from .user import UserSerializer
+from .utility import UtilitySerializer
+from .state import StateIDSerializer
+from .boundary import BoundaryListSerializer, BoundaryDetailSerializer
diff --git a/src/django/api/serializers/state.py b/src/django/api/serializers/state.py
new file mode 100644
index 00000000..c7af3e48
--- /dev/null
+++ b/src/django/api/serializers/state.py
@@ -0,0 +1,11 @@
+from rest_framework.serializers import ModelSerializer
+
+from ..models import State
+
+
+class StateIDSerializer(ModelSerializer):
+ """Serializes State as its two letter abbreviation"""
+
+ class Meta:
+ model = State
+ fields = ["id"]
diff --git a/src/django/api/serializers/user.py b/src/django/api/serializers/user.py
new file mode 100644
index 00000000..e9cf99f2
--- /dev/null
+++ b/src/django/api/serializers/user.py
@@ -0,0 +1,12 @@
+from rest_framework.serializers import ModelSerializer
+
+from ..models import User
+from .utility import UtilitySerializer
+
+
+class UserSerializer(ModelSerializer):
+ utilities = UtilitySerializer(many=True)
+
+ class Meta:
+ model = User
+ fields = ("email", "role", "utilities")
diff --git a/src/django/api/serializers/utility.py b/src/django/api/serializers/utility.py
new file mode 100644
index 00000000..d5059a51
--- /dev/null
+++ b/src/django/api/serializers/utility.py
@@ -0,0 +1,12 @@
+from rest_framework.serializers import ModelSerializer
+
+from ..models import Utility
+from .state import StateIDSerializer
+
+
+class UtilitySerializer(ModelSerializer):
+ state = StateIDSerializer
+
+ class Meta:
+ model = Utility
+ fields = "__all__"
diff --git a/src/django/api/views/auth.py b/src/django/api/views/auth.py
index 3a3d0b12..32eea1d2 100644
--- a/src/django/api/views/auth.py
+++ b/src/django/api/views/auth.py
@@ -9,6 +9,8 @@
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
+from ..serializers import UserSerializer
+
class Login(LoginView):
permission_classes = (AllowAny,)
@@ -34,13 +36,13 @@ def post(self, request, *args, **kwargs):
login(request, user)
- return Response(status=status.HTTP_200_OK)
+ return Response(UserSerializer(user).data)
def get(self, request, *args, **kwargs):
if not request.user.is_active:
raise AuthenticationFailed("Unable to sign in")
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return Response(UserSerializer(request.user).data)
class Logout(LogoutView):
diff --git a/src/django/api/views/boundary.py b/src/django/api/views/boundary.py
index f85d77bf..2b991a71 100644
--- a/src/django/api/views/boundary.py
+++ b/src/django/api/views/boundary.py
@@ -5,11 +5,9 @@
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
-from ..models.user import Roles
-from ..models.boundary import Boundary
-from ..models.submission import Submission
+from ..models import Boundary, Roles, Submission
-from ..serializers.boundary import BoundaryListSerializer, BoundaryDetailSerializer
+from ..serializers import BoundaryListSerializer, BoundaryDetailSerializer
def get_boundary_queryset_for_user(user):