mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
move serializable http types to a separate module (#633)
## Description We use few HTTP types (URL, http header name, value, header map etc.) in the resolved metadata, which needs to be serialized. We have newtype wrappers around them to implement serialization/deserialization. This PR moves them to a separate helper module. And also introduces a new type `SerializableHeaderName` which is required in an upcoming PR of forwarding request headers to NDC. Functional no-op. V3_GIT_ORIGIN_REV_ID: 4f907a652a9826bc52996fa37d2a7590f24ee30a
This commit is contained in:
parent
d7f1753c65
commit
dd2e2847d3
178
v3/crates/metadata-resolve/src/helpers/http.rs
Normal file
178
v3/crates/metadata-resolve/src/helpers/http.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//! Wrapper over HTTP types which can serialized/deserialized
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use open_dds::EnvironmentValue;
|
||||||
|
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||||
|
use serde::{
|
||||||
|
de::Error as DeError,
|
||||||
|
ser::{Error as SerError, SerializeMap},
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::BTreeMap, fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum HeaderError {
|
||||||
|
InvalidHeaderName { header_name: String },
|
||||||
|
InvalidHeaderValue { header_name: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for HeaderError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
HeaderError::InvalidHeaderName { header_name } => {
|
||||||
|
write!(f, "invalid HTTP header name: {}", header_name)
|
||||||
|
}
|
||||||
|
HeaderError::InvalidHeaderValue { header_name } => {
|
||||||
|
write!(f, "invalid HTTP header value: {}", header_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct SerializableUrl(pub reqwest::Url);
|
||||||
|
|
||||||
|
impl SerializableUrl {
|
||||||
|
pub fn new(url: &str) -> Result<Self, url::ParseError> {
|
||||||
|
let url = reqwest::Url::parse(url)?;
|
||||||
|
Ok(Self(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SerializableUrl {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SerializableUrl {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let url_str = String::deserialize(deserializer)?;
|
||||||
|
let url = reqwest::Url::parse(&url_str)
|
||||||
|
.map_err(|_| D::Error::custom(format!("Invalid URL: {url_str}")))?;
|
||||||
|
Ok(SerializableUrl(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct SerializableHeaderName(pub HeaderName);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl SerializableHeaderName {
|
||||||
|
pub fn new(header_name_str: String) -> Result<Self, HeaderError> {
|
||||||
|
let header_name =
|
||||||
|
HeaderName::from_str(&header_name_str).map_err(|_| HeaderError::InvalidHeaderName {
|
||||||
|
header_name: header_name_str,
|
||||||
|
})?;
|
||||||
|
Ok(SerializableHeaderName(header_name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SerializableHeaderName {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SerializableHeaderName {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let header_str = String::deserialize(deserializer)?;
|
||||||
|
SerializableHeaderName::new(header_str).map_err(D::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct SerializableHeaderMap(pub HeaderMap);
|
||||||
|
|
||||||
|
impl SerializableHeaderMap {
|
||||||
|
pub fn new(headers: &IndexMap<String, EnvironmentValue>) -> Result<Self, HeaderError> {
|
||||||
|
let header_map = headers
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
Ok((
|
||||||
|
HeaderName::from_str(k).map_err(|_| HeaderError::InvalidHeaderName {
|
||||||
|
header_name: k.clone(),
|
||||||
|
})?,
|
||||||
|
HeaderValue::from_str(&v.value).map_err(|_| {
|
||||||
|
HeaderError::InvalidHeaderValue {
|
||||||
|
header_name: k.clone(),
|
||||||
|
}
|
||||||
|
})?,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<HeaderMap, HeaderError>>()?;
|
||||||
|
Ok(Self(header_map))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SerializableHeaderMap {
|
||||||
|
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 {
|
||||||
|
map.serialize_entry(k.as_str(), v.to_str().map_err(S::Error::custom)?)?;
|
||||||
|
}
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SerializableHeaderMap {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let hash_map = BTreeMap::<String, String>::deserialize(deserializer)?;
|
||||||
|
let header_map: HeaderMap = hash_map
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
Ok((
|
||||||
|
HeaderName::from_str(&k).map_err(D::Error::custom)?,
|
||||||
|
HeaderValue::from_str(&v).map_err(D::Error::custom)?,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<HeaderMap, D::Error>>()?;
|
||||||
|
Ok(SerializableHeaderMap(header_map))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_name_serialization_deserialization() {
|
||||||
|
let headers_str = json!([
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz0123456789_#.~!$&'*+",
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz0123456789_#.~!$&'*+",
|
||||||
|
]);
|
||||||
|
let headers: Vec<SerializableHeaderName> = serde_json::from_value(headers_str).unwrap();
|
||||||
|
let serialized_headers = serde_json::to_string(&headers).unwrap();
|
||||||
|
let deserialized_headers: Vec<SerializableHeaderName> =
|
||||||
|
serde_json::from_str(&serialized_headers).unwrap();
|
||||||
|
assert_eq!(deserialized_headers, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_map_serialization_deserialization() {
|
||||||
|
let headers_str = r#"{"some_header_name":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~!#$&'()*+,/:;=?@[]\""}"#;
|
||||||
|
let headers: SerializableHeaderMap = serde_json::from_str(headers_str).unwrap();
|
||||||
|
let serialized_headers = serde_json::to_string(&headers).unwrap();
|
||||||
|
assert_eq!(headers_str, serialized_headers);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
//! Functions that are shared between metadata stages
|
//! Functions that are shared between metadata stages
|
||||||
pub mod argument;
|
pub mod argument;
|
||||||
|
pub mod http;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod ndc_validation;
|
pub mod ndc_validation;
|
||||||
pub mod type_mappings;
|
pub mod type_mappings;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
use crate::helpers::http::{HeaderError, SerializableHeaderMap, SerializableUrl};
|
||||||
use crate::types::error::Error;
|
use crate::types::error::Error;
|
||||||
use crate::types::subgraph::Qualified;
|
use crate::types::subgraph::Qualified;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use lang_graphql::ast::common::OperationType;
|
||||||
|
use ndc_models;
|
||||||
use open_dds::{
|
use open_dds::{
|
||||||
commands::{FunctionName, ProcedureName},
|
commands::{FunctionName, ProcedureName},
|
||||||
data_connector::{
|
data_connector::{
|
||||||
@ -12,15 +12,8 @@ use open_dds::{
|
|||||||
},
|
},
|
||||||
EnvironmentValue,
|
EnvironmentValue,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use lang_graphql::ast::common::OperationType;
|
use std::collections::BTreeMap;
|
||||||
use ndc_models;
|
|
||||||
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
|
||||||
use serde::{
|
|
||||||
de::Error as DeError,
|
|
||||||
ser::{Error as SerError, SerializeMap},
|
|
||||||
};
|
|
||||||
use std::{collections::BTreeMap, str::FromStr};
|
|
||||||
|
|
||||||
/// information that does not change between resolver stages
|
/// information that does not change between resolver stages
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -209,9 +202,6 @@ impl DataConnectorLink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct SerializableUrl(pub reqwest::Url);
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ResolvedReadWriteUrls {
|
pub struct ResolvedReadWriteUrls {
|
||||||
@ -241,95 +231,6 @@ impl ResolvedDataConnectorUrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerializableUrl {
|
|
||||||
pub fn new(url: &str) -> Result<Self, url::ParseError> {
|
|
||||||
let url = reqwest::Url::parse(url)?;
|
|
||||||
Ok(Self(url))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for SerializableUrl {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(self.0.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for SerializableUrl {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let url_str = String::deserialize(deserializer)?;
|
|
||||||
let url = reqwest::Url::parse(&url_str)
|
|
||||||
.map_err(|_| D::Error::custom(format!("Invalid URL: {url_str}")))?;
|
|
||||||
Ok(SerializableUrl(url))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct SerializableHeaderMap(pub HeaderMap);
|
|
||||||
|
|
||||||
pub enum HeaderError {
|
|
||||||
InvalidHeaderName { header_name: String },
|
|
||||||
InvalidHeaderValue { header_name: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerializableHeaderMap {
|
|
||||||
fn new(headers: &IndexMap<String, EnvironmentValue>) -> Result<Self, HeaderError> {
|
|
||||||
let header_map = headers
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
Ok((
|
|
||||||
HeaderName::from_str(k).map_err(|_| HeaderError::InvalidHeaderName {
|
|
||||||
header_name: k.clone(),
|
|
||||||
})?,
|
|
||||||
HeaderValue::from_str(&v.value).map_err(|_| {
|
|
||||||
HeaderError::InvalidHeaderValue {
|
|
||||||
header_name: k.clone(),
|
|
||||||
}
|
|
||||||
})?,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Result<HeaderMap, HeaderError>>()?;
|
|
||||||
Ok(Self(header_map))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for SerializableHeaderMap {
|
|
||||||
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 {
|
|
||||||
map.serialize_entry(k.as_str(), v.to_str().map_err(S::Error::custom)?)?;
|
|
||||||
}
|
|
||||||
map.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for SerializableHeaderMap {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let hash_map = BTreeMap::<String, String>::deserialize(deserializer)?;
|
|
||||||
let header_map: HeaderMap = hash_map
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
Ok((
|
|
||||||
HeaderName::from_str(&k).map_err(D::Error::custom)?,
|
|
||||||
HeaderValue::from_str(&v).map_err(D::Error::custom)?,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Result<HeaderMap, D::Error>>()?;
|
|
||||||
Ok(SerializableHeaderMap(header_map))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ndc_models;
|
use ndc_models;
|
||||||
@ -353,14 +254,6 @@ mod tests {
|
|||||||
assert_eq!(actual_url_str, serialized_url);
|
assert_eq!(actual_url_str, serialized_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_header_map_serialization_deserialization() {
|
|
||||||
let headers_str = r#"{"name":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~!#$&'()*+,/:;=?@[]\""}"#;
|
|
||||||
let headers: super::SerializableHeaderMap = serde_json::from_str(headers_str).unwrap();
|
|
||||||
let serialized_headers = serde_json::to_string(&headers).unwrap();
|
|
||||||
assert_eq!(headers_str, serialized_headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_data_connector_context_capablities() {
|
fn test_data_connector_context_capablities() {
|
||||||
let data_connector_with_capabilities: DataConnectorLinkV1 =
|
let data_connector_with_capabilities: DataConnectorLinkV1 =
|
||||||
|
Loading…
Reference in New Issue
Block a user