Skip to content
This repository was archived by the owner on Oct 9, 2023. It is now read-only.

Commit e65a06b

Browse files
Concept map JSON serialization (#285)
## What is the goal of this PR? We implement the JSON serialization of concept maps according to typedb/typedb-behaviour#238.
1 parent c24395f commit e65a06b

File tree

9 files changed

+156
-6
lines changed

9 files changed

+156
-6
lines changed

dependencies/vaticle/repositories.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ def vaticle_typedb_behaviour():
3939
git_repository(
4040
name = "vaticle_typedb_behaviour",
4141
remote = "https://github.com/vaticle/typedb-behaviour",
42-
commit = "04ec1f75cc376524b3cac75af37e1716230845e0" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour
42+
commit = "46619244d5f1773505eeacf6d5bd0ae71781f8e8" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour
4343
)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#
2+
# Copyright (C) 2022 Vaticle
3+
#
4+
# Licensed to the Apache Software Foundation (ASF) under one
5+
# or more contributor license agreements. See the NOTICE file
6+
# distributed with this work for additional information
7+
# regarding copyright ownership. The ASF licenses this file
8+
# to you under the Apache License, Version 2.0 (the
9+
# "License"); you may not use this file except in compliance
10+
# with the License. You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing,
15+
# software distributed under the License is distributed on an
16+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
# KIND, either express or implied. See the License for the
18+
# specific language governing permissions and limitations
19+
# under the License.
20+
#
21+
22+
package(default_visibility = ["//tests/behaviour:__subpackages__"])
23+
load("//tools:behave_rule.bzl", "typedb_behaviour_py_test")
24+
load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test")
25+
26+
py_library(
27+
name = "steps",
28+
srcs = ["serialization_steps.py"],
29+
deps = [],
30+
)
31+
32+
typedb_behaviour_py_test(
33+
name = "test",
34+
feats = ["@vaticle_typedb_behaviour//concept:serialization.feature"],
35+
background_core = ["//tests/behaviour/background:core"],
36+
background_cluster = ["//tests/behaviour/background:cluster"],
37+
steps = [
38+
":steps",
39+
"//tests/behaviour/typeql:steps",
40+
"//tests/behaviour/connection:steps",
41+
"//tests/behaviour/connection/database:steps",
42+
"//tests/behaviour/connection/session:steps",
43+
"//tests/behaviour/connection/transaction:steps",
44+
],
45+
deps = [
46+
"//:client_python",
47+
"//tests/behaviour:context",
48+
"//tests/behaviour/util:util",
49+
"//tests/behaviour/config:parameters",
50+
"//tests/behaviour/background",
51+
],
52+
native_typedb_artifact = "//tests:native-typedb-artifact",
53+
native_typedb_cluster_artifact = "//tests:native-typedb-cluster-artifact",
54+
size = "medium",
55+
)
56+
57+
checkstyle_test(
58+
name = "checkstyle",
59+
include = glob(["*"]),
60+
license_type = "apache-header",
61+
size = "small",
62+
)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#
2+
# Copyright (C) 2022 Vaticle
3+
#
4+
# Licensed to the Apache Software Foundation (ASF) under one
5+
# or more contributor license agreements. See the NOTICE file
6+
# distributed with this work for additional information
7+
# regarding copyright ownership. The ASF licenses this file
8+
# to you under the Apache License, Version 2.0 (the
9+
# "License"); you may not use this file except in compliance
10+
# with the License. You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing,
15+
# software distributed under the License is distributed on an
16+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
# KIND, either express or implied. See the License for the
18+
# specific language governing permissions and limitations
19+
# under the License.
20+
#
21+
22+
import json
23+
from collections import Counter
24+
from hamcrest.core.base_matcher import BaseMatcher
25+
from typing import TypeVar, List, Generic
26+
27+
from behave import *
28+
from hamcrest import *
29+
30+
from tests.behaviour.context import Context
31+
32+
T = TypeVar('T')
33+
34+
35+
class UnorderedEqualTo(BaseMatcher, Generic[T]):
36+
def __init__(self, expected: List[T]):
37+
self.expected = Counter([json.dumps(item, sort_keys=True) for item in expected])
38+
39+
def _matches(self, actual: List[T]) -> bool:
40+
actual = Counter([json.dumps(item, sort_keys=True) for item in actual])
41+
return actual == self.expected
42+
43+
def describe_to(self, description):
44+
description.append_text('is equal, in any order, to ').append_text(repr(self.expected))
45+
46+
47+
def unordered_equal_to(expected: List[T]) -> UnorderedEqualTo[T]:
48+
return UnorderedEqualTo(expected)
49+
50+
51+
@step("JSON of answer concepts matches")
52+
def step_impl(context: Context):
53+
expected = json.loads(context.text)
54+
actual = [answer.json() for answer in context.answers]
55+
assert_that(actual, unordered_equal_to(expected))

typedb/api/answer/concept_map.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# under the License.
2020
#
2121
from abc import ABC, abstractmethod
22-
from typing import Mapping, Iterable, Tuple
22+
from typing import Iterable, Mapping, Tuple, Union
2323

2424
from typedb.api.concept.concept import Concept
2525

@@ -42,6 +42,12 @@ def get(self, variable: str) -> Concept:
4242
def explainables(self) -> "ConceptMap.Explainables":
4343
pass
4444

45+
def json(self) -> Mapping[str, Mapping[str, Union[str, int, float, bool]]]:
46+
return {
47+
var: concept.json()
48+
for var, concept in self._map.items()
49+
}
50+
4551
class Explainables(ABC):
4652

4753
@abstractmethod

typedb/api/concept/concept.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# under the License.
2020
#
2121
from abc import ABC, abstractmethod
22-
from typing import TYPE_CHECKING
22+
from typing import Mapping, Union, TYPE_CHECKING
2323

2424
from typedb.common.exception import TypeDBClientException, INVALID_CONCEPT_CASTING
2525

@@ -107,6 +107,10 @@ def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteConcept":
107107
def is_remote(self) -> bool:
108108
pass
109109

110+
@abstractmethod
111+
def json(self) -> Mapping[str, Union[str, int, float, bool]]:
112+
pass
113+
110114

111115
class RemoteConcept(Concept, ABC):
112116

typedb/api/concept/thing/attribute.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#
2121
from abc import ABC, abstractmethod
2222
from datetime import datetime
23-
from typing import TYPE_CHECKING, Iterator, Union
23+
from typing import TYPE_CHECKING, Iterator, Mapping, Union
2424

2525
from typedb.api.concept.thing.thing import Thing, RemoteThing
2626

@@ -62,6 +62,13 @@ def is_datetime(self):
6262
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteAttribute":
6363
pass
6464

65+
def json(self) -> Mapping[str, Union[str, int, float, bool]]:
66+
return {
67+
"type": self.get_type().get_label().name(),
68+
"value_type": str(self.get_type().get_value_type()),
69+
"value": self.get_value(),
70+
}
71+
6572

6673
class RemoteAttribute(RemoteThing, Attribute, ABC):
6774

@@ -175,6 +182,13 @@ def get_value(self) -> datetime:
175182
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteDateTimeAttribute":
176183
pass
177184

185+
def json(self) -> Mapping[str, Union[str, int, float, bool]]:
186+
return {
187+
"type": self.get_type().get_label().name(),
188+
"value_type": str(self.get_type().get_value_type()),
189+
"value": self.get_value().isoformat(timespec='milliseconds')
190+
}
191+
178192

179193
class RemoteDateTimeAttribute(RemoteAttribute, DateTimeAttribute, ABC):
180194
pass

typedb/api/concept/thing/thing.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# under the License.
2020
#
2121
from abc import ABC, abstractmethod
22-
from typing import List, TYPE_CHECKING, Iterator
22+
from typing import List, TYPE_CHECKING, Iterator, Mapping
2323

2424
from typedb.api.concept.concept import Concept, RemoteConcept
2525

@@ -52,6 +52,9 @@ def is_thing(self) -> bool:
5252
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteThing":
5353
pass
5454

55+
def json(self) -> Mapping[str, str]:
56+
return {"type": self.get_type().get_label().name()}
57+
5558

5659
class RemoteThing(RemoteConcept, Thing, ABC):
5760

typedb/api/concept/type/attribute_type.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def is_keyable(self) -> bool:
9797
def proto(self) -> concept_proto.AttributeType.ValueType:
9898
return concept_proto.AttributeType.ValueType.Value(self.name)
9999

100+
def __str__(self):
101+
return self.name.lower()
102+
100103

101104
class RemoteAttributeType(RemoteThingType, AttributeType, ABC):
102105

typedb/api/concept/type/type.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# under the License.
2020
#
2121
from abc import ABC, abstractmethod
22-
from typing import Iterator, TYPE_CHECKING
22+
from typing import Iterator, Mapping, TYPE_CHECKING
2323

2424
from typedb.api.concept.concept import Concept, RemoteConcept
2525
from typedb.common.label import Label
@@ -49,6 +49,9 @@ def is_type(self) -> bool:
4949
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteType":
5050
pass
5151

52+
def json(self) -> Mapping[str, str]:
53+
return {"label": self.get_label().scoped_name()}
54+
5255

5356
class RemoteType(RemoteConcept, Type, ABC):
5457

0 commit comments

Comments
 (0)