Skip to content

Commit

Permalink
Add a library that maps FHIR primitives to their respective FHIRPath …
Browse files Browse the repository at this point in the history
…primitive types.

PiperOrigin-RevId: 320487100
  • Loading branch information
aaronnash authored and nickgeorge committed Jul 17, 2020
1 parent 7dfd19a commit 683b31d
Show file tree
Hide file tree
Showing 4 changed files with 367 additions and 0 deletions.
37 changes: 37 additions & 0 deletions cc/google/fhir/fhir_path/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@ cc_library(
],
)

cc_library(
name = "fhir_path_types",
srcs = [
"fhir_path_types.cc",
],
hdrs = [
"fhir_path_types.h",
],
strip_include_prefix = "//cc/",
visibility = [":__pkg__"],
deps = [
"//cc/google/fhir:annotations",
"//cc/google/fhir/status:statusor",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
],
)

cc_library(
name = "utils",
srcs = [
Expand Down Expand Up @@ -243,6 +262,24 @@ cc_test(
],
)

cc_test(
name = "fhir_path_types_test",
size = "small",
srcs = [
"fhir_path_types_test.cc",
],
deps = [
":fhir_path_types",
"//cc/google/fhir:type_macros",
"//cc/google/fhir:util",
"//proto/r4/core:datatypes_cc_proto",
"//proto/r4/core/resources:bundle_and_contained_resource_cc_proto",
"//proto/stu3:datatypes_cc_proto",
"//proto/stu3:resources_cc_proto",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "utils_test",
size = "small",
Expand Down
96 changes: 96 additions & 0 deletions cc/google/fhir/fhir_path/fhir_path_types.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2020 Google LLC
//
// Licensed 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:https://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.

#include "google/fhir/fhir_path/fhir_path_types.h"

#include "absl/strings/string_view.h"
#include "google/fhir/annotations.h"

namespace google::fhir::fhir_path::internal {

using ::google::protobuf::Message;

StatusOr<FhirPathSystemType> GetSystemType(
const ::google::protobuf::Message& fhir_primitive) {
static const auto* type_map =
new absl::flat_hash_map<absl::string_view, FhirPathSystemType>(
{{"http:https://hl7.org/fhir/StructureDefinition/boolean",
FhirPathSystemType::kBoolean},
{"http:https://hl7.org/fhir/StructureDefinition/string",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/uri",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/url",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/canonical",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/code",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/oid",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/id",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/uuid",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/sid",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/markdown",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/base64Binary",
FhirPathSystemType::kString},
{"http:https://hl7.org/fhir/StructureDefinition/integer",
FhirPathSystemType::kInteger},
{"http:https://hl7.org/fhir/StructureDefinition/unsignedInt",
FhirPathSystemType::kInteger},
{"http:https://hl7.org/fhir/StructureDefinition/positiveInt",
FhirPathSystemType::kInteger},
{"http:https://hl7.org/fhir/StructureDefinition/decimal",
FhirPathSystemType::kDecimal},
{"http:https://hl7.org/fhir/StructureDefinition/date",
FhirPathSystemType::kDate},
{"http:https://hl7.org/fhir/StructureDefinition/dateTime",
FhirPathSystemType::kDateTime},
{"http:https://hl7.org/fhir/StructureDefinition/instant",
FhirPathSystemType::kDateTime},
{"http:https://hl7.org/fhir/StructureDefinition/time",
FhirPathSystemType::kTime},
{"http:https://hl7.org/fhir/StructureDefinition/Quantity",
FhirPathSystemType::kQuantity}});

auto type =
type_map->find(GetStructureDefinitionUrl(fhir_primitive.GetDescriptor()));
if (type == type_map->end()) {
return absl::NotFoundError(
absl::StrCat(fhir_primitive.GetTypeName(),
" does not map to a FHIRPath primitive type."));
}
return type->second;
}

bool IsSystemInteger(const Message& message) {
StatusOr<FhirPathSystemType> type = GetSystemType(message);
return type.ok() && type.ValueOrDie() == FhirPathSystemType::kInteger;
}

bool IsSystemString(const Message& message) {
StatusOr<FhirPathSystemType> type = GetSystemType(message);
return type.ok() && type.ValueOrDie() == FhirPathSystemType::kString;
}

bool IsSystemDecimal(const Message& message) {
StatusOr<FhirPathSystemType> type = GetSystemType(message);
return type.ok() && type.ValueOrDie() == FhirPathSystemType::kDecimal;
}

} // namespace google::fhir::fhir_path::internal
62 changes: 62 additions & 0 deletions cc/google/fhir/fhir_path/fhir_path_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2020 Google LLC
//
// Licensed 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:https://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.

#ifndef GOOGLE_FHIR_FHIR_PATH_FHIR_PATH_TYPES_H_
#define GOOGLE_FHIR_FHIR_PATH_FHIR_PATH_TYPES_H_

#include "google/protobuf/message.h"
#include "absl/container/flat_hash_map.h"
#include "google/fhir/status/statusor.h"

namespace google::fhir::fhir_path::internal {
enum class FhirPathSystemType {
kBoolean = 1,
kString = 2,
kInteger = 3,
kDecimal = 4,
kDate = 5,
kDateTime = 6,
kTime = 7,
kQuantity = 8
};

// Returns the FHIRPath primitive type the provided FHIR primitive maps to.
// Returns a NotFoundError if no mapping exists between the provided FHIR type
// and a FHIRPath primitive type.
//
// See https://www.hl7.org/fhir/fhirpath.html#types
StatusOr<FhirPathSystemType> GetSystemType(
const ::google::protobuf::Message& fhir_primitive);

// Returns true if the provided FHIR message converts to System.Integer. False
// otherwise.
//
// See https://www.hl7.org/fhir/fhirpath.html#types
bool IsSystemInteger(const ::google::protobuf::Message& message);

// Returns true if the provided FHIR message converts to System.String. False
// otherwise.
//
// See https://www.hl7.org/fhir/fhirpath.html#types
bool IsSystemString(const ::google::protobuf::Message& message);

// Returns true if the provided FHIR message converts to System.Decimal. False
// otherwise.
//
// See https://www.hl7.org/fhir/fhirpath.html#types
bool IsSystemDecimal(const ::google::protobuf::Message& message);

} // namespace google::fhir::fhir_path::internal

#endif // GOOGLE_FHIR_FHIR_PATH_FHIR_PATH_TYPES_H_
172 changes: 172 additions & 0 deletions cc/google/fhir/fhir_path/fhir_path_types_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2020 Google LLC
//
// Licensed 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:https://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.

#include "google/fhir/fhir_path/fhir_path_types.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "google/fhir/util.h"
#include "google/fhir/type_macros.h"
#include "proto/r4/core/datatypes.pb.h"
#include "proto/r4/core/resources/bundle_and_contained_resource.pb.h"
#include "proto/stu3/datatypes.pb.h"
#include "proto/stu3/resources.pb.h"

namespace google::fhir::fhir_path::internal {

using testing::Not;
using testing::Pointee;
using testing::Truly;

template <typename T>
void TestType(FhirPathSystemType expected_type) {
T proto;
StatusOr<FhirPathSystemType> system_type = GetSystemType(proto);

if (!system_type.ok()) {
ADD_FAILURE() << "No system type defined for " << proto.GetTypeName()
<< ": " << system_type.status();
}

EXPECT_EQ(system_type.ValueOrDie(), expected_type)
<< "Wrong system type for " << proto.GetTypeName();
}

template <typename... T>
struct FhirPrimitiveTypes {
static void VerifySystemTypeMapping(FhirPathSystemType expected_type) {
(TestType<T>(expected_type), ...);
}

static std::vector<std::unique_ptr<::google::protobuf::Message>> GetInstances() {
std::vector<std::unique_ptr<::google::protobuf::Message>> results;
(results.push_back(std::make_unique<T>()), ...);
return results;
}
};

template <typename BundleType, typename... AdditionalStringTypes>
struct FhirStringTypes
: public FhirPrimitiveTypes<
FHIR_DATATYPE(BundleType, string_value),
FHIR_DATATYPE(BundleType, uri), FHIR_DATATYPE(BundleType, code),
FHIR_DATATYPE(BundleType, oid), FHIR_DATATYPE(BundleType, id),
FHIR_DATATYPE(BundleType, markdown),
FHIR_DATATYPE(BundleType, base64_binary), AdditionalStringTypes...> {
};

template <typename BundleType>
struct FhirTypes {
using Booleans = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, boolean)>;

using Integers = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, integer),
FHIR_DATATYPE(BundleType, unsigned_int),
FHIR_DATATYPE(BundleType, positive_int)>;

using Strings = FhirStringTypes<BundleType>;

using Decimals = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, decimal)>;

using Dates = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, date)>;

using DateTimes = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, date_time),
FHIR_DATATYPE(BundleType, instant)>;

