Group sessions can now be properly started

This commit is contained in:
neviyn 2019-02-19 16:05:19 +00:00
parent cd6aba097d
commit 2676307170
5 changed files with 185 additions and 38 deletions

View File

@ -40,7 +40,7 @@ class CustomWebSecurityConfigurerAdapter : WebSecurityConfigurerAdapter() {
@Throws(Exception::class) @Throws(Exception::class)
override fun configure(http: HttpSecurity) { override fun configure(http: HttpSecurity) {
http.csrf().disable().authorizeRequests() http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/site", "/api/tutor", "/api/observation").authenticated() .antMatchers(HttpMethod.POST, "/api/site", "/api/tutor", "/api/observation", "/api/grpob/start").authenticated()
.anyRequest().permitAll() .anyRequest().permitAll()
.and() .and()
.httpBasic() .httpBasic()

View File

@ -15,6 +15,6 @@ class WebSocketConfig : WebSocketMessageBrokerConfigurer {
} }
override fun registerStompEndpoints(registry: StompEndpointRegistry) { override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/websocket").withSockJS() registry.addEndpoint("/websocket").setAllowedOrigins("http://localhost:8080", "http://127.0.0.1:8080").withSockJS()
} }
} }

View File

@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import uk.co.neviyn.observationdatabase.GroupObservation import uk.co.neviyn.observationdatabase.GroupObservation
@ -22,6 +23,7 @@ import uk.co.neviyn.observationdatabase.SiteRepository
import uk.co.neviyn.observationdatabase.TutorRepository import uk.co.neviyn.observationdatabase.TutorRepository
import java.net.Inet4Address import java.net.Inet4Address
import java.net.NetworkInterface import java.net.NetworkInterface
import javax.validation.Valid
@RestController @RestController
@RequestMapping("/api/grpob") @RequestMapping("/api/grpob")
@ -45,7 +47,7 @@ class GroupSessionController {
* Start a new Group Observation session * Start a new Group Observation session
*/ */
@PostMapping("/start") @PostMapping("/start")
fun startGroupObservation(initData: GroupObservationInit): Map<String, String> { fun startGroupObservation(@Valid @RequestBody initData: GroupObservationInit): Map<String, String> {
val site = siteRepository.findById(initData.site) val site = siteRepository.findById(initData.site)
val tutors = tutorRepository.findAllById(initData.tutors).toSet() val tutors = tutorRepository.findAllById(initData.tutors).toSet()
if (!site.isPresent) { if (!site.isPresent) {
@ -65,8 +67,12 @@ class GroupSessionController {
*/ */
@GetMapping("/recover") @GetMapping("/recover")
fun reconnectToGroupObservation(): Map<String, Any> { fun reconnectToGroupObservation(): Map<String, Any> {
logger.debug("Previous group observation requested") if (GroupSessionManager.isValid()) {
return getConnectionDetails().plus(mapOf("id" to GroupSessionManager.sessionId.toString(), "observations" to GroupSessionManager.observations)) logger.info("Previous group observation requested, current id ${GroupSessionManager.sessionId}")
return getConnectionDetails().plus(mapOf("id" to GroupSessionManager.sessionId.toString(), "observations" to GroupSessionManager.observations))
}
logger.info("Tried to recover a session but no session is active")
return mapOf("error" to "No session currently active")
} }
/** /**

View File

@ -11,7 +11,7 @@
</b-row> </b-row>
<b-row v-else> <b-row v-else>
<b-col> <b-col>
<b-form id="submission-form" novalidate> <b-form id="submission-form" novalidate @submit="onSubmit">
<b-row align-h="center"> <b-row align-h="center">
<b-col> <b-col>
<b-form-group label="Site"> <b-form-group label="Site">
@ -43,17 +43,65 @@
</b-form-group> </b-form-group>
</b-col> </b-col>
</b-row> </b-row>
<b-row
v-for="(item, index) in scenarioTitles"
v-bind:key="index"
class="border bottom-buffer"
fluid
>
<b-col>
<b-form-input
v-model="item.data"
type="text"
placeholder="Enter scenario description."
></b-form-input>
</b-col>
<b-col cols="1">
<b-button v-on:click="scenarioTitles.splice(index, 1)" variant="danger">
<b>Delete</b>
</b-button>
</b-col>
</b-row>
<b-row align-h="center">
<b-col>
<b-button
v-on:click="scenarioTitles.push({data:''})"
size="lg"
variant="primary"
>Add Another Scenario</b-button>
</b-col>
</b-row>
<b-row>
<b-col>
<br>
</b-col>
</b-row>
<b-row align-h="center"> <b-row align-h="center">
<b-col> <b-col>
<b-button type="submit" size="lg" variant="primary">Start</b-button> <b-button type="submit" size="lg" variant="primary">Start</b-button>
</b-col> </b-col>
<b-col> <b-col>
<b-button size="lg" variant="secondary">Connect to Previous Session</b-button> <b-button
size="lg"
variant="secondary"
v-on:click="connectToPrevious()"
>Connect to Previous Session</b-button>
</b-col> </b-col>
</b-row> </b-row>
</b-form> </b-form>
</b-col> </b-col>
</b-row> </b-row>
<b-modal
id="submissionModal"
ref="submissionModal"
title="Enter password to confirm submission"
@ok="handleOk"
@shown="clearPassword"
>
<form @submit.stop.prevent="handleSubmit">
<b-form-input type="password" placeholder="Enter password" v-model="submitPassword"></b-form-input>
</form>
</b-modal>
</b-container> </b-container>
</template> </template>
<script> <script>
@ -75,7 +123,7 @@ export default {
tutors: null, tutors: null,
siteOptions: [], siteOptions: [],
tutorOptions: [], tutorOptions: [],
scenarioTitles: null, scenarioTitles: [{ data: "" }, { data: "" }, { data: "" }],
type: null, type: null,
submitPassword: null submitPassword: null
}; };
@ -88,15 +136,6 @@ export default {
}, },
mounted() { mounted() {
console.log("loading"); console.log("loading");
var self = this;
this.stompclient.connect(
[],
function() {
self.stompclient.subscribe("/ws/observations", function(incomingData) {
self.data = incomingData.content.observations;
});
}
);
Vue.axios Vue.axios
.get("/site") .get("/site")
.then(response => { .then(response => {
@ -104,39 +143,112 @@ export default {
}) })
.catch(error => { .catch(error => {
if (error.response.status === 404) { if (error.response.status === 404) {
this.$router.push("/dberror"); //this.$router.push("/dberror");
return; return;
} }
}); });
}, },
methods: { methods: {
startSession: function() { startSession: function() {
var self = this;
let axiosConfig = { let axiosConfig = {
auth: { auth: {
username: "admin", username: "admin",
password: this.submitPassword password: this.submitPassword
} }
}; };
console.log({
site: self.site,
tutors: self.tutors,
scenarioTitles: self.scenarioTitles.map(x => x.data),
type: self.type
});
Vue.axios Vue.axios
.post( .post(
"/grpob/start", "/grpob/start",
{ {
site: this.site, site: self.site,
tutors: this.tutors, tutors: self.tutors,
scenarioTitles: this.whom, scenarioTitles: self.scenarioTitles.map(x => x.data),
type: this.type type: self.type
}, },
axiosConfig axiosConfig
) )
.then(function(response) { .then(function(response) {
console.log(response); console.log(response);
this.qrdata = if ('error' in response.data) {
"http://" + response.ip + ":" + response.port + "/#/" + response.id; } else {
let rdata = response.data;
self.qrdata = `http://${rdata.ip}:${rdata.port}/#/groupsession/${
rdata.id
}`;
self.active = true;
self.stompclient = webstomp.over(
new SockJS("http://127.0.0.1:8080/websocket", {
heartbeat: false
})
);
self.stompclient.connect(
[],
function() {
self.stompclient.subscribe("/ws/observations", function(
incomingData
) {
self.data = incomingData.content.observations;
});
}
);
self.$refs.submissionModal.hide();
self.clearPassword();
}
}) })
.catch(function(error) { .catch(function(error) {
self.active = false;
console.log(error); console.log(error);
}); });
}, },
connectToPrevious: function() {
var self = this;
Vue.axios
.get("/grpob/recover")
.then(function(response) {
console.log(response);
if ('error' in response.data) {
} else {
let rdata = response.data;
self.data = rdata.observations;
self.qrdata = `http://${rdata.ip}:${rdata.port}/#/groupsession/${
rdata.id
}`;
console.log(self.active);
self.active = true;
console.log(self.active);
self.stompclient = webstomp.over(
new SockJS("http://127.0.0.1:8080/websocket", {
heartbeat: false
})
);
self.stompclient.connect(
[],
function() {
self.stompclient.subscribe("/ws/observations", function(
incomingData
) {
self.data = incomingData.content.observations;
});
}
);
self.$refs.submissionModal.hide();
self.clearPassword();
}
})
.catch(function(error) {
if (error.response.status === 404) {
this.$router.push("/dberror");
return;
}
});
},
getTutors: function() { getTutors: function() {
if (this.site != null) { if (this.site != null) {
Vue.axios Vue.axios
@ -151,6 +263,28 @@ export default {
} }
}); });
} }
},
onSubmit: function(e) {
e.preventDefault();
e.stopPropagation();
var form = document.getElementById("submission-form");
if (form.checkValidity()) {
this.showModal();
}
form.classList.add("was-validated");
},
showModal() {
this.$refs.submissionModal.show();
},
clearPassword() {
this.submitPassword = null;
},
handleOk(evt) {
// Prevent modal from closing
evt.preventDefault();
if (this.submitPassword !== null) {
this.startSession();
}
} }
}, },
beforeDestroy() { beforeDestroy() {

View File

@ -1,6 +1,12 @@
<template> <template>
<b-container fluid> <b-container fluid>
<b-container <b-container v-if="!valid">
<p>Getting session data from server</p>
</b-container>
<b-container v-else-if="scenarios.length === 0">
<p>No scenarios defined for this session, please setup a new group session</p>
</b-container>
<b-container v-else
v-for="(item, index) in scenarios" v-for="(item, index) in scenarios"
v-bind:key="index" v-bind:key="index"
class="border bottom-buffer" class="border bottom-buffer"
@ -288,28 +294,31 @@ export default {
scenarios: [], scenarios: [],
totals: [0, 0, 0, 0, 0, 0, 0, 0], totals: [0, 0, 0, 0, 0, 0, 0, 0],
warningBound: 2.5, warningBound: 2.5,
submitPassword: null submitPassword: null,
valid: false
}; };
}, },
mounted() { mounted() {
/* let self = this;
if (this.id != null) { if (this.id != null) {
Vue.axios Vue.axios
.get("/grpob/valid/" + this.id) .get(`/grpob/valid/${this.id}`)
.then(response => { .then(function (response) {
response.titles.forEach(function(x) { if(response.data.titles != null) {
this.addAnotherObservation(x); response.data.titles.forEach(function(x) {
}); self.addAnotherObservation(x);
})
self.valid = true
};
}) })
.catch(error => { .catch(function(error) {
if (error.response.status === 404) { if (error.response.status === 404) {
this.$router.push("/dberror"); self.$router.push("/dberror");
return; return;
} }
console.log(error)
}); });
} }
*/
this.addAnotherObservation("Sample Entry");
}, },
methods: { methods: {
addAnotherObservation: function(newTitle) { addAnotherObservation: function(newTitle) {
@ -436,5 +445,3 @@ export default {
} }
}; };
</script> </script>
};
</script>