incremental work on ui

This commit is contained in:
Jonathan Shook 2020-08-17 16:20:11 -05:00
parent 9717487db4
commit 1d6e4f29a6
7 changed files with 174 additions and 24 deletions

View File

@ -11,6 +11,9 @@ package io.nosqlbench.docsys.api;
* .io/documentation/latest/jaxrs-resources.html#d0e2040">Jersey jax-rs
* resources documentation</A>
*
* @see <A href="https://repo1.maven.org/maven2/org/glassfish/jersey/jersey-documentation/2.5
* .1/jersey-documentation-2.5.1-user-guide.pdf">Jersey 2.5.1 user guide</a>
*
* @see <A href="https://github.com/jax-rs/spec/blob/master/spec
* .pdf">JAX-RS: Java™ API for RESTful Web Services Version 2.1
* Proposed Final Draft June 9, 2017</A>

View File

@ -1,22 +1,77 @@
<template>
<v-container fluid>
<v-select :items="workspaces" label="default workspace"></v-select>
<v-container>
<v-text-field dense
label="Name of new workspace"
v-if="mode=='adding'"
v-model="new_workspace"
ref="new_workspace_input"
hint="workspace name"
@blur="commitWorkspace(new_workspace)"
@keydown.enter="commitWorkspace(new_workspace)"
></v-text-field>
<v-select dense
label="workspace"
v-if="mode=='showing'"
v-model="workspace"
:items="workspaces"
item-text="name"
item-value="name"
>
<template v-slot:append-item>
<v-list-item>
<v-btn @click="addWorkspace()">+ Add Workspace</v-btn>
</v-list-item>
</template>
</v-select>
</v-container>
</template>
<script>
export default {
name: 'WorkspaceSelector',
name: 'workspace-selector',
data(context) {
let data = {
workspaces: [],
workspace: 'default',
new_workspace: "",
mode: "showing",
workspaces: [{name: 'default'}],
workspace: {name: 'default'},
enabled: false
};
return data;
},
methods: {
addWorkspace: function (evt) {
this.mode = "adding";
setTimeout(() => {
this.$refs.new_workspace_input.$el.focus()
});
console.log("add evt:" + JSON.stringify(evt));
},
commitWorkspace: function (evt) {
console.log("commit evt:" + JSON.stringify(evt));
let committed = this.$axios.$get("/services/workspaces/" + evt)
.then(res => {
return res;
})
.catch((e) => {
console.log("create: error: " + e)
});
console.log("committed: " + JSON.stringify(committed))
this.workspaces = this.$axios.$get("/services/workspaces/")
.then(res => {
console.log("workspaces async:" + JSON.stringify(res));
return res;
})
.catch((e) => {
console.log("refresh error: " + e)
});
this.workspace = evt;
this.mode = "showing"
this.$forceUpdate();
}
},
async asyncData({$axios, store}) {
let enabled = await $axios.$get("/services/nb/enabled")
let enabled = await $axios.$get("/services/status")
.then(res => {
return res
})

View File

@ -14,7 +14,7 @@
justify-center
align-center>
<v-content>
<v-main>
<v-container fluid>
<v-layout row>
<v-flex>
@ -53,7 +53,7 @@
</v-container>
</v-content>
</v-main>
</v-layout>
<v-footer app dark color="secondary">
@ -251,7 +251,7 @@
return data;
},
async asyncData({ $axios, store }) {
let enabled = await $axios.$get("/services/nb/enabled")
let enabled = await $axios.$get("/services/status")
.then(res => {
return res
})

View File

@ -4,7 +4,9 @@
<v-toolbar-title>NoSQLBench - Workspaces</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn title="start a new workspace">
<workspace-selector></workspace-selector>
<v-btn title="start a new workspace" @click="startNewWorkspace()">
<v-icon>mdi-folder-plus-outline</v-icon>
</v-btn>
<v-btn title="upload a workspace zip file">
@ -35,9 +37,9 @@
]
-->
<v-main justify-start align-start class="pa-4">
<v-main justify-start align-start class="d-flex pa-4 ma-4">
<v-main>
<v-card max-width="344" v-for="(workspace,w) in workspaces" :key="w" class="pa-4">
<v-card max-width="344" v-for="(workspace,w) in workspaces" :key="w" class="pa-4 ma-4">
<v-card-title title="workspace name">{{ workspace.name }}</v-card-title>
<v-card-subtitle title="last change">{{ abbrev(workspace.summary.last_changed_filename) }}</v-card-subtitle>
<v-divider></v-divider>
@ -51,6 +53,10 @@
<v-divider></v-divider>
<v-list-item>
<v-btn title="view details of workspace">
<v-icon>mdi-magnify</v-icon>
</v-btn>
<v-btn title="use this workspace">
<v-icon>mdi-play</v-icon>
</v-btn>
@ -58,12 +64,13 @@
<v-btn title="download zipped workspace">
<v-icon>mdi-folder-download</v-icon>
</v-btn>
<v-btn title="add one or more files">
<v-icon>mdi-file-plus</v-icon>
</v-btn>
<v-btn title="view details of workspace">
<v-icon>mdi-magnify</v-icon>
<v-spacer></v-spacer>
<v-btn title="purge workspace">
<v-icon @click="purgeWorkspace(workspace.name)">mdi-trash-can</v-icon>
</v-btn>
</v-list-item>
</v-list>
</v-card>
@ -74,8 +81,14 @@
</template>
<script>
import WorkspaceSelector from "~/components/WorkspaceSelector";
export default {
name: "workspaces.vue",
components: {
WorkspaceSelector
},
data(context) {
let data = {
workspaces: [
@ -89,10 +102,22 @@ export default {
methods: {
abbrev(name) {
return name;
},
purgeWorkspace: function(ws) {
console.log("purging " + ws);
this.$axios.$delete("/services/workspaces/" + ws)
.then(res => { return res })
.catch((e) => {
console.log("error: " + e)
});
this.$forceUpdate();
},
startNewWorkspace() {
console.log("starting new workspace");
}
},
async asyncData({$axios, store}) {
let enabled = await $axios.$get("/services/nb/enabled")
let enabled = await $axios.$get("/services/status")
.then(res => {
return res
})

View File

@ -12,6 +12,8 @@ import io.nosqlbench.engine.rest.services.WorkspaceService;
import io.nosqlbench.engine.rest.transfertypes.RunScenarioRequest;
import io.nosqlbench.engine.rest.transfertypes.ScenarioInfo;
import io.nosqlbench.nb.annotations.Service;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.inject.Singleton;
import javax.ws.rs.*;
@ -27,6 +29,7 @@ import java.util.*;
@Singleton
@Path("/services/executor/")
public class ScenarioExecutorEndpoint implements WebServiceObject {
private final static Logger logger = LogManager.getLogger(ScenarioExecutorEndpoint.class);
private ScenariosExecutor executor = new ScenariosExecutor("executor-service", 1);

View File

@ -39,12 +39,30 @@ public class WorkspacesEndpoint implements WebServiceObject {
return Response.ok(wsviews).build();
}
@DELETE
@Path("{workspace}")
@Produces(MediaType.APPLICATION_JSON)
public Response deleteWorkspace(@PathParam("workspace") String workspace,
@QueryParam("deleteCount") String deleteCount) {
try {
int dc = deleteCount!=null ? Integer.valueOf(deleteCount):0;
getSvc().purgeWorkspace(workspace,dc);
return Response.ok("removed workspace " + workspace).build();
} catch (Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
}
@GET
@Path("{workspace}")
@Produces(MediaType.APPLICATION_JSON)
public Response getWorkspaceInfo(@PathParam("workspace") String workspace) {
WorkspaceView workpaceView = getSvc().getWorkspaceView(workspace);
return Response.ok(workpaceView).build();
try {
WorkspaceView workpaceView = getSvc().getWorkspaceView(workspace);
return Response.ok(workpaceView).build();
} catch (Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
}
@POST

View File

@ -2,6 +2,8 @@ package io.nosqlbench.engine.rest.services;
import io.nosqlbench.engine.rest.domain.WorkSpace;
import io.nosqlbench.engine.rest.transfertypes.WorkspaceView;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MediaType;
@ -9,9 +11,13 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
public class WorkspaceService {
private final static Logger logger = LogManager.getLogger(WorkspaceService.class);
private final Path root;
public static String DEFAULT = "default";
public static final String WORKSPACE_ROOT = "workspaces_root";
@ -65,13 +71,12 @@ public class WorkspaceService {
public WorkSpace getWorkspace(String workspaceName) {
assertLegalWorkspaceName(workspaceName);
return new WorkSpace(this.root,workspaceName);
return new WorkSpace(this.root, workspaceName);
}
public static void assertLegalWorkspaceName(String workspaceName) {
if (!workspaceName.matches("[a-zA-Z][a-zA-Z0-9]+")) {
throw new RuntimeException("Workspaces must start with an alphabetic" +
" character, and contain only letters and numbers.");
if (!workspaceName.matches("[a-zA-Z0-9]+")) {
throw new RuntimeException("Workspace names must contain only letters and numbers.");
}
}
@ -114,6 +119,47 @@ public class WorkspaceService {
return root.resolve(workspaceName);
}
public void purgeWorkspace(String workspaceName, int deleteCount) {
assertLegalWorkspaceName(workspaceName);
Path path = workspacePath(workspaceName);
if (Files.exists(path)) {
try (Stream<Path> counter = Files.walk(path)) {
long foundFiles = counter.count();
if (foundFiles > 100 && deleteCount != foundFiles) {
throw new RuntimeException(
"To delete " + foundFiles + " files, you must provide a deleteCount=<count> " +
"parameter that matches. This is a safety mechanism."
);
}
logger.debug("found " + foundFiles + " to delete.");
} catch (Exception e) {
throw new RuntimeException(e);
}
Path relativize = root.relativize(path);
if (relativize.toString().contains("..")) {
throw new RuntimeException("Illegal path to delete: " + path.toString());
}
try (Stream<Path> walk = Files.walk(path)) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
// .peek(System.out::println)
.forEach(f -> {
logger.debug("deleting '" + f.toString() + "'");
if (!f.delete()) {
throw new RuntimeException("Unable to delete " + f.toString());
};
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public final static class FileInfo {
private final Path path;