Fix serialization of the NodeFieldTypeMappings (#260)

V3_GIT_ORIGIN_REV_ID: 10ff7b35139fc48e618270758d3f805890039c53
This commit is contained in:
Karthikeyan Chinnakonda 2023-12-22 18:21:36 +05:30 committed by hasura-bot
parent 048ddbd33d
commit f53e329eb1
5 changed files with 112 additions and 8 deletions

View File

@ -16,6 +16,7 @@ use crate::metadata::resolved;
use crate::metadata::resolved::subgraph::Qualified;
use crate::schema::types::{GlobalID, NamespaceAnnotation, NodeFieldTypeNameMapping};
use crate::schema::GDS;
use crate::utils::HashMapWithJsonKey;
/// IR for the 'select_one' operation on a model
#[derive(Serialize, Debug)]
@ -39,8 +40,10 @@ pub struct NodeSelect<'n, 's> {
fn get_relay_node_namespace_typename_mappings<'s>(
field_call: &normalized_ast::FieldCall<'s, GDS>,
) -> Result<&'s HashMap<Qualified<CustomTypeName>, resolved::model::FilterPermission>, error::Error>
{
) -> Result<
&'s HashMapWithJsonKey<Qualified<CustomTypeName>, resolved::model::FilterPermission>,
error::Error,
> {
field_call
.info
.namespaced
@ -88,7 +91,7 @@ pub(crate) fn relay_node_ir<'n, 's>(
let typename_permissions: &'s HashMap<
Qualified<CustomTypeName>,
resolved::model::FilterPermission,
> = get_relay_node_namespace_typename_mappings(field_call)?;
> = &get_relay_node_namespace_typename_mappings(field_call)?.0;
let typename_mapping = typename_mappings.get(&global_id.typename).ok_or(
error::InternalDeveloperError::GlobalIDTypenameMappingNotFound {
type_name: global_id.typename.clone(),

View File

@ -17,6 +17,7 @@ use crate::schema::types::{
Annotation, NodeFieldTypeNameMapping, OutputAnnotation, RootFieldAnnotation,
};
use crate::schema::{Role, GDS};
use crate::utils::HashMapWithJsonKey;
pub(crate) struct RelayNodeFieldOutput {
pub relay_node_gql_field: gql_schema::Field<GDS>,
@ -111,7 +112,7 @@ pub(crate) fn relay_node_field(
relay_node_field_permissions.insert(
role.clone(),
Some(types::NamespaceAnnotation::NodeFieldTypeMappings(
role_type_permission,
HashMapWithJsonKey(role_type_permission),
)),
);
}

View File

@ -11,9 +11,12 @@ use std::{
use open_dds::{commands, models, types};
use crate::metadata::resolved::{
self,
subgraph::{Qualified, QualifiedTypeReference},
use crate::{
metadata::resolved::{
self,
subgraph::{Qualified, QualifiedTypeReference},
},
utils::HashMapWithJsonKey,
};
use strum_macros::Display;
@ -183,7 +186,7 @@ pub enum NamespaceAnnotation {
/// decoding, a typename will be obtained. We need to use that typename to look up the
/// Hashmap to get the appropriate `resolved::model::FilterPermission`.
NodeFieldTypeMappings(
HashMap<Qualified<types::CustomTypeName>, resolved::model::FilterPermission>,
HashMapWithJsonKey<Qualified<types::CustomTypeName>, resolved::model::FilterPermission>,
),
}

View File

@ -1 +1,92 @@
pub mod json_ext;
use serde::{de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize};
use std::{collections::HashMap, hash::Hash};
/// HashMapWithJsonKey serializes the key<K> as a String
/// during serialization and similarly while deserialization, the keys
/// are expected to be Stringified and it is then deserialized into K.
/// This type can be helpful when a HashMap<K,V> needs to be serialized,
/// where K is a custom struct and which serializes into a JSON object
/// by default, serializing such a HashMap into JSON will throw an error
/// because JSON spec mandates that the keys in a JSON object be keys.
/// So, wrapping the HashMap with this type would serialize the Hashmap's
/// keys as strings and deserialize correspondingly.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HashMapWithJsonKey<K: Serialize + Eq + Hash + for<'a> Deserialize<'a>, V>(
pub HashMap<K, V>,
);
impl<K: Serialize + for<'a> Deserialize<'a> + Eq + Hash, V: Serialize> Serialize
for HashMapWithJsonKey<K, V>
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (k, v) in self.0.iter() {
let stringified_key = serde_json::to_string(k).map_err(serde::ser::Error::custom)?;
map.serialize_entry(&stringified_key, v)?;
}
map.end()
}
}
impl<'de, K: DeserializeOwned + Hash + Eq + Serialize, V: Deserialize<'de>> Deserialize<'de>
for HashMapWithJsonKey<K, V>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let map: HashMap<String, V> = Deserialize::deserialize(deserializer)?;
let mut result = HashMap::new();
for (k, v) in map.into_iter() {
let k_str = serde_json::from_str(&k).map_err(serde::de::Error::custom)?;
result.insert(k_str, v);
}
Ok(HashMapWithJsonKey(result))
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::utils::HashMapWithJsonKey;
#[test]
fn test_hashmap_with_serializable_key() {
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Debug, Clone)]
struct Foo {
x: u32,
}
let mut test_map: HashMap<Foo, String> = HashMap::new();
test_map.insert(Foo { x: 1 }, "hello".to_string());
// The `test_map` cannot be serialized as is because
// the key by default is serialized to a JSON object
// and the JSON spec mandates that in a JSON object, the
// key must be a String.
assert!(serde_json::to_string(&test_map).is_err());
let test_map_with_serializable_key = HashMapWithJsonKey(test_map.clone());
let serialized_test_map_with_serializable_key =
serde_json::to_string(&test_map_with_serializable_key).unwrap();
assert_eq!(
serialized_test_map_with_serializable_key,
"{\"{\\\"x\\\":1}\":\"hello\"}"
);
let deserialized_test_map_with_serializable_key: HashMapWithJsonKey<Foo, String> =
serde_json::from_str(&serialized_test_map_with_serializable_key).unwrap();
assert_eq!(test_map, deserialized_test_map_with_serializable_key.0);
}
}

View File

@ -65,6 +65,9 @@ pub fn test_execution_expectation_legacy(test_path_string: &str, common_metadata
let gds = GDS::new(serde_json::from_value(metadata).unwrap()).unwrap();
let schema = GDS::build_schema(&gds).unwrap();
// Ensure schema is serialized successfully.
serde_json::to_string(&schema).unwrap();
let query = fs::read_to_string(request_path).unwrap();
let session = {
@ -129,6 +132,9 @@ pub fn test_execution_expectation(test_path_string: &str, common_metadata_paths:
let gds = GDS::new(serde_json::from_value(metadata).unwrap()).unwrap();
let schema = GDS::build_schema(&gds).unwrap();
// Ensure schema is serialized successfully.
serde_json::to_string(&schema).unwrap();
let query = fs::read_to_string(request_path).unwrap();
let session_vars_path = &test_path.join("session_variables.json");