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

Enhanced fetching of Definitions via radar-self-enrolment-definitions #18

Open
wants to merge 25 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2a1ad16
Added configurations for github data fetching
this-Aditya Aug 12, 2024
2791e0b
Added utility class to cache the data
this-Aditya Aug 12, 2024
6b61852
Work on Github client and service
this-Aditya Aug 12, 2024
bf1fdd1
Modified github client and service to fetch the definition file data
this-Aditya Aug 13, 2024
91ec90a
Modified data cache
this-Aditya Aug 13, 2024
b37925c
Added file for caching definitions file name url map
this-Aditya Aug 13, 2024
112f3d3
Added structure for definition type
this-Aditya Aug 14, 2024
1ad79be
Configured caches and github service
this-Aditya Aug 14, 2024
feb7867
Merge branch 'feat/initial-components' into fetch-definitions
this-Aditya Aug 14, 2024
2f6ce55
Added docs for classes and functions with final touches
this-Aditya Aug 14, 2024
704bd07
Now pages will fetch data from github
this-Aditya Aug 14, 2024
9fa6243
Removed unused import
this-Aditya Aug 14, 2024
1ae9911
Merge branch 'feat/initial-components' into fetch-definitions
this-Aditya Aug 21, 2024
716f90f
Added configs and types for mp projects fetching
this-Aditya Aug 21, 2024
af669e9
Verifying project names from management portal
this-Aditya Aug 21, 2024
54d25ce
Updated urls for mp
this-Aditya Aug 22, 2024
ace41bb
Added custom errors for github client
this-Aditya Aug 22, 2024
2206466
Merge branch 'fetch-definitions' into mp-verify
this-Aditya Aug 22, 2024
529ece8
Improved error handling and showing them in UI
this-Aditya Aug 22, 2024
5c6b151
PR feedback addressed
this-Aditya Aug 23, 2024
aaf01be
Formatted code and removed log
this-Aditya Aug 23, 2024
cf22a8f
Format code
this-Aditya Aug 23, 2024
5f85e34
Merge pull request #19 from RADAR-base/mp-verify
this-Aditya Aug 26, 2024
74cb53e
Created new Exception component
this-Aditya Aug 26, 2024
08064c2
Formatted exceptions are shown in eligibility and study-consent pages
this-Aditya Aug 26, 2024
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
37 changes: 37 additions & 0 deletions config/github-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const GITHUB_CONFIG = {
ORGANIZATION_NAME: "RADAR-base",
REPOSITORY_NAME:
process.env.GITHUB_REPO_NAME || "radar-self-enrolment-definitions",
DEFINITIONS_BRANCH: process.env.GITHUB_REPO_BRANCH_NAME || "test",
MAX_CONTENT_LENGTH: parseInt(
process.env.GITHUB_RESPONSE_CONTENT_LENGTH || "1000000",
10,
),
CACHE_DURATION: parseInt(
process.env.GITHUB_RESPONSE_CACHE_DURATION || "180000",
10,
),
CACHE_SIZE: parseInt(process.env.GITHUB_RESPONSE_CACHE_SIZE || "50", 10),
API_URL: "https://api.github.com",
ACCEPT_HEADER: "application/vnd.github.v3+json",
}

export const GITHUB_AUTH_CONFIG = {
GITHUB_AUTH_TOKEN: process.env.GITHUB_AUTH_TOKEN || "",
}

export const REMOTE_DEFINITIONS_CONFIG = {
CONSENT_VERSION: "v1",
ELIGIBILITY_VERSION: "v1",
STUDY_INFO_VERSION: "v1",

CONSENT_DEFINITION_FILE_NAME_CONTENT: "consent",
ELIGIBILITY_DEFINITION_FILE_NAME_CONTENT: "eligibility",
STUDY_INFO_DEFINITION_FILE_NAME_CONTENT: "study_info",
}

export const MP_CONFIG = {
BASE_URL:
process.env.MP_CONFIG_BASE_URL || "http://127.0.1.1:8080/managementportal",
PROJECTS_ENDPOINT: process.env.MP_PROJECTS_ENDPOINT || "api/public/projects",
}
92 changes: 85 additions & 7 deletions pages/eligibility.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
import { RegistrationFlow, UpdateRegistrationFlowBody } from "@ory/client"
import { AxiosError } from "axios"
import type { NextPage } from "next"
import type { GetServerSideProps, NextPage } from "next"
import Head from "next/head"
import { useRouter } from "next/router"
import { useEffect, useState } from "react"
import { MutableRefObject, useEffect, useState } from "react"
import { toast } from "react-toastify"

import { eligibilityQuestions } from "../data/eligibility-questionnaire"
import { REMOTE_DEFINITIONS_CONFIG } from "../config/github-config"
// Import render helpers
import { MarginCard, CardTitle, TextCenterButton } from "../pkg"
import FormattedExcpetion from "../pkg/ui/FormattedExcpetion"
import githubService from "../services/github-service"
import { ContentLengthError } from "../utils/errors/ContentLengthError"
import { GithubApiError } from "../utils/errors/GithubApiError"
import { MPFetchError } from "../utils/errors/MPFetchError"
import { NoContentError } from "../utils/errors/NoContentError"
import { Definition } from "../utils/structures"

interface EligibilityFormProps {
questions: any[]
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}

interface EligibilityPageProps {
definitions: string
exceptionMessage: string
exceptionStatusCode: number
}

// Renders the eligibility page
const Eligibility: NextPage = () => {
const Eligibility: NextPage<EligibilityPageProps> = ({
definitions,
exceptionMessage,
exceptionStatusCode,
}) => {
const IS_ELIGIBLE = "yes"
const router = useRouter()
const [eligibility, setEligibility] = useState<boolean>()
const questions: any[] = eligibilityQuestions
const [eligibilityQuestions, setEligibilityQuestions] = useState<
Definition[]
>([])

const checkEligibility = async (values: any) => {
// Eligibility check
Expand All @@ -33,7 +52,10 @@ const Eligibility: NextPage = () => {
if (!router.isReady) {
return
}
})
if (definitions != null) {
setEligibilityQuestions(JSON.parse(definitions) as Definition[])
}
}, [router.isReady, definitions])

const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
Expand All @@ -52,6 +74,15 @@ const Eligibility: NextPage = () => {
}
}

if (exceptionMessage) {
return (
<FormattedExcpetion tileText="An exception occurred while fetching the project or definitions">
<p>{exceptionMessage}</p>
{exceptionStatusCode && <p>Status Code: {exceptionStatusCode}</p>}
</FormattedExcpetion>
)
}

return (
<>
<Head>
Expand All @@ -60,7 +91,7 @@ const Eligibility: NextPage = () => {
{eligibility === false ? (
<NotEligibleMessage />
) : (
<EligibilityForm questions={questions} onSubmit={onSubmit} />
<EligibilityForm questions={eligibilityQuestions} onSubmit={onSubmit} />
)}
</>
)
Expand Down Expand Up @@ -103,4 +134,51 @@ const EligibilityForm: React.FC<EligibilityFormProps> = ({
</MarginCard>
)

export const getServerSideProps: GetServerSideProps = async (context) => {
const { projectId } = context.query

if (typeof projectId === "string") {
try {
const consentDefinitions: string | undefined =
await githubService.initiateFetch(
projectId,
REMOTE_DEFINITIONS_CONFIG.ELIGIBILITY_DEFINITION_FILE_NAME_CONTENT,
REMOTE_DEFINITIONS_CONFIG.ELIGIBILITY_VERSION,
)

if (consentDefinitions == undefined) return { props: {} }

return {
props: {
definitions: consentDefinitions,
},
}
} catch (error: any) {
if (
error instanceof ContentLengthError ||
error instanceof NoContentError
) {
return {
props: {
exceptionMessage: error.message,
},
}
} else if (error instanceof GithubApiError) {
return {
props: {
exceptionMessage: error.message,
exceptionStatusCode: error.statusCode,
},
}
} else {
return {
props: {
exceptionMessage: error.message,
},
}
}
}
} else return { props: {} }
}

export default Eligibility
87 changes: 82 additions & 5 deletions pages/study-consent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {
} from "@ory/client"
import { H3, P } from "@ory/themes"
import { AxiosError } from "axios"
import type { NextPage } from "next"
import type { GetServerSideProps, NextPage } from "next"
import Head from "next/head"
import Link from "next/link"
import { useRouter } from "next/router"
import { ReactNode, useEffect, useState } from "react"
import { MutableRefObject, ReactNode, useEffect, useState } from "react"

import { consentQuestions } from "../data/consent-questionnaire"
import { REMOTE_DEFINITIONS_CONFIG } from "../config/github-config"
import {
ActionCard,
CenterLink,
Expand All @@ -24,12 +24,24 @@ import {
} from "../pkg"
import { handleFlowError } from "../pkg/errors"
import ory from "../pkg/sdk"
import FormattedExcpetion from "../pkg/ui/FormattedExcpetion"
import githubService from "../services/github-service"
import { ContentLengthError } from "../utils/errors/ContentLengthError"
import { GithubApiError } from "../utils/errors/GithubApiError"
import { NoContentError } from "../utils/errors/NoContentError"
import { Definition } from "../utils/structures"

interface Props {
flow?: SettingsFlow
only?: Methods
}

interface StudyConsentPageProps {
definitions: string
exceptionMessage: string
exceptionStatusCode: number
}

function StudyConsentCard({ children }: Props & { children: ReactNode }) {
return (
<ActionCard wide className="cardMargin">
Expand All @@ -38,7 +50,11 @@ function StudyConsentCard({ children }: Props & { children: ReactNode }) {
)
}

const StudyConsent: NextPage = () => {
const StudyConsent: NextPage<StudyConsentPageProps> = ({
definitions,
exceptionMessage,
exceptionStatusCode,
}) => {
const [flow, setFlow] = useState<SettingsFlow>()

// Get ?flow=... from the URL
Expand All @@ -48,11 +64,16 @@ const StudyConsent: NextPage = () => {
const [traits, setTraits] = useState<any>()
const [consent, setConsent] = useState<any>({})

const [consentQuestions, setConsentQuestions] = useState<Definition[]>([])

useEffect(() => {
// If the router is not ready yet, or we already have a flow, do nothing.
if (!router.isReady || flow) {
return
}
if (definitions != null) {
setConsentQuestions(JSON.parse(definitions) as Definition[])
}

// If ?flow=.. was in the URL, we fetch it
if (flowId) {
Expand Down Expand Up @@ -83,7 +104,7 @@ const StudyConsent: NextPage = () => {
setConsent(traits.consent)
})
.catch(handleFlowError(router, "settings", setFlow))
}, [flowId, router, router.isReady, returnTo, flow])
}, [flowId, router, router.isReady, returnTo, flow, definitions])

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setConsent({
Expand All @@ -103,6 +124,15 @@ const StudyConsent: NextPage = () => {
},
}

if (exceptionMessage) {
return (
<FormattedExcpetion tileText="An exception occurred while fetching the project or definitions">
<p>{exceptionMessage}</p>
{exceptionStatusCode && <p>Status Code: {exceptionStatusCode}</p>}
</FormattedExcpetion>
)
}

return (
router
// On submission, add the flow ID to the URL but do not navigate. This prevents the user loosing
Expand Down Expand Up @@ -210,4 +240,51 @@ const ConsentForm: React.FC<any> = ({
)
}

export const getServerSideProps: GetServerSideProps = async (context) => {
const { projectId } = context.query

if (typeof projectId === "string") {
try {
const consentDefinitions: string | undefined =
await githubService.initiateFetch(
projectId,
REMOTE_DEFINITIONS_CONFIG.CONSENT_DEFINITION_FILE_NAME_CONTENT,
REMOTE_DEFINITIONS_CONFIG.CONSENT_VERSION,
)

if (consentDefinitions == undefined) return { props: {} }

return {
props: {
definitions: consentDefinitions,
},
}
} catch (error: any) {
if (
error instanceof ContentLengthError ||
error instanceof NoContentError
) {
return {
props: {
exceptionMessage: error.message,
},
}
} else if (error instanceof GithubApiError) {
return {
props: {
exceptionMessage: error.message,
exceptionStatusCode: error.statusCode,
},
}
} else {
return {
props: {
exceptionMessage: error.message,
},
}
}
}
} else return { props: {} }
}

export default StudyConsent
Loading
Loading