Skip to content
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

Integrate auth token #224

Merged
merged 2 commits into from
Nov 21, 2023
Merged
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 measure-backend/measure-go/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (u *User) getTeams() ([]map[string]string, error) {
return nil, err
}

team["teamId"] = teamId
team["id"] = teamId
team["name"] = name
team["role"] = role
teams = append(teams, team)
Expand Down
14 changes: 7 additions & 7 deletions measure-web-app/app/[teamId]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import React, { useState, useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/navigation';
import TeamSwitcher from "../components/team_switcher";
import { getAccessTokenOrRedirectToAuth, logoutIfAuthError } from "../utils/auth_utils";

export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {

enum TeamsApiStatus {
Loading,
Success,
Expand Down Expand Up @@ -44,27 +44,27 @@ export default function DashboardLayout({
];

const [teamsApiStatus, setTeamsApiStatus] = useState(TeamsApiStatus.Loading);
const [authToken, setAuthToken] = useState("abcde123");
const [teams, setTeams] = useState(emptyTeams);
const [selectedTeam, setSelectedTeam] = useState(teams[0].id)

const pathName = usePathname();
const router = useRouter();

const getTeams = async (authToken:string) => {
const getTeams = async () => {
setTeamsApiStatus(TeamsApiStatus.Loading)

const origin = "https://frosty-fog-7165.fly.dev"
const authToken = await getAccessTokenOrRedirectToAuth(router)
const origin = process.env.NEXT_PUBLIC_API_BASE_URL
const opts = {
headers: {
"Authorization": `Bearer ${authToken}`
}
};

const res = await fetch(`${origin}/teams`, opts);

if(!res.ok) {
setTeamsApiStatus(TeamsApiStatus.Error)
logoutIfAuthError(router, res)
return
}

Expand All @@ -75,8 +75,8 @@ export default function DashboardLayout({
}

useEffect(() => {
getTeams(authToken)
}, [authToken]);
getTeams()
}, []);

const onTeamChanged = (item:string) => {
const selectedTeamId = teams.find((e) => e.name === item)!.id
Expand Down
20 changes: 14 additions & 6 deletions measure-web-app/app/[teamId]/overview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import Dropdown from "@/app/components/dropdown";
import FilterPill from "@/app/components/filter_pill";
import UserFlow from "@/app/components/user_flow";
import MetricsOverview from '@/app/components/metrics_overview';
import { getAccessTokenOrRedirectToAuth, logoutIfAuthError } from '@/app/utils/auth_utils';
import { useRouter } from 'next/navigation';

export default function Overview({ params }: { params: { teamId: string } }) {
const router = useRouter()

enum AppsApiStatus {
Loading,
Success,
Expand Down Expand Up @@ -43,9 +47,10 @@ export default function Overview({ params }: { params: { teamId: string } }) {
setFormattedEndDate(new Date(endDate).toLocaleDateString());
}, [startDate, endDate]);

const getApps = async (authToken:string, teamId:string, ) => {
const getApps = async (teamId:string, ) => {
setAppsApiStatus(AppsApiStatus.Loading)

const authToken = await getAccessTokenOrRedirectToAuth(router)
const origin = process.env.NEXT_PUBLIC_API_BASE_URL
const opts = {
headers: {
Expand All @@ -56,6 +61,7 @@ export default function Overview({ params }: { params: { teamId: string } }) {
const res = await fetch(`${origin}/teams/${teamId}/apps`, opts);
if(!res.ok) {
setAppsApiStatus(AppsApiStatus.Error)
logoutIfAuthError(router, res)
return
}
const data = await res.json()
Expand All @@ -66,12 +72,13 @@ export default function Overview({ params }: { params: { teamId: string } }) {
}

useEffect(() => {
getApps("abcde123", params.teamId)
getApps(params.teamId)
}, []);

const getFilters = async (authToken:string, appId:string, ) => {
const getFilters = async (appId:string, ) => {
setFiltersApiStatus(FiltersApiStatus.Loading)

const authToken = await getAccessTokenOrRedirectToAuth(router)
const origin = process.env.NEXT_PUBLIC_API_BASE_URL
const opts = {
headers: {
Expand All @@ -81,6 +88,7 @@ export default function Overview({ params }: { params: { teamId: string } }) {

const res = await fetch(`${origin}/apps/${appId}/filters`, opts);
if(!res.ok) {
logoutIfAuthError(router, res)
setFiltersApiStatus(FiltersApiStatus.Error)
return
}
Expand All @@ -92,7 +100,7 @@ export default function Overview({ params }: { params: { teamId: string } }) {
}

useEffect(() => {
getFilters("abcde123", selectedApp)
getFilters(selectedApp)
}, [selectedApp]);

return (
Expand Down Expand Up @@ -121,9 +129,9 @@ export default function Overview({ params }: { params: { teamId: string } }) {
</div>
<div className="py-8" />
{appsApiStatus === AppsApiStatus.Success && filtersApiStatus === FiltersApiStatus.Error && <p className="text-lg font-display">Error fetching filters, please refresh page or select a different app to try again</p>}
{appsApiStatus === AppsApiStatus.Success && filtersApiStatus === FiltersApiStatus.Success && <UserFlow authToken="abcde123" appId={selectedApp} startDate={startDate} endDate={endDate} appVersion={selectedVersion} />}
{appsApiStatus === AppsApiStatus.Success && filtersApiStatus === FiltersApiStatus.Success && <UserFlow appId={selectedApp} startDate={startDate} endDate={endDate} appVersion={selectedVersion} />}
<div className="py-8" />
{appsApiStatus === AppsApiStatus.Success && filtersApiStatus === FiltersApiStatus.Success && <MetricsOverview authToken="abcde123" appId={selectedApp} startDate={startDate} endDate={endDate} appVersion={selectedVersion} />}
{appsApiStatus === AppsApiStatus.Success && filtersApiStatus === FiltersApiStatus.Success && <MetricsOverview appId={selectedApp} startDate={startDate} endDate={endDate} appVersion={selectedVersion} />}
</div>
)
}
4 changes: 2 additions & 2 deletions measure-web-app/app/auth/callback/github/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function GET(request: Request) {
}

type Team = {
teamId: string,
id: string,
name: string,
role: string,
}
Expand All @@ -51,5 +51,5 @@ export async function GET(request: Request) {
return NextResponse.redirect(errRedirectUrl, { status: 302 })
}

return NextResponse.redirect(`${requestUrl.origin}/${ownTeam.teamId}/overview`, { status: 302 })
return NextResponse.redirect(`${requestUrl.origin}/${ownTeam.id}/overview`, { status: 302 })
}
4 changes: 2 additions & 2 deletions measure-web-app/app/auth/callback/google/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function POST(request: Request) {
}

type Team = {
teamId: string,
id: string,
name: string,
role: string,
}
Expand All @@ -64,5 +64,5 @@ export async function POST(request: Request) {
return NextResponse.redirect(errRedirectUrl, { status: 302 })
}

return NextResponse.redirect(`${requestUrl.origin}/${ownTeam.teamId}/overview`, { status: 302 })
return NextResponse.redirect(`${requestUrl.origin}/${ownTeam.id}/overview`, { status: 302 })
}
4 changes: 2 additions & 2 deletions measure-web-app/app/auth/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function GET(request: Request) {
}

type Team = {
teamId: string,
id: string,
name: string,
role: string,
}
Expand All @@ -55,5 +55,5 @@ export async function GET(request: Request) {
return NextResponse.redirect(errRedirectUrl, { status: 302 })
}

return NextResponse.redirect(`${requestUrl.origin}/${ownTeam.teamId}/overview`, { status: 302 })
return NextResponse.redirect(`${requestUrl.origin}/${ownTeam.id}/overview`, { status: 302 })
}
15 changes: 10 additions & 5 deletions measure-web-app/app/components/metrics_overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import InfoCircleAppAdoption from './info_circle_app_adoption';
import InfoCircleAppSize from './info_circle_app_size';
import InfoCircleExceptionRate from './info_circle_exception_rate';
import InfoCircleAppStartTime from './info_circle_app_start_time';
import { getAccessTokenOrRedirectToAuth, logoutIfAuthError } from '@/app/utils/auth_utils';
import { useRouter } from 'next/navigation';

interface MetricsOverviewProps {
authToken: string,
appId:string,
startDate:string,
endDate:string,
Expand All @@ -20,7 +21,7 @@ export enum MetricsApiStatus {
Error
}

const MetricsOverview: React.FC<MetricsOverviewProps> = ({ authToken, appId, startDate, endDate, appVersion }) => {
const MetricsOverview: React.FC<MetricsOverviewProps> = ({ appId, startDate, endDate, appVersion }) => {

const emptyData = {
"adoption": {
Expand Down Expand Up @@ -73,9 +74,12 @@ const MetricsOverview: React.FC<MetricsOverviewProps> = ({ authToken, appId, sta
const [data, setData] = useState(emptyData);
const [metricsApiStatus, setMetricsApiStatus] = useState(MetricsApiStatus.Loading);

const getData = async (authToken:string, appId:string, startDate:string, endDate:string, appVersion:string) => {
const router = useRouter()

const getData = async (appId:string, startDate:string, endDate:string, appVersion:string) => {
setMetricsApiStatus(MetricsApiStatus.Loading)

const authToken = await getAccessTokenOrRedirectToAuth(router)
const origin = process.env.NEXT_PUBLIC_API_BASE_URL
const opts = {
headers: {
Expand All @@ -89,6 +93,7 @@ const MetricsOverview: React.FC<MetricsOverviewProps> = ({ authToken, appId, sta

if(!res.ok) {
setMetricsApiStatus(MetricsApiStatus.Error)
logoutIfAuthError(router, res)
return
}

Expand All @@ -97,8 +102,8 @@ const MetricsOverview: React.FC<MetricsOverviewProps> = ({ authToken, appId, sta
}

useEffect(() => {
getData(authToken, appId, startDate, endDate, appVersion)
}, [authToken, appId, startDate, endDate, appVersion]);
getData(appId, startDate, endDate, appVersion)
}, [appId, startDate, endDate, appVersion]);

return (
<div className="flex flex-wrap gap-16 w-5/6">
Expand Down
17 changes: 11 additions & 6 deletions measure-web-app/app/components/user_flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

import React, { useState, useEffect } from 'react';
import { ResponsiveSankey } from '@nivo/sankey'
import { getAccessTokenOrRedirectToAuth, logoutIfAuthError } from '@/app/utils/auth_utils';
import { useRouter } from 'next/navigation';

interface UserFlowProps {
authToken: string,
appId:string,
startDate:string,
endDate:string,
appVersion:string,
}

const UserFlow: React.FC<UserFlowProps> = ({ authToken, appId, startDate, endDate, appVersion }) => {
const UserFlow: React.FC<UserFlowProps> = ({ appId, startDate, endDate, appVersion }) => {
enum JourneyApiStatus {
Loading,
Success,
Expand All @@ -38,9 +39,12 @@ const UserFlow: React.FC<UserFlowProps> = ({ authToken, appId, startDate, endDat
const [journeyApiStatus, setJourneyApiStatus] = useState(JourneyApiStatus.Loading);
const [data, setData] = useState(emptyData);

const getData = async (authToken:string, appId:string, startDate:string, endDate:string, appVersion:string) => {
const router = useRouter()

const getData = async (appId:string, startDate:string, endDate:string, appVersion:string) => {
setJourneyApiStatus(JourneyApiStatus.Loading)

const authToken = await getAccessTokenOrRedirectToAuth(router)
const origin = process.env.NEXT_PUBLIC_API_BASE_URL
const opts = {
headers: {
Expand All @@ -51,9 +55,10 @@ const UserFlow: React.FC<UserFlowProps> = ({ authToken, appId, startDate, endDat
const serverFormattedStartDate = new Date(startDate).toISOString()
const serverFormattedEndDate = new Date(endDate).toISOString()
const res = await fetch(`${origin}/apps/${appId}/journey?version=${appVersion}&from=${serverFormattedStartDate}&to=${serverFormattedEndDate}`, opts);

if(!res.ok) {
setJourneyApiStatus(JourneyApiStatus.Error)
logoutIfAuthError(router, res)
return
}

Expand All @@ -62,8 +67,8 @@ const UserFlow: React.FC<UserFlowProps> = ({ authToken, appId, startDate, endDat
}

useEffect(() => {
getData(authToken, appId, startDate, endDate, appVersion)
}, [authToken, appId, startDate, endDate, appVersion]);
getData(appId, startDate, endDate, appVersion)
}, [appId, startDate, endDate, appVersion]);

return (
<div className="flex items-center justify-center border border-black text-black font-sans text-sm w-5/6 h-screen">
Expand Down
36 changes: 36 additions & 0 deletions measure-web-app/app/utils/auth_utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createClient } from '@supabase/supabase-js'
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context';

// Utility function to try and access current access token. If session retrieval
// fails for any reason, logout will be called and the user will be redirected to auth
export async function getAccessTokenOrRedirectToAuth(router: AppRouterInstance) {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

const { data: {session}, error } = await supabase.auth.getSession()

if(error) {
await supabase.auth.signOut()
router.push('/auth/logout')
return null
}

return session!.access_token;
}

// Utility function to check if API reponse has an authentication error.
// If it does, logout will be called and the user will be redirected to auth
export async function logoutIfAuthError(router: AppRouterInstance, res: Response) {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

if(res.status === 401) {
await supabase.auth.signOut()
router.push('/auth/logout')
return
}
}
4 changes: 2 additions & 2 deletions measure-web-app/utils/supabase/server.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { createBrowserClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export const createClient = () => {
const cookieStore = cookies()

return createServerClient(
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
Expand Down
Loading
Loading