Skip to content

Commit abd8fef

Browse files
authored
TypeDB Studio 2.4.0-alpha-3 (#477)
## What is the goal of this PR? **Non-Debian Linux support** - We've added a Java binary artifact (a gzipped shell runner) that runs on all Linux platforms. Please note that Java 11 must be installed in order to run it. **Visualiser bugfixes** - When there exist 2 (or more) edges between the same vertices, they are no longer superimposed, but are now rendered as curved edges - `isa` edges are now displayed - When dragging a roleplayer, its attributes and relations will now move with it - Disconnected subgraphs are now less likely to be pushed far from the centre **Concept Details bugfixes** - Field values are now scrollable and selectable - Internal ID is displayed correctly **Other bugfixes** - Fixed a possible crash on submitting the login form - Fixed an issue causing code changes to be possibly ignored by the query runner
1 parent 3d991e0 commit abd8fef

26 files changed

+986
-198
lines changed

.circleci/config.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ commands:
2222

2323
install-bazel-linux:
2424
steps:
25-
- run: curl -OL https://raw.githubusercontent.com/graknlabs/dependencies/master/tool/bazelinstall/linux.sh
25+
- run: curl -OL https://raw.githubusercontent.com/vaticle/dependencies/master/tool/bazelinstall/linux.sh
2626
- run: bash ./linux.sh && rm ./linux.sh
2727

2828
install-bazel-mac:
@@ -82,9 +82,14 @@ jobs:
8282
- install-bazel-linux
8383
- checkout
8484
- run: bazel build //:application-image
85+
- run: bazel build //:linux-java-binary
8586
- run: |
86-
mkdir -p ~/src && cp bazel-bin/application-image.zip ~/src
87-
mkdir -p ~/dist && cd ~/dist && jar xf ~/src/application-image.zip
87+
mkdir -p ~/src
88+
mkdir -p ~/dist
89+
cp bazel-bin/typedb-studio-linux-java-binary.tar.gz ~/dist/typedb-studio-linux-java-binary-$(cat VERSION).tar.gz
90+
cp bazel-bin/application-image.zip ~/src
91+
cd ~/dist
92+
jar xf ~/src/application-image.zip
8893
- persist_to_workspace:
8994
root: ~/dist
9095
paths:
@@ -133,7 +138,7 @@ jobs:
133138
image: ubuntu-1604:201903-01
134139
steps:
135140
- checkout
136-
- run: git push --delete https://$REPO_GITHUB_TOKEN@github.com/graknlabs/workbase $CIRCLE_BRANCH
141+
- run: git push --delete https://$REPO_GITHUB_TOKEN@github.com/vaticle/typedb-studio.git $CIRCLE_BRANCH
137142

138143
workflows:
139144
release:

.grabl/automation.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ build:
3131
# TODO: re-enable checkstyle tests
3232
# bazel run @vaticle_dependencies//tool/checkstyle:test-coverage
3333
# bazel test $(bazel query 'kind(checkstyle_test, //...)') --test_output=streamed
34+
test-unit:
35+
image: vaticle-ubuntu-20.04
36+
command: |
37+
bazel test //visualiser/...
3438
3539
release:
3640
# filter:

BUILD

Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,7 @@ load("@vaticle_bazel_distribution//github:rules.bzl", "deploy_github")
2727
load("@vaticle_bazel_distribution//brew:rules.bzl", "deploy_brew")
2828
load("@io_bazel_rules_kotlin//kotlin/internal:toolchains.bzl", "define_kt_toolchain")
2929

30-
java_binary(
31-
name = "hello",
32-
srcs = ["Hello.java"],
33-
main_class = "com.vaticle.typedb.studio.Hello",
34-
deps = [
35-
# "@maven//:org_jetbrains_compose_material_material_desktop",
36-
# "@maven//:org_jetbrains_compose_material_material_icons_core_desktop"
37-
# "@maven//:org_jetbrains_compose_ui_ui_desktop",
38-
],
39-
)
40-
41-
java_deps(
42-
name = "hello-deps",
43-
target = ":hello",
44-
java_deps_root = "lib/",
45-
maven_name = False,
46-
)
4730

48-
assemble_zip(
49-
name = "hello-bundle",
50-
targets = [":hello-deps"],
51-
output_filename = "hello-assemble",
52-
)
53-
54-
# TODO: If we remove some of these deps, IntelliJ starts to complain - we should investigate
5531
kt_jvm_library(
5632
name = "studio",
5733
srcs = ["main.kt"],
@@ -65,29 +41,12 @@ kt_jvm_library(
6541
"//workspace",
6642

6743
# Maven
68-
# "@maven//:androidx_annotation_annotation_1_2_0",
69-
# "@maven//:androidx_annotation_annotation",
70-
# "@maven//:org_jetbrains_annotations_13_0",
71-
# "@maven//:org_jetbrains_annotations",
72-
# "@maven//:org_jetbrains_compose_ui_ui_tooling_preview_desktop_1_0_0_alpha3",
73-
# "@maven//:org_jetbrains_compose_desktop_desktop_jvm",
74-
# "@maven//:org_jetbrains_compose_ui_ui_tooling_preview_desktop",
75-
# "@maven//:org_jetbrains_compose_material_material_desktop",
76-
# "@maven//:org_jetbrains_compose_material_material_ripple_desktop",
77-
# "@maven//:org_jetbrains_compose_material_material_icons_core_desktop",
78-
# "@maven//:org_jetbrains_compose_foundation_foundation_desktop",
79-
# "@maven//:org_jetbrains_compose_animation_animation_desktop",
80-
# "@maven//:org_jetbrains_compose_foundation_foundation_layout_desktop",
81-
# "@maven//:org_jetbrains_compose_animation_animation_core_desktop",
82-
# "@maven//:org_jetbrains_compose_ui_ui_desktop",
83-
# "@maven//:org_jetbrains_compose_ui_ui_text_desktop",
84-
# "@maven//:org_jetbrains_compose_ui_ui_graphics_desktop",
85-
# "@maven//:org_jetbrains_compose_ui_ui_unit_desktop",
86-
# "@maven//:org_jetbrains_compose_ui_ui_geometry_desktop",
87-
# "@maven//:org_jetbrains_compose_ui_ui_util_desktop",
88-
# "@maven//:org_jetbrains_compose_runtime_runtime_saveable_desktop",
89-
# "@maven//:org_jetbrains_compose_runtime_runtime_desktop",
90-
# "@maven//:org_jetbrains_skiko_skiko_jvm",
44+
# NOTE: These dependencies are not required to build the project, but IntelliJ needs them to offer autocompletion
45+
"@maven//:org_jetbrains_compose_desktop_desktop_jvm",
46+
"@maven//:org_jetbrains_compose_foundation_foundation_desktop",
47+
"@maven//:org_jetbrains_compose_foundation_foundation_layout_desktop",
48+
"@maven//:org_jetbrains_compose_ui_ui_desktop",
49+
"@maven//:org_jetbrains_compose_ui_ui_geometry_desktop",
9150
],
9251
resources = [
9352
"//resources:logback-xml",
@@ -198,6 +157,16 @@ jvm_application_image(
198157
mac_code_signing_cert = "@vaticle_apple_developer_id_application_cert//file",
199158
)
200159

160+
# A little misleading. Because of the way our java_deps target is generated, this will actually produce a Mac runner
161+
# if built on Mac, and fail to produce anything useful if built on Windows.
162+
assemble_targz(
163+
name = "linux-java-binary",
164+
targets = [":application-image-deps", "//binary:assemble-bash-targz"],
165+
additional_files = assemble_files,
166+
output_filename = "typedb-studio-linux-java-binary",
167+
visibility = ["//:__pkg__"]
168+
)
169+
201170
deploy_github(
202171
name = "deploy-github",
203172
organisation = deployment_github['github.organisation'],

Hello.java

Lines changed: 0 additions & 7 deletions
This file was deleted.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.4.0-alpha-2
1+
2.4.0-alpha-3

binary/BUILD

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#
2+
# Copyright (C) 2021 Vaticle
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU Affero General Public License as
6+
# published by the Free Software Foundation, either version 3 of the
7+
# License, or (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU Affero General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Affero General Public License
15+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
#
17+
#
18+
19+
load("@vaticle_bazel_distribution//common:rules.bzl", "assemble_targz")
20+
21+
exports_files(["typedb-studio"])
22+
23+
assemble_targz(
24+
name = "assemble-bash-targz",
25+
additional_files = {
26+
"//binary:typedb-studio": 'typedb-studio',
27+
},
28+
permissions = {
29+
"typedb-studio": "0755",
30+
},
31+
visibility = ["//visibility:public"]
32+
)

binary/typedb-studio

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Copyright (C) 2021 Vaticle
4+
#
5+
# This program is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU Affero General Public License as
7+
# published by the Free Software Foundation, either version 3 of the
8+
# License, or (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU Affero General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU Affero General Public License
16+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
#
18+
19+
# TypeDB Studio global variables
20+
JAVA_BIN=java
21+
if [[ ! -z "$JAVA_HOME" ]]; then
22+
JAVA_BIN="$JAVA_HOME/bin/java"
23+
fi
24+
[[ $(readlink $0) ]] && path=$(readlink $0) || path=$0
25+
STUDIO_HOME=$(cd "$(dirname "${path}")" && pwd -P)
26+
27+
# ================================================
28+
# common helper functions
29+
# ================================================
30+
exit_if_java_not_found() {
31+
which "${JAVA_BIN}" > /dev/null
32+
exit_code=$?
33+
34+
if [[ $exit_code -ne 0 ]]; then
35+
echo "Java is not installed on this machine. TypeDB Studio needs Java 11+ in order to run."
36+
exit 1
37+
fi
38+
}
39+
40+
# =============================================
41+
# main routine
42+
# =============================================
43+
44+
exit_if_java_not_found
45+
46+
LIB_DIR="${STUDIO_HOME}/lib"
47+
CLASSPATH="${LIB_DIR}/*"
48+
MAIN_CLASS=com.vaticle.typedb.studio.MainKt
49+
50+
# exec replaces current shell process with java so no commands after this one will ever get executed
51+
exec $JAVA_BIN ${JAVAOPTS} -cp "${CLASSPATH}" ${MAIN_CLASS} "${@:1}"

data/DB.kt

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@ import com.vaticle.typedb.client.api.connection.TypeDBTransaction
1515
import com.vaticle.typedb.client.api.connection.TypeDBTransaction.Type.READ
1616
import com.vaticle.typedb.client.api.logic.Explanation
1717
import com.vaticle.typedb.client.common.exception.TypeDBClientException
18-
import com.vaticle.typedb.studio.data.VertexEncoding.*
1918
import com.vaticle.typedb.studio.data.EdgeDirection.*
19+
import com.vaticle.typedb.studio.data.EdgeEncoding.*
20+
import com.vaticle.typedb.studio.data.VertexEncoding.*
2021
import com.vaticle.typeql.lang.TypeQL
2122
import com.vaticle.typeql.lang.TypeQL.match
22-
import java.lang.IllegalArgumentException
23-
import java.lang.IllegalStateException
23+
import com.vaticle.typeql.lang.query.TypeQLQuery
2424
import java.util.concurrent.CompletableFuture
2525
import java.util.concurrent.ConcurrentHashMap
2626
import java.util.concurrent.atomic.AtomicInteger
27+
import java.util.stream.Collectors
2728
import java.util.stream.Stream
2829

2930
class DB(val client: DBClient, private val dbName: String) {
@@ -50,14 +51,14 @@ class DB(val client: DBClient, private val dbName: String) {
5051
CompletableFuture.allOf(*tasks.toTypedArray()).join()
5152
}
5253

54+
// TODO: The complexity of this function is too high - we should create some kind of ConceptMapLoader class
5355
private fun loadConceptMapAsync(conceptMap: ConceptMap, explanationID: Int? = null): CompletableFuture<Void> {
5456
val tasks: MutableList<CompletableFuture<Unit>> = mutableListOf()
5557
conceptMap.map().entries.parallelStream().forEach { (varName: String, concept: Concept) ->
5658
if (concept.isThing) {
5759
val thing = concept.asThing()
5860
val vertexID: Int = vertexGenerator.things.computeIfAbsent(thing.iid) {
5961
val thingVertex: VertexData = vertexGenerator.generateVertex(thing)
60-
// println("Added a thing! $thingVertex")
6162
responseStream.putVertex(thingVertex)
6263
try {
6364
if (thing.isInferred) {
@@ -75,15 +76,27 @@ class DB(val client: DBClient, private val dbName: String) {
7576
println(e.message)
7677
}
7778

79+
thing.type.let { type ->
80+
val typeNodeID = vertexGenerator.types[type.label.name()]
81+
if (typeNodeID != null) {
82+
responseStream.putEdge(EdgeData(id = vertexGenerator.nextID(), source = thingVertex.id,
83+
target = typeNodeID, encoding = ISA, label = "isa"))
84+
} else {
85+
incompleteTypeEdges.getOrPut(type.label.name()) { mutableListOf() }
86+
.add(IncompleteEdgeData(id = vertexGenerator.nextID(), vertexID = thingVertex.id,
87+
direction = OUTGOING, encoding = ISA, label = "isa"))
88+
}
89+
}
90+
7891
tasks += LoadOwnedAttributesTask(thing, tx!!, thingVertex, vertexGenerator, responseStream, incompleteThingEdges).supplyAsync()
7992
if (thing.isRelation) {
8093
tasks += LoadRoleplayersTask(thing.asRelation().asRemote(tx), thingVertex, vertexGenerator, responseStream).supplyAsync()
8194
}
8295

8396
incompleteThingEdges.remove(thing.iid)?.forEach {
8497
val edgeData = when (it.direction) {
85-
INCOMING -> EdgeData(it.id, source = thingVertex.id, target = it.vertexID, it.label, it.inferred)
86-
OUTGOING -> EdgeData(it.id, source = it.vertexID, target = thingVertex.id, it.label, it.inferred)
98+
INCOMING -> EdgeData(it.id, source = thingVertex.id, target = it.vertexID, it.encoding, it.label, it.inferred)
99+
OUTGOING -> EdgeData(it.id, source = it.vertexID, target = thingVertex.id, it.encoding, it.label, it.inferred)
87100
}
88101
responseStream.putEdge(edgeData)
89102
}
@@ -106,8 +119,8 @@ class DB(val client: DBClient, private val dbName: String) {
106119

107120
incompleteTypeEdges.remove(thingType.label.scopedName())?.forEach {
108121
val edgeData = when (it.direction) {
109-
INCOMING -> EdgeData(it.id, source = typeVertex.id, target = it.vertexID, it.label, it.inferred)
110-
OUTGOING -> EdgeData(it.id, source = it.vertexID, target = typeVertex.id, it.label, it.inferred)
122+
INCOMING -> EdgeData(it.id, source = typeVertex.id, target = it.vertexID, it.encoding, it.label, it.inferred)
123+
OUTGOING -> EdgeData(it.id, source = it.vertexID, target = typeVertex.id, it.encoding, it.label, it.inferred)
111124
}
112125
responseStream.putEdge(edgeData)
113126
}
@@ -136,6 +149,7 @@ class DB(val client: DBClient, private val dbName: String) {
136149
vertexGenerator = VertexGenerator()
137150
CompletableFuture.supplyAsync {
138151
try {
152+
// TypeQL.parseQueries<TypeQLQuery>(query).collect(Collectors.toList())
139153
loadAnswerStream(tx!!.query().match(query))
140154
println("Completed query: $query")
141155
responseStream.completed = true
@@ -186,10 +200,6 @@ class DB(val client: DBClient, private val dbName: String) {
186200
}
187201
return responseStream
188202
}
189-
190-
fun close() {
191-
client.close()
192-
}
193203
}
194204

195205
class VertexGenerator(val things: ConcurrentHashMap<String, Int> = ConcurrentHashMap(),
@@ -267,7 +277,7 @@ private class LoadRoleplayersTask(private val relation: Relation.Remote, vertex:
267277
return@computeIfAbsent vertex.id
268278
}
269279
responseStream.putEdge(EdgeData(id = vertexGenerator.nextID(), source = vertex.id, target = rolePlayerNodeID,
270-
label = rolePlayers.key.label.name(), inferred = relation.isInferred))
280+
encoding = ROLEPLAYER, label = rolePlayers.key.label.name(), inferred = relation.isInferred))
271281
}
272282
}
273283
}
@@ -287,11 +297,11 @@ private class LoadOwnedAttributesTask(private val thing: Thing, private val tx:
287297
val attributeNodeID = vertexGenerator.things[attribute.iid]
288298
if (attributeNodeID != null) {
289299
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = vertex.id, target = attributeNodeID,
290-
label = "has", inferred = isEdgeInferred))
300+
encoding = HAS, label = "has", inferred = isEdgeInferred))
291301
} else {
292302
incompleteEdges.getOrPut(attribute.iid) { mutableListOf() }
293303
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = OUTGOING,
294-
label = "has", inferred = isEdgeInferred))
304+
encoding = HAS, label = "has", inferred = isEdgeInferred))
295305
}
296306
}
297307
}
@@ -306,10 +316,12 @@ private class LoadPlayableRolesTask(private val thingType: ThingType.Remote, ver
306316
thingType.plays.parallel().forEach { roleType ->
307317
val relationTypeNodeID = vertexGenerator.types[roleType.label.scope().get()]
308318
if (relationTypeNodeID != null) {
309-
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = relationTypeNodeID, target = vertex.id, label = roleType.label.name()))
319+
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = relationTypeNodeID,
320+
target = vertex.id, encoding = PLAYS, label = roleType.label.name()))
310321
} else {
311322
incompleteEdges.getOrPut(roleType.label.scope().get()) { mutableListOf() }
312-
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = INCOMING, label = roleType.label.name()))
323+
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = INCOMING,
324+
encoding = PLAYS, label = roleType.label.name()))
313325
}
314326
}
315327
}
@@ -324,10 +336,12 @@ private class LoadOwnedAttributeTypesTask(private val thingType: ThingType.Remot
324336
thingType.owns.parallel().forEach { attributeType ->
325337
val attributeTypeNodeID = vertexGenerator.types[attributeType.label.name()]
326338
if (attributeTypeNodeID != null) {
327-
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = vertex.id, target = attributeTypeNodeID, label = "owns"))
339+
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = vertex.id,
340+
target = attributeTypeNodeID, encoding = OWNS, label = "owns"))
328341
} else {
329342
incompleteEdges.getOrPut(attributeType.label.name()) { mutableListOf() }
330-
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = OUTGOING, label = "owns"))
343+
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = OUTGOING,
344+
encoding = OWNS, label = "owns"))
331345
}
332346
}
333347
}
@@ -342,10 +356,12 @@ private class LoadSupertypeTask(private val thingType: ThingType.Remote, vertex:
342356
thingType.supertype?.let { supertype ->
343357
val supertypeNodeID = vertexGenerator.types[supertype.label.name()]
344358
if (supertypeNodeID != null) {
345-
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = vertex.id, target = supertypeNodeID, label = "sub"))
359+
responseStream.putEdge(EdgeData(vertexGenerator.nextID(), source = vertex.id, target = supertypeNodeID,
360+
encoding = SUB, label = "sub"))
346361
} else {
347362
incompleteEdges.getOrPut(supertype.label.name()) { mutableListOf() }
348-
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = OUTGOING, label = "sub"))
363+
.add(IncompleteEdgeData(vertexGenerator.nextID(), vertexID = vertex.id, direction = OUTGOING,
364+
encoding = SUB, label = "sub"))
349365
}
350366
}
351367
}

0 commit comments

Comments
 (0)