-
Notifications
You must be signed in to change notification settings - Fork 1
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
Make run button functional #25
Changes from 4 commits
7d9c378
02be36f
d1d4f9c
7b146ba
52b12eb
bcb53f2
7434c9d
782a8db
f45d8e9
12b7581
8c0c4f5
0906bca
0966d2b
143063c
fbb2a05
2a03920
67ba34d
c3a4d8e
fbc712e
93ca120
501d92a
d84c44c
3e43583
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ | |
v-if="props.metadata && formData" | ||
class="inputs" | ||
role="form" | ||
:data-test-form-data="JSON.stringify(formData)" | ||
:data-test-navigate-to="navigateToData" | ||
@submit.prevent="submitForm" | ||
> | ||
|
@@ -24,7 +23,7 @@ | |
<CButtonGroup | ||
role="group" | ||
:aria-label="parameter.label" | ||
:size="screenIsLarge ? 'lg' : undefined" | ||
:size="appStore.largeScreen ? 'lg' : undefined" | ||
> | ||
<!-- This component's "v-model" prop type signature dictates we can't pass it a number. --> | ||
<CFormCheck | ||
|
@@ -38,7 +37,6 @@ | |
autocomplete="off" | ||
:label="option.label" | ||
:value="option.id" | ||
:disabled="!pageMounted" | ||
/> | ||
</CButtonGroup> | ||
</CRow> | ||
|
@@ -52,8 +50,7 @@ | |
:id="parameter.id" | ||
v-model="formData[parameter.id]" | ||
:aria-label="parameter.label" | ||
class="form-select" :class="[screenIsLarge ? 'form-select-lg' : '']" | ||
:disabled="!pageMounted" | ||
class="form-select" :class="[appStore.largeScreen ? 'form-select-lg' : '']" | ||
> | ||
<option | ||
v-for="(option) in parameter.options" | ||
|
@@ -69,9 +66,9 @@ | |
<CButton | ||
id="run-button" | ||
color="primary" | ||
:size="screenIsLarge ? 'lg' : undefined" | ||
:size="appStore.largeScreen ? 'lg' : undefined" | ||
type="submit" | ||
:disabled="formSubmitting || !pageMounted" | ||
:disabled="formSubmitting" | ||
@click="submitForm" | ||
> | ||
Run | ||
|
@@ -99,16 +96,18 @@ | |
metadataFetchError: FetchError | null | ||
}>(); | ||
|
||
// This is only a temporary piece of code, used until we implement numeric inputs. | ||
const allParametersOfImplementedTypes = computed(() => props.metadata?.parameters.filter(({ parameterType }) => parameterType !== ParameterType.Numeric)); | ||
|
||
const formData = ref( | ||
// Create a new object with keys set to the id values of the metadata.parameters array of objects, and all values set to default values. | ||
props.metadata?.parameters.reduce((acc, { id, defaultOption, options }) => { | ||
allParametersOfImplementedTypes.value?.reduce((acc, { id, defaultOption, options }) => { | ||
acc[id] = defaultOption || options[0].id; | ||
return acc; | ||
}, {} as { [key: string]: string | number }), | ||
); | ||
|
||
const appStore = useAppStore(); | ||
const { screenIsLarge } = storeToRefs(appStore); | ||
const navigateToData = ref(""); | ||
const pageMounted = ref(false); | ||
|
||
|
@@ -135,12 +134,15 @@ | |
return; | ||
}; | ||
|
||
const formDataObject = new FormData(); | ||
Object.entries(formData.value).forEach(([key, value]) => { | ||
formDataObject.append(key, value.toString()); | ||
}); | ||
|
||
formSubmitting.value = true; | ||
const response = await $fetch<NewScenarioData>("/api/scenarios", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. POST "scenarios" seems a bit odd when it's only posting a single scenario - or is the idea that it will do multiple scenarios when we do the comparison feature? (In that case it will be POSTing an array of parameters potentially..) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was imagining all of the scenario-related endpoints living under a single 'scenarios' namespace, rather than varying between 'scenario' and 'scenarios' depending on the use case. I believe in CRUD it is conventional to standardize on the plural form in route names. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's a bit uneasy. Fair enough! |
||
method: "POST", | ||
query: { // Using query instead of body because I couldn't work out how to send a body in the integration test. | ||
parameters: formData.value, | ||
}, | ||
body: formDataObject, | ||
}).catch((error: FetchError) => { | ||
console.error(error); | ||
absternator marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
|
@@ -153,7 +155,7 @@ | |
}; | ||
|
||
onMounted(() => { | ||
pageMounted.value = true; | ||
}); | ||
</script> | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -25,21 +25,20 @@ | |||||
} | ||||||
|
||||||
const appStore = useAppStore(useNuxtApp().$pinia); | ||||||
appStore.initializeAppState(); | ||||||
|
||||||
const setScreenSize = () => { | ||||||
// As this function uses window, it can only be run client-side, so we have to pass the $pinia instance | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The linkage between these clauses wasn't immediately obvious to me! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went through an adventure since writing this, submitted a PR to clarify the docs of the nuxt pinia module, and eventually the maintainer let me know that in fact "you don't need to pass the pinia instance within setup" As such, the next PR removes this, here: https://github.com/jameel-institute/daedalus-web-app/pull/27/files#diff-433a9abd4ca70520f69fc064c160bb093918ce26dda6087a8bbfdbe2895d1944 |
||||||
// to the useAppStore function: https://pinia.vuejs.org/ssr/nuxt.html#Awaiting-for-actions-in-pages | ||||||
const breakpoint = 992; // CoreUI's "lg" breakpoint | ||||||
if (window.innerWidth < breakpoint) { | ||||||
appStore.setScreenSize(false); | ||||||
appStore.largeScreen = false; | ||||||
} else { | ||||||
appStore.setScreenSize(true); | ||||||
appStore.largeScreen = true; | ||||||
} | ||||||
}; | ||||||
|
||||||
onMounted(() => { | ||||||
setScreenSize(); | ||||||
window.addEventListener("resize", setScreenSize); | ||||||
}); | ||||||
onBeforeUnmount(() => { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,12 @@ | ||
import { runScenario } from "@/server/handlers/scenarios"; | ||
import { defineRApiEventHandler } from "~/server/utils/defineRApiEventHandler"; | ||
import { defineRApiEventHandler } from "@/server/utils/defineRApiEventHandler"; | ||
import { formDataToObject } from "@/server/utils/helpers"; | ||
import type { NewScenarioResponse } from "@/types/apiResponseTypes"; | ||
import type { ParameterDict } from "@/types/apiRequestTypes"; | ||
|
||
export default defineRApiEventHandler( | ||
async (event): Promise<NewScenarioResponse> => { | ||
const query = getQuery(event); | ||
|
||
const modelParameters = JSON.parse(query.parameters as string) as ParameterDict; | ||
|
||
const newScenarioResponse = await runScenario(modelParameters, event); | ||
|
||
const formDataBody = await readFormData(event); | ||
const newScenarioResponse = await runScenario(formDataToObject(formDataBody), event); | ||
return newScenarioResponse; | ||
}, | ||
); | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { formDataToObject } from "@/server/utils/helpers"; | ||
|
||
describe("formDataToObject", () => { | ||
it("should convert FormData to an object with correct values, handling single and multiple values for the same key correctly", () => { | ||
const formData = new FormData(); | ||
formData.append("name", "John Doe"); | ||
formData.append("age", "30"); | ||
formData.append("hobby", "reading"); | ||
formData.append("hobby", "coding"); | ||
|
||
const expectedObject = { | ||
name: "John Doe", | ||
age: "30", | ||
hobby: ["reading", "coding"], | ||
}; | ||
|
||
expect(formDataToObject(formData)).toEqual(expectedObject); | ||
}); | ||
|
||
it("should return an empty object for empty FormData", () => { | ||
const formData = new FormData(); | ||
const expectedObject = {}; | ||
|
||
expect(formDataToObject(formData)).toEqual(expectedObject); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd assumed that we were just going to post JSON in the same format that the R API accepts and pass it straight through? You're saying that doesn't work (in the tests), that ordinary objects get turned into... what? Completely empty body?
It's not something to do with needing to set the content type is it? Does it default to POSTing form encoded data so it barfs on plain objects but accepts FormData? Hm, the ofetch docs suggest it should deal with that transparently and add the header itself, but it is supicious... Could try setting that header explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have found something that appears to be working - JSON stringifying the body (in the test). The oftech docs claimed that this happened automatically, but evidently not 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that still it behaves differently under test than in real life, so I had to add in a check for whether the body was being received as an object (as in real life, and in e2e tests) or a JSON parsable string (as in the integration test). I hope this is tolerable, as the price for moving away from FormData.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed. maybe try adding header
Content-Type: application/json
when doing the POST.