using Times = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, time)>;

using Quantities = FhirPrimitiveTypes<FHIR_DATATYPE(BundleType, quantity)>;
};

struct Stu3Types : public FhirTypes<stu3::proto::Bundle> {};

struct R4Types : public FhirTypes<r4::core::Bundle> {
using Strings = FhirStringTypes<r4::core::Bundle, r4::core::Uuid,
r4::core::Canonical, r4::core::Url>;
};

template <typename T>
class FhirPathTypesTest : public ::testing::Test {};

using TestTypes = ::testing::Types<Stu3Types, R4Types>;
TYPED_TEST_SUITE(FhirPathTypesTest, TestTypes);

TYPED_TEST(FhirPathTypesTest, GetSystemType) {
TypeParam::Booleans::VerifySystemTypeMapping(FhirPathSystemType::kBoolean);
TypeParam::Integers::VerifySystemTypeMapping(FhirPathSystemType::kInteger);
TypeParam::Strings::VerifySystemTypeMapping(FhirPathSystemType::kString);
TypeParam::Decimals::VerifySystemTypeMapping(FhirPathSystemType::kDecimal);
TypeParam::Dates::VerifySystemTypeMapping(FhirPathSystemType::kDate);
TypeParam::DateTimes::VerifySystemTypeMapping(FhirPathSystemType::kDateTime);
TypeParam::Times::VerifySystemTypeMapping(FhirPathSystemType::kTime);
TypeParam::Quantities::VerifySystemTypeMapping(FhirPathSystemType::kQuantity);
}

