-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #358 from softwareconstruction240/324-program-config
Introduce Program Configuration (Set Live Phases / Banner Message)
- Loading branch information
Showing
15 changed files
with
409 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package edu.byu.cs.controller; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.JsonObject; | ||
import edu.byu.cs.dataAccess.ConfigurationDao; | ||
import edu.byu.cs.dataAccess.DaoService; | ||
import edu.byu.cs.dataAccess.DataAccessException; | ||
import edu.byu.cs.model.User; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import spark.Route; | ||
|
||
import java.util.ArrayList; | ||
|
||
public class ConfigController { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(SubmissionController.class); | ||
|
||
private static void logConfigChange(String changeMessage, String adminNetId) { | ||
LOGGER.info("[CONFIG] Admin %s has %s".formatted(adminNetId, changeMessage)); | ||
} | ||
|
||
public static final Route getConfigAdmin = (req, res) -> { | ||
JsonObject response = getPublicConfig(); | ||
|
||
res.status(200); | ||
return response.toString(); | ||
}; | ||
|
||
public static final Route getConfigStudent = (req, res) -> { | ||
String response = getPublicConfig().toString(); | ||
|
||
res.status(200); | ||
return response; | ||
}; | ||
|
||
private static JsonObject getPublicConfig() throws DataAccessException { | ||
ConfigurationDao dao = DaoService.getConfigurationDao(); | ||
|
||
JsonObject response = new JsonObject(); | ||
|
||
response.addProperty("bannerMessage", dao.getConfiguration(ConfigurationDao.Configuration.BANNER_MESSAGE, String.class)); | ||
response.addProperty("phases", dao.getConfiguration(ConfigurationDao.Configuration.STUDENT_SUBMISSIONS_ENABLED, String.class)); | ||
|
||
return response; | ||
} | ||
|
||
public static final Route updateLivePhases = (req, res) -> { | ||
ConfigurationDao dao = DaoService.getConfigurationDao(); | ||
|
||
JsonObject jsonObject = new Gson().fromJson(req.body(), JsonObject.class); | ||
ArrayList phasesArray = new Gson().fromJson(jsonObject.get("phases"), ArrayList.class); | ||
|
||
dao.setConfiguration(ConfigurationDao.Configuration.STUDENT_SUBMISSIONS_ENABLED, phasesArray, ArrayList.class); | ||
|
||
User user = req.session().attribute("user"); | ||
logConfigChange("set the following phases as live: %s".formatted(phasesArray), user.netId()); | ||
|
||
res.status(200); | ||
return ""; | ||
}; | ||
|
||
public static final Route updateBannerMessage = (req, res) -> { | ||
ConfigurationDao dao = DaoService.getConfigurationDao(); | ||
|
||
JsonObject jsonObject = new Gson().fromJson(req.body(), JsonObject.class); | ||
String message = new Gson().fromJson(jsonObject.get("bannerMessage"), String.class); | ||
dao.setConfiguration(ConfigurationDao.Configuration.BANNER_MESSAGE, message, String.class); | ||
|
||
User user = req.session().attribute("user"); | ||
if (message.isEmpty()) { | ||
logConfigChange("cleared the banner message", user.netId()); | ||
} else { | ||
logConfigChange("set the banner message to: '%s'".formatted(message), user.netId()); | ||
} | ||
|
||
res.status(200); | ||
return ""; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Autograder Config Info | ||
|
||
The autograder has a bunch of settings that change from time to time, but need to persist across reboots. For instance, whether a phase is enabled for student submissions, or the Canvas Course ID. Thats where the Autograder Config system comes in. | ||
|
||
The `Configuration` table in the database is a set of key value pairs. This gives us a lot of flexibility, but also the need to define how it will be used. That is what this file is for. | ||
|
||
### Live Phases | ||
The `STUDENT_SUBMISSION_ENABLED` Configuration enum has a value of an array of phases. Each phase in the array is enabled, the rest are not. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { type Config, useAppConfigStore } from '@/stores/appConfig' | ||
import { Phase } from '@/types/types' | ||
import { useAuthStore } from '@/stores/auth' | ||
|
||
export const getConfig = async ():Promise<Config> => { | ||
let path = "/api" | ||
if (useAuthStore().user?.role == 'ADMIN') { | ||
path += "/admin" | ||
} | ||
path += "/config" | ||
|
||
try { | ||
const response = await fetch(useAppConfigStore().backendUrl + path, { | ||
method: 'GET', | ||
credentials: 'include' | ||
}); | ||
|
||
return await response.json(); | ||
} catch (e) { | ||
console.error('Failed to get configuration: ', e); | ||
throw "Failed to get configuration" | ||
} | ||
} | ||
|
||
export const setBannerMessage = async (message: String): Promise<void> => { | ||
const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/config/banner', { | ||
method: 'POST', | ||
credentials: 'include', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ | ||
"bannerMessage": message, | ||
}) | ||
}); | ||
|
||
if (!response.ok) { | ||
console.error(response); | ||
throw new Error(await response.text()); | ||
} | ||
await useAppConfigStore().updateConfig(); | ||
} | ||
|
||
export const setLivePhases = async (phases: Array<Phase>): Promise<void> => { | ||
const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/config/phases', { | ||
method: 'POST', | ||
credentials: 'include', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ | ||
"phases": phases, | ||
}) | ||
}); | ||
|
||
if (!response.ok) { | ||
console.error(response); | ||
throw new Error(await response.text()); | ||
} | ||
await useAppConfigStore().updateConfig(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,34 @@ | ||
import { ref, computed } from 'vue' | ||
import { ref, type Ref } from 'vue' | ||
import { defineStore } from 'pinia' | ||
import { listOfPhases, Phase } from '@/types/types' | ||
import { getConfig } from '@/services/configService' | ||
|
||
type ImportMeta = { | ||
VITE_APP_BACKEND_URL: string | ||
} | ||
|
||
export type Config = { | ||
bannerMessage: string | ||
phases: Array<Phase> | ||
} | ||
|
||
// @ts-ignore | ||
const env: ImportMeta = import.meta.env; | ||
export const useAppConfigStore = defineStore('appConfig', () => { | ||
const backendUrl = ref<string>(env.VITE_APP_BACKEND_URL); | ||
|
||
return { backendUrl } | ||
const updateConfig = async () => { | ||
const latestConfig = await getConfig(); | ||
|
||
bannerMessage.value = latestConfig.bannerMessage | ||
for (const phase of listOfPhases() as Phase[]) { | ||
activePhaseList.value[phase] = latestConfig.phases.includes(phase); | ||
} | ||
} | ||
|
||
const bannerMessage: Ref<string> = ref<string>("") | ||
// using the enum, if phaseActivationList[phase] == true, then that phase is active | ||
const activePhaseList: Ref<boolean[]> = ref<Array<boolean>>([]) | ||
|
||
return { updateConfig, backendUrl, bannerMessage, phaseActivationList: activePhaseList } | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.