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

Commit

Permalink
Concept map JSON serialization (#285)
Browse files Browse the repository at this point in the history
## What is the goal of this PR?

We implement the JSON serialization of concept maps according to
typedb/typedb-behaviour#238.
  • Loading branch information
dmitrii-ubskii authored Mar 24, 2023
1 parent c24395f commit e65a06b
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 6 deletions.
2 changes: 1 addition & 1 deletion dependencies/vaticle/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ def vaticle_typedb_behaviour():
git_repository(
name = "vaticle_typedb_behaviour",
remote = "https://github.com/vaticle/typedb-behaviour",
commit = "04ec1f75cc376524b3cac75af37e1716230845e0" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour
commit = "46619244d5f1773505eeacf6d5bd0ae71781f8e8" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour
)
62 changes: 62 additions & 0 deletions tests/behaviour/concept/serialization/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#
# Copyright (C) 2022 Vaticle
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

package(default_visibility = ["//tests/behaviour:__subpackages__"])
load("//tools:behave_rule.bzl", "typedb_behaviour_py_test")
load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test")

py_library(
name = "steps",
srcs = ["serialization_steps.py"],
deps = [],
)

typedb_behaviour_py_test(
name = "test",
feats = ["@vaticle_typedb_behaviour//concept:serialization.feature"],
background_core = ["//tests/behaviour/background:core"],
background_cluster = ["//tests/behaviour/background:cluster"],
steps = [
":steps",
"//tests/behaviour/typeql:steps",
"//tests/behaviour/connection:steps",
"//tests/behaviour/connection/database:steps",
"//tests/behaviour/connection/session:steps",
"//tests/behaviour/connection/transaction:steps",
],
deps = [
"//:client_python",
"//tests/behaviour:context",
"//tests/behaviour/util:util",
"//tests/behaviour/config:parameters",
"//tests/behaviour/background",
],
native_typedb_artifact = "//tests:native-typedb-artifact",
native_typedb_cluster_artifact = "//tests:native-typedb-cluster-artifact",
size = "medium",
)

checkstyle_test(
name = "checkstyle",
include = glob(["*"]),
license_type = "apache-header",
size = "small",
)
55 changes: 55 additions & 0 deletions tests/behaviour/concept/serialization/serialization_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#
# Copyright (C) 2022 Vaticle
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

import json
from collections import Counter
from hamcrest.core.base_matcher import BaseMatcher
from typing import TypeVar, List, Generic

from behave import *
from hamcrest import *

from tests.behaviour.context import Context

T = TypeVar('T')


class UnorderedEqualTo(BaseMatcher, Generic[T]):
def __init__(self, expected: List[T]):
self.expected = Counter([json.dumps(item, sort_keys=True) for item in expected])

def _matches(self, actual: List[T]) -> bool:
actual = Counter([json.dumps(item, sort_keys=True) for item in actual])
return actual == self.expected

def describe_to(self, description):
description.append_text('is equal, in any order, to ').append_text(repr(self.expected))


def unordered_equal_to(expected: List[T]) -> UnorderedEqualTo[T]:
return UnorderedEqualTo(expected)


@step("JSON of answer concepts matches")
def step_impl(context: Context):
expected = json.loads(context.text)
actual = [answer.json() for answer in context.answers]
assert_that(actual, unordered_equal_to(expected))
8 changes: 7 additions & 1 deletion typedb/api/answer/concept_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# under the License.
#
from abc import ABC, abstractmethod
from typing import Mapping, Iterable, Tuple
from typing import Iterable, Mapping, Tuple, Union

from typedb.api.concept.concept import Concept

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

def json(self) -> Mapping[str, Mapping[str, Union[str, int, float, bool]]]:
return {
var: concept.json()
for var, concept in self._map.items()
}

class Explainables(ABC):

@abstractmethod
Expand Down
6 changes: 5 additions & 1 deletion typedb/api/concept/concept.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# under the License.
#
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from typing import Mapping, Union, TYPE_CHECKING

from typedb.common.exception import TypeDBClientException, INVALID_CONCEPT_CASTING

Expand Down Expand Up @@ -107,6 +107,10 @@ def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteConcept":
def is_remote(self) -> bool:
pass

@abstractmethod
def json(self) -> Mapping[str, Union[str, int, float, bool]]:
pass


class RemoteConcept(Concept, ABC):

Expand Down
16 changes: 15 additions & 1 deletion typedb/api/concept/thing/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#
from abc import ABC, abstractmethod
from datetime import datetime
from typing import TYPE_CHECKING, Iterator, Union
from typing import TYPE_CHECKING, Iterator, Mapping, Union

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

Expand Down Expand Up @@ -62,6 +62,13 @@ def is_datetime(self):
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteAttribute":
pass

def json(self) -> Mapping[str, Union[str, int, float, bool]]:
return {
"type": self.get_type().get_label().name(),
"value_type": str(self.get_type().get_value_type()),
"value": self.get_value(),
}


class RemoteAttribute(RemoteThing, Attribute, ABC):

Expand Down Expand Up @@ -175,6 +182,13 @@ def get_value(self) -> datetime:
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteDateTimeAttribute":
pass

def json(self) -> Mapping[str, Union[str, int, float, bool]]:
return {
"type": self.get_type().get_label().name(),
"value_type": str(self.get_type().get_value_type()),
"value": self.get_value().isoformat(timespec='milliseconds')
}


class RemoteDateTimeAttribute(RemoteAttribute, DateTimeAttribute, ABC):
pass
5 changes: 4 additions & 1 deletion typedb/api/concept/thing/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# under the License.
#
from abc import ABC, abstractmethod
from typing import List, TYPE_CHECKING, Iterator
from typing import List, TYPE_CHECKING, Iterator, Mapping

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

Expand Down Expand Up @@ -52,6 +52,9 @@ def is_thing(self) -> bool:
def as_remote(self, transaction: "TypeDBTransaction") -> "RemoteThing":
pass

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


class RemoteThing(RemoteConcept, Thing, ABC):

Expand Down
3 changes: 3 additions & 0 deletions typedb/api/concept/type/attribute_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ def is_keyable(self) -> bool:
def proto(self) -> concept_proto.AttributeType.ValueType:
return concept_proto.AttributeType.ValueType.Value(self.name)

def __str__(self):
return self.name.lower()


class RemoteAttributeType(RemoteThingType, AttributeType, ABC):

Expand Down
5 changes: 4 additions & 1 deletion typedb/api/concept/type/type.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# under the License.
#
from abc import ABC, abstractmethod
from typing import Iterator, TYPE_CHECKING
from typing import Iterator, Mapping, TYPE_CHECKING

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

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


class RemoteType(RemoteConcept, Type, ABC):

Expand Down

0 comments on commit e65a06b

Please sign in to comment.