TYPED_TEST(FhirPathTypesTest, IsSystemInteger) {
EXPECT_THAT(TypeParam::Booleans::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
EXPECT_THAT(TypeParam::Integers::GetInstances(),
Each(Pointee(Truly(IsSystemInteger))));
EXPECT_THAT(TypeParam::Strings::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
EXPECT_THAT(TypeParam::Decimals::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
EXPECT_THAT(TypeParam::Dates::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
EXPECT_THAT(TypeParam::DateTimes::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
EXPECT_THAT(TypeParam::Times::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
EXPECT_THAT(TypeParam::Quantities::GetInstances(),
Each(Pointee(Not(Truly(IsSystemInteger)))));
}

TYPED_TEST(FhirPathTypesTest, IsSystemString) {
EXPECT_THAT(TypeParam::Booleans::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
EXPECT_THAT(TypeParam::Integers::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
EXPECT_THAT(TypeParam::Strings::GetInstances(),
Each(Pointee(Truly(IsSystemString))));
EXPECT_THAT(TypeParam::Decimals::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
EXPECT_THAT(TypeParam::Dates::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
EXPECT_THAT(TypeParam::DateTimes::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
EXPECT_THAT(TypeParam::Times::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
EXPECT_THAT(TypeParam::Quantities::GetInstances(),
Each(Pointee(Not(Truly(IsSystemString)))));
}

TYPED_TEST(FhirPathTypesTest, IsSystemDecimal) {
EXPECT_THAT(TypeParam::Booleans::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
EXPECT_THAT(TypeParam::Integers::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
EXPECT_THAT(TypeParam::Strings::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
EXPECT_THAT(TypeParam::Decimals::GetInstances(),
Each(Pointee(Truly(IsSystemDecimal))));
EXPECT_THAT(TypeParam::Dates::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
EXPECT_THAT(TypeParam::DateTimes::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
EXPECT_THAT(TypeParam::Times::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
EXPECT_THAT(TypeParam::Quantities::GetInstances(),
Each(Pointee(Not(Truly(IsSystemDecimal)))));
}

} // namespace google::fhir::fhir_path::internal

0 comments on commit 683b31d

Please sign in to comment.