*: handle api errors

* Use a global error when fetching data
* Use a local error message when calling a mutating api
This commit is contained in:
Simone Gotti 2019-05-13 16:29:47 +02:00
parent 98073502e2
commit 789d0f5fcd
22 changed files with 522 additions and 173 deletions

View File

@ -43,8 +43,27 @@
</div>
</div>
</nav>
<div class="main-container container">
<router-view></router-view>
<div v-if="error" class="container">
<div class="message is-danger global-error-message">
<div class="message-body">
<nav class="level">
<div class="level-left">
<div class="level-item">
<p>Failed to fetch data: {{ error }}</p>
</div>
</div>
<div class="level-right">
<div class="level-item">
<button class="button is-danger" @click="reload()">Retry</button>
</div>
</div>
</nav>
</div>
</div>
</div>
<div v-else class="main-container container">
<router-view v-if="routerActive"></router-view>
</div>
</div>
</template>
@ -57,7 +76,12 @@ export default {
name: "App",
components: {},
computed: {
...mapGetters(["user"])
...mapGetters(["error", "user"])
},
data() {
return {
routerActive: true
};
},
watch: {
user: function(user) {
@ -68,6 +92,14 @@ export default {
});
}
}
},
// method to reload current view from https://github.com/vuejs/vue-router/issues/296#issuecomment-356530037
methods: {
reload() {
this.$store.dispatch("setError", null);
this.routerActive = false;
this.$nextTick(() => (this.routerActive = true));
}
}
};
</script>
@ -78,4 +110,8 @@ export default {
.main-container {
margin-top: 2rem;
}
.global-error-message {
margin-top: 10rem;
}
</style>

View File

@ -19,9 +19,9 @@
<div class="control">
<button class="button is-primary" @click="createProject()">Create Project</button>
</div>
<div class="control">
<button class="button is-text">Cancel</button>
</div>
</div>
<div v-if="createProjectError" class="message is-danger">
<div class="message-body">{{ createProjectError }}</div>
</div>
</div>
</template>
@ -47,6 +47,7 @@ export default {
},
data() {
return {
createProjectError: null,
user: null,
remoteSources: null,
remoteRepos: [],
@ -56,23 +57,32 @@ export default {
};
},
methods: {
resetErrors() {
this.createProjectError = null;
},
repoSelected(remoteSource, repoPath) {
this.selectedRemoteSource = remoteSource;
this.remoteRepoPath = repoPath;
},
async createProject() {
this.resetErrors();
let refArray = [this.ownertype, this.ownername];
if (this.projectgroupref) {
refArray = [...refArray, ...this.projectgroupref];
}
let parentref = refArray.join("/");
await createProject(
let { error } = await createProject(
parentref,
this.projectName,
this.selectedRemoteSource.name,
this.remoteRepoPath
);
if (error) {
this.createProjectError = error;
return;
}
let projectref = [this.projectName];
if (this.projectgroupref) {
@ -84,9 +94,20 @@ export default {
}
},
created: async function() {
this.user = await fetchCurrentUser();
let { data, error } = await fetchCurrentUser();
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.user = data;
// TODO(sgotti) filter only remote source where the user has a linked account
this.remoteSources = await fetchRemoteSources();
({ data, error } = await fetchRemoteSources());
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.remoteSources = data;
}
};
</script>

View File

@ -15,9 +15,9 @@
<div class="control">
<button class="button is-primary" @click="createProjectGroup()">Create Project Group</button>
</div>
<div class="control">
<button class="button is-text">Cancel</button>
</div>
</div>
<div v-if="createProjectGroupError" class="message is-danger">
<div class="message-body">{{ createProjectGroupError }}</div>
</div>
</div>
</template>
@ -27,8 +27,6 @@ import { createProjectGroup } from "@/util/data.js";
import { projectGroupLink } from "@/util/link.js";
import remoterepos from "@/components/remoterepos.vue";
export default {
components: {},
name: "createprojectgroup",
@ -39,18 +37,31 @@ export default {
},
data() {
return {
createProjectGroupError: null,
projectGroupName: null
};
},
methods: {
resetErrors() {
this.createProjectGroupError = null;
},
async createProjectGroup() {
this.resetErrors();
let refArray = [this.ownertype, this.ownername];
if (this.projectgroupref) {
refArray = [...refArray, ...this.projectgroupref];
}
let parentref = refArray.join("/");
await createProjectGroup(parentref, this.projectGroupName);
let { error } = await createProjectGroup(
parentref,
this.projectGroupName
);
if (error) {
this.createProjectGroupError = error;
return;
}
let projectgroupref = [this.projectGroupName];
if (this.projectgroupref) {

View File

@ -42,6 +42,9 @@
>Delete Project Group</button>
</div>
</div>
<div v-if="deleteProjectGroupError" class="message is-danger">
<div class="message-body">{{ deleteProjectGroupError }}</div>
</div>
</div>
</nav>
</div>
@ -64,6 +67,7 @@ export default {
},
data() {
return {
deleteProjectGroupError: null,
variables: [],
allvariables: [],
projectGroupNameToDelete: ""
@ -83,6 +87,9 @@ export default {
}
},
methods: {
resetErrors() {
this.deleteProjectGroupError = null;
},
async deleteProjectGroup() {
let projectgroupref = [
this.ownertype,
@ -91,7 +98,11 @@ export default {
].join("/");
if (this.projectGroupNameToDelete == this.projectGroupName) {
let res = await deleteProjectGroup(projectgroupref);
let { error } = await deleteProjectGroup(projectgroupref);
if (error) {
this.deleteProjectGroupError = error;
return;
}
this.$router.push(
projectGroupLink(
this.ownertype,
@ -103,16 +114,33 @@ export default {
}
},
created: async function() {
this.variables = await fetchVariables(
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
let { data, error } = await fetchVariables(
"projectgroup",
[this.ownertype, this.ownername, ...this.projectgroupref].join("/"),
projectgroupref,
false
);
this.allvariables = await fetchVariables(
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.variables = data;
({ data, error } = await fetchVariables(
"projectgroup",
[this.ownertype, this.ownername, ...this.projectgroupref].join("/"),
projectgroupref,
true
);
));
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.allvariables = data;
}
};
</script>

View File

@ -32,7 +32,11 @@
</template>
<script>
import { apiurl, fetch } from "@/util/auth";
import {
fetchProjectGroupProjects,
fetchProjectGroupSubgroups
} from "@/util/data.js";
import { projectLink, projectGroupLink } from "@/util/link.js";
export default {
@ -66,24 +70,33 @@ export default {
return ref;
},
async fetchProjects(ownertype, ownername) {
let ref = [ownertype, ownername];
let projectgroupref = [ownertype, ownername];
if (this.projectgroupref) {
ref.push(...this.projectgroupref);
projectgroupref.push(...this.projectgroupref);
}
let path = "/projectgroups/" + encodeURIComponent(ref.join("/"));
path += "/projects";
let res = await (await fetch(apiurl(path))).json();
this.projects = res;
let { data, error } = await fetchProjectGroupProjects(
projectgroupref.join("/")
);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.projects = data;
},
async fetchProjectGroups(ownertype, ownername) {
let ref = [ownertype, ownername];
let projectgroupref = [ownertype, ownername];
if (this.projectgroupref) {
ref.push(...this.projectgroupref);
projectgroupref.push(...this.projectgroupref);
}
let path = "/projectgroups/" + encodeURIComponent(ref.join("/"));
path += "/subgroups";
let res = await (await fetch(apiurl(path))).json();
this.projectgroups = res;
let { data, error } = await fetchProjectGroupSubgroups(
projectgroupref.join("/")
);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.projectgroups = data;
},
projectLink: projectLink,
projectGroupLink: projectGroupLink

View File

@ -23,6 +23,10 @@
<button class="button is-primary" @click="updateProject()">Update</button>
</div>
</div>
<div v-if="updateProjectError" class="message is-danger">
<div class="message-body">{{ updateProjectError }}</div>
</div>
</div>
</nav>
<nav class="panel">
@ -64,6 +68,10 @@
@click="deleteProject()"
:disabled="!deleteButtonEnabled"
>Delete Project</button>
<div v-if="deleteProjectError" class="message is-danger">
<div class="message-body">{{ deleteProjectError }}</div>
</div>
</div>
</div>
</nav>
@ -92,6 +100,8 @@ export default {
},
data() {
return {
updateProjectError: null,
deleteProjectError: null,
project: null,
projectIsPrivate: false,
variables: [],
@ -111,7 +121,13 @@ export default {
}
},
methods: {
resetErrors() {
this.updateProjectError = null;
this.deleteProjectError = null;
},
async updateProject() {
this.resetErrors();
let projectref = [
this.ownertype,
this.ownername,
@ -122,9 +138,19 @@ export default {
if (this.projectIsPrivate) {
visibility = "private";
}
let res = await updateProject(projectref, this.project.name, visibility);
let { error } = await updateProject(
projectref,
this.project.name,
visibility
);
if (error) {
this.updateProjectError = error;
return;
}
},
async deleteProject() {
this.resetErrors();
let projectref = [
this.ownertype,
this.ownername,
@ -132,7 +158,11 @@ export default {
].join("/");
if (this.projectNameToDelete == this.projectName) {
let res = await deleteProject(projectref);
let { error } = await deleteProject(projectref);
if (error) {
this.deleteProjectError = error;
return;
}
this.$router.push(
projectGroupLink(
this.ownertype,
@ -148,19 +178,27 @@ export default {
"/"
);
this.project = await fetchProject(projectref);
let { data, error } = await fetchProject(projectref);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.project = data;
this.projectIsPrivate = this.project.visibility == "private";
this.variables = await fetchVariables(
"project",
[this.ownertype, this.ownername, ...this.projectref].join("/"),
false
);
this.allvariables = await fetchVariables(
"project",
[this.ownertype, this.ownername, ...this.projectref].join("/"),
true
);
({ data, error } = await fetchVariables("project", projectref, false));
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.variables = data;
({ data, error } = await fetchVariables("project", projectref, true));
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.allvariables = data;
}
};
</script>

View File

@ -43,7 +43,12 @@ export default {
this.$emit("reposelected", this.remoterepos[index].path);
},
async fetchRemoteRepos(remotesourceid) {
this.remoterepos = await userRemoteRepos(remotesourceid);
let { data, error } = await userRemoteRepos(remotesourceid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.remoterepos = data;
}
},
created: function() {

View File

@ -101,7 +101,13 @@ export default {
return "unknown";
},
async fetchRun() {
this.run = await fetchRun(this.runid);
let { data, error } = await fetchRun(this.runid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.run = data;
// sort tasks by level
let tasks = this.run.tasks;
let sortedTasks = Object.keys(this.run.tasks)

View File

@ -67,8 +67,7 @@
</template>
<script>
import { apiurl, fetch } from "@/util/auth";
import { fetchProject, fetchRuns } from "@/util/data.js";
import { fetchUser, fetchProject, fetchRuns } from "@/util/data.js";
import { userLocalRunLink, projectRunLink } from "@/util/link.js";
export default {
@ -127,15 +126,28 @@ export default {
this.pollData();
},
async fetchProject() {
this.project = await fetchProject(
[this.ownertype, this.ownername, ...this.projectref].join("/")
);
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
let { data, error } = await fetchProject(projectref);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.project = data;
this.fetchRuns();
},
async fetchUser() {
let res = await (await fetch(apiurl("/users/" + this.username))).json();
this.user = res;
let { data, error } = await fetchUser(this.username);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.user = data;
this.fetchRuns();
},
@ -159,7 +171,12 @@ export default {
group = "/user/" + this.user.id;
}
this.runs = await fetchRuns(group, lastrun);
let { data, error } = await fetchRuns(group, lastrun);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.runs = data;
},
pollData() {
clearInterval(this.polling);

View File

@ -63,10 +63,20 @@ export default {
return "unknown";
},
async fetchRun() {
this.run = await fetchRun(this.runid);
let { data, error } = await fetchRun(this.runid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.run = data;
},
async fetchTask() {
this.task = await fetchTask(this.runid, this.taskid);
let { data, error } = await fetchTask(this.runid, this.taskid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.task = data;
},
pollData() {
this.polling = setInterval(() => {

View File

@ -10,26 +10,33 @@
<button class="button is-primary" @click="deleteUserToken(token)">Delete</button>
</div>
</div>
<div v-if="deleteUserTokenError" class="message is-danger">
<div class="message-body">{{ deleteUserTokenError }}</div>
</div>
</div>
<div v-else class="item-list">No user tokens</div>
<div v-else>No user tokens</div>
<hr>
<div v-if="token" class="notification is-success">
<button class="delete" @click="closeNewTokenNotification()"></button>
User token created: {{token}}
</div>
<h4 class="title is-4">Create new User Token</h4>
<h5 class="title is-5">Create new User Token</h5>
<div class="field is-grouped">
<div class="control">
<input class="input is-primary" type="text" placeholder="Token name" v-model="newtokenname">
</div>
<button class="button is-primary" @click="createUserToken()">Create Token</button>
</div>
<div v-if="createUserTokenError" class="message is-danger">
<div class="message-body">{{ createUserTokenError }}</div>
</div>
</div>
</template>
<script>
import {
fetchCurrentUser,
fetchRemoteSources,
createUserToken,
deleteUserToken
} from "@/util/data.js";
@ -40,33 +47,85 @@ export default {
props: {},
data() {
return {
createUserTokenError: null,
deleteUserTokenError: null,
user: [],
remotesources: [],
token: null,
newtokenname: null
};
},
methods: {
resetErrors() {
this.createUserTokenError = null;
this.deleteUserTokenError = null;
},
async fetchCurrentUser() {
let { data, error } = await fetchCurrentUser();
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.user = data;
},
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.remotesources = data;
},
async createUserToken() {
let res = await createUserToken(this.user.username, this.newtokenname);
this.token = res.token;
this.resetErrors();
let { data, error } = await createUserToken(
this.user.username,
this.newtokenname
);
if (error) {
this.createUserTokenError = error;
return;
}
this.token = data.token;
this.newtokenname = null;
this.user = await fetchCurrentUser();
this.fetchCurrentUser();
},
async deleteUserToken(tokenname) {
await deleteUserToken(this.user.username, tokenname);
this.user = await fetchCurrentUser();
this.resetErrors();
let { error } = await deleteUserToken(this.user.username, tokenname);
if (error) {
this.deleteUserTokenError = error;
return;
}
this.fetchCurrentUser();
},
closeNewTokenNotification() {
this.token = null;
}
},
created: async function() {
this.user = await fetchCurrentUser();
created: function() {
this.fetchCurrentUser();
this.fetchRemoteSources();
}
};
</script>
<style scoped lang="scss">
@import "@/css/_variables.scss";
.item-list {
.item {
margin-bottom: 5px;
border: 1px solid $grey-lighter;
cursor: pointer;
display: flex;
padding: 10px;
}
.name {
font-weight: bold;
}
}
</style>

View File

@ -21,9 +21,11 @@ import Logout from "./views/Logout.vue";
import { parseRef } from "@/util/link.js";
import store from "./store";
Vue.use(VueRouter);
export default new VueRouter({
const router = new VueRouter({
mode: "history",
routes: [
{
@ -327,3 +329,10 @@ export default new VueRouter({
},
]
});
router.beforeEach((to, from, next) => {
store.dispatch("setError", null);
next()
})
export default router

View File

@ -4,11 +4,16 @@ import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
error: null,
user: null,
registeruser: null
registeruser: null,
}
const getters = {
// global error
error: state => {
return state.error
},
user: state => {
return state.user
},
@ -18,6 +23,9 @@ const getters = {
}
const mutations = {
setError(state, error) {
state.error = error
},
setUser(state, user) {
state.user = user
},
@ -27,6 +35,9 @@ const mutations = {
}
const actions = {
setError({ commit }, error) {
commit('setError', error)
},
setUser({ commit }, user) {
commit('setUser', user)
},

View File

@ -7,13 +7,13 @@ const USER_KEY = 'user';
let API_URL = window.CONFIG.API_URL;
let API_BASE_PATH = window.CONFIG.API_BASE_PATH;
export function login(token, user) {
export function setLoggedUser(token, user) {
setIdToken(token);
setUser(user);
store.dispatch('setUser', user)
}
export function logout() {
export function doLogout() {
unsetIdToken();
unsetUser()
store.dispatch('setUser', null)
@ -48,6 +48,19 @@ export function oauth2callbackurl() {
return new URL(API_URL + "/oauth2/callback");
}
export async function loginapi(init) {
if (init === undefined) {
init = {}
}
try {
let res = await window.fetch(loginurl(), init)
return res
} catch (e) {
throw e
}
}
export async function fetch(url, init) {
if (init === undefined) {
init = {}
@ -60,10 +73,14 @@ export async function fetch(url, init) {
init.headers["Authorization"] = "bearer " + idToken
}
let res = await window.fetch(url, init)
if (res.status === 401) {
router.push({ name: "login" })
} else { return res }
try {
let res = await window.fetch(url, init)
if (res.status === 401) {
router.push({ name: "login" })
} else { return res }
} catch (e) {
throw e
}
}
export function setIdToken(idToken) {

View File

@ -1,9 +1,49 @@
import { apiurl, fetch } from "@/util/auth";
import { apiurl, loginapi } from "@/util/auth";
import { fetch as authfetch } from "@/util/auth";
export async function fetch(url, init) {
try {
let res = await authfetch(url, init)
if (!res.ok) {
let data = await res.json()
return { data: null, error: data.message }
} else {
if (res.status == 204) {
return { data: null, error: null }
}
return { data: await res.json(), error: null }
}
} catch (e) {
return { data: null, error: "api call failed: " + e }
}
}
export async function login(username, password, remotesourcename) {
let init = {
method: "POST",
body: JSON.stringify({
remote_source_name: remotesourcename,
login_name: username,
password: password
})
}
try {
let res = await loginapi(init)
if (!res.ok) {
let data = await res.json()
return { data: null, error: data.message }
} else {
return { data: await res.json(), error: null }
}
} catch (e) {
return { data: null, error: "api call failed: " + e }
}
}
export async function fetchCurrentUser() {
let path = "/user"
let res = await fetch(apiurl(path));
return res.json();
return await fetch(apiurl(path));
}
export async function fetchRuns(group, lastrun) {
@ -14,25 +54,40 @@ export async function fetchRuns(group, lastrun) {
if (lastrun) {
u.searchParams.append("lastrun", true)
}
let res = await fetch(u)
return res.json();
return await fetch(u)
}
export async function fetchRun(runid) {
let res = await fetch(apiurl("/runs/" + runid));
return res.json();
return await fetch(apiurl("/runs/" + runid));
}
export async function fetchTask(runid, taskid) {
let res = await fetch(apiurl("/runs/" + runid + "/tasks/" + taskid))
return res.json();
return await fetch(apiurl("/runs/" + runid + "/tasks/" + taskid))
}
export async function fetchUser(username) {
let path = "/users/" + username
return await fetch(apiurl(path));
}
export async function fetchProjectGroupSubgroups(projectgroupref) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref)
path += "/subgroups";
return await fetch(apiurl(path));
}
export async function fetchProjectGroupProjects(projectgroupref) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref)
path += "/projects";
return await fetch(apiurl(path));
}
export async function fetchProject(ref) {
let path = "/projects/" + encodeURIComponent(ref)
let res = await fetch(apiurl(path));
return res.json();
return await fetch(apiurl(path));
}
export async function fetchVariables(ownertype, ref, all) {
@ -47,8 +102,7 @@ export async function fetchVariables(ownertype, ref, all) {
if (all) {
path += "?tree&removeoverridden";
}
let res = await fetch(apiurl(path));
return res.json();
return await fetch(apiurl(path));
}
export async function createUserToken(username, tokenname) {
@ -59,8 +113,7 @@ export async function createUserToken(username, tokenname) {
token_name: tokenname,
})
}
let res = await fetch(apiurl(path), init)
return res.json();
return await fetch(apiurl(path), init)
}
export async function deleteUserToken(username, tokenname) {
@ -68,8 +121,7 @@ export async function deleteUserToken(username, tokenname) {
let init = {
method: "DELETE",
}
let res = await fetch(apiurl(path), init)
return res.text();
return await fetch(apiurl(path), init)
}
export async function restartRun(runid, fromStart) {
@ -81,8 +133,7 @@ export async function restartRun(runid, fromStart) {
from_start: fromStart
})
}
let res = await fetch(apiurl(path), init)
return res.json();
return await fetch(apiurl(path), init)
}
export async function stopRun(runid) {
@ -93,8 +144,7 @@ export async function stopRun(runid) {
action_type: "stop"
})
}
let res = await fetch(apiurl(path), init)
return res.json();
return await fetch(apiurl(path), init)
}
export async function approveTask(runid, taskid) {
@ -105,20 +155,17 @@ export async function approveTask(runid, taskid) {
action_type: "approve"
})
}
let res = await fetch(apiurl(path), init)
return res.json();
return await fetch(apiurl(path), init)
}
export async function fetchRemoteSources() {
let path = "/remotesources"
let res = await fetch(apiurl(path));
return res.json();
return await fetch(apiurl(path));
}
export async function userRemoteRepos(remotesourceid) {
let path = "/user/remoterepos/" + remotesourceid
let res = await fetch(apiurl(path));
return res.json();
return await fetch(apiurl(path));
}
export async function createProjectGroup(parentref, name) {
@ -131,8 +178,7 @@ export async function createProjectGroup(parentref, name) {
visibility: "public",
})
}
let res = await fetch(apiurl(path), init)
return res.json();
return await fetch(apiurl(path), init)
}
export async function createProject(parentref, name, remotesourcename, remoterepopath) {
@ -147,8 +193,7 @@ export async function createProject(parentref, name, remotesourcename, remoterep
repo_path: remoterepopath,
})
}
let res = await fetch(apiurl(path), init)
return res.json();
return await fetch(apiurl(path), init)
}
export async function updateProject(projectref, name, visibility) {
@ -160,8 +205,7 @@ export async function updateProject(projectref, name, visibility) {
visibility: visibility,
})
}
let res = await fetch(apiurl(path), init)
return res.json()
return await fetch(apiurl(path), init)
}
export async function deleteProject(projectref) {
@ -169,8 +213,7 @@ export async function deleteProject(projectref) {
let init = {
method: "DELETE",
}
let res = await fetch(apiurl(path), init)
return res.text();
return await fetch(apiurl(path), init)
}
export async function deleteProjectGroup(projectgroupref) {
@ -178,6 +221,5 @@ export async function deleteProjectGroup(projectgroupref) {
let init = {
method: "DELETE",
}
let res = await fetch(apiurl(path), init)
return res.text();
return await fetch(apiurl(path), init)
}

View File

@ -1,17 +1,23 @@
<template>
<div>
<div v-if="error" class="message is-danger">
<div class="message-header">
<p>Login error</p>
</div>
<div class="message-body">{{ error }}</div>
</div>
<div class="column is-4 is-offset-4">
<div class="box" v-for="rs in remotesources" v-bind:key="rs.id">
<LoginForm
action="Login"
:name="rs.name"
v-if="rs.auth_type == 'password'"
v-on:login="doLogin(rs.name, $event.username, $event.password)"
v-on:login="doLogin($event.username, $event.password, rs.name)"
/>
<button
v-else
class="button is-info is-fullwidth"
@click="doLogin(rs.name)"
@click="doLogin(null, null, rs.name)"
>Login with {{rs.name}}</button>
</div>
</div>
@ -19,8 +25,8 @@
</template>
<script>
import { fetchRemoteSources } from "@/util/data";
import { loginurl, fetch, login, logout } from "@/util/auth";
import { fetchRemoteSources, login } from "@/util/data";
import { setLoggedUser, doLogout } from "@/util/auth";
import LoginForm from "@/components/loginform";
@ -31,34 +37,39 @@ export default {
},
data: function() {
return {
error: null,
remotesources: null
};
},
methods: {
async getRemoteSources() {
this.remotesources = await fetchRemoteSources();
},
async doLogin(rsName, username, password) {
let u = loginurl();
let res = await (await fetch(u, {
method: "POST",
body: JSON.stringify({
remote_source_name: rsName,
login_name: username,
password: password
})
})).json();
if (res.oauth2_redirect) {
window.location = res.oauth2_redirect;
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
return;
}
login(res.token, res.user);
this.remotesources = data;
},
async doLogin(username, password, remotesourcename) {
this.error = null;
let { data, error } = await login(username, password, remotesourcename);
if (error) {
// set local login error on failed login.
this.error = error;
return;
}
if (data.oauth2_redirect) {
window.location = data.oauth2_redirect;
return;
}
setLoggedUser(data.token, data.user);
this.$router.push({ name: "home" });
}
},
created: function() {
logout();
this.getRemoteSources();
doLogout();
this.fetchRemoteSources();
}
};
</script>

View File

@ -1,10 +1,10 @@
<script>
import { logout } from "@/util/auth";
import { doLogout } from "@/util/auth";
export default {
name: "Logout",
created: function() {
logout();
doLogout();
this.$router.push("/");
}
};

View File

@ -1,12 +1,18 @@
<template>
<div>
<div>{{code}}</div>
<div>{{username}}</div>
<div v-if="error" class="message is-danger">
<div class="message-header">
<p>Error</p>
</div>
<div class="message-body">{{ error }}</div>
</div>
</div>
</template>
<script>
import { oauth2callbackurl, login, fetch } from "@/util/auth";
import { fetch } from "@/util/data";
import { oauth2callbackurl, setLoggedUser } from "@/util/auth";
export default {
components: {},
@ -14,6 +20,7 @@ export default {
props: {},
data() {
return {
error: null,
run: null,
code: this.$route.query.code,
polling: null,
@ -25,12 +32,18 @@ export default {
let u = oauth2callbackurl();
u.searchParams.append("code", this.$route.query.code);
u.searchParams.append("state", this.$route.query.state);
let res = await (await fetch(u)).json();
if (res.request_type === "loginuser") {
login(res.response.token, res.response.user);
let { data, error } = await fetch(u);
if (error) {
// set local login error on failed oauth2.
this.error = error;
return;
}
if (data.request_type === "loginuser") {
setLoggedUser(data.response.token, data.response.user);
this.$router.push("/");
} else if (res.request_type === "authorize") {
this.$store.dispatch("setRegisterUser", res.response);
} else if (data.request_type === "authorize") {
this.$store.dispatch("setRegisterUser", data.response);
this.$router.push("/register");
}
}

View File

@ -134,7 +134,12 @@ export default {
watch: {
$route: async function(route) {
if (route.params.runid) {
this.run = await fetchRun(route.params.runid);
let { data, error } = await fetchRun(route.params.runid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.run = data;
}
}
},
@ -153,7 +158,12 @@ export default {
},
created: async function() {
if (this.$route.params.runid) {
this.run = await fetchRun(this.$route.params.runid);
let { data, error } = await fetchRun(this.$route.params.runid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.run = data;
}
}
};

View File

@ -51,8 +51,6 @@ import {
projectGroupCreateProjectLink
} from "@/util/link.js";
import { fetchRun } from "@/util/data.js";
import projbreadcrumbs from "@/components/projbreadcrumbs.vue";
import createprojectbutton from "@/components/createprojectbutton.vue";
@ -64,18 +62,6 @@ export default {
ownername: String,
projectgroupref: Array
},
data() {
return {
run: null
};
},
watch: {
$route: async function(route) {
if (route.params.runid) {
this.run = await fetchRun(route.params.runid);
}
}
},
methods: {
projectGroupProjectsLink: projectGroupProjectsLink,
projectGroupSettingsLink: projectGroupSettingsLink,
@ -103,11 +89,6 @@ export default {
)
);
}
},
created: async function() {
if (this.$route.params.runid) {
this.run = await fetchRun(this.$route.params.runid);
}
}
};
</script>

View File

@ -31,7 +31,9 @@ import { mapGetters } from "vuex";
import LoginForm from "@/components/loginform";
import RegisterForm from "@/components/registerform";
import { apiurl, authorizeurl, registerurl, fetch, logout } from "@/util/auth";
import { fetchRemoteSources } from "@/util/data";
import { authorizeurl, registerurl, fetch, doLogout } from "@/util/auth";
export default {
name: "Register",
@ -48,9 +50,13 @@ export default {
...mapGetters(["registeruser"])
},
methods: {
async getRemoteSources() {
let res = await (await fetch(apiurl("/remotesources"))).json();
this.remotesources = res;
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.remotesources = data;
},
async doAuthorize(rsName, username, password) {
let u = authorizeurl();
@ -99,8 +105,8 @@ export default {
}
},
created: function() {
logout();
this.getRemoteSources();
doLogout();
this.fetchRemoteSources();
}
};
</script>

View File

@ -117,7 +117,12 @@ export default {
watch: {
$route: async function(route) {
if (route.params.runid) {
this.run = await fetchRun(route.params.runid);
let { data, error } = await fetchRun(route.params.runid);
if (error) {
this.$store.dispatch("setError", error);
return;
}
this.run = data;
}
}
},