Repository: p2panda/aquadoggo (language: rust)

Issue:
GraphQL Docs look funny with `Scalar` suffixes
![Screenshot 2022-08-11 at 12-01-58 Playground - http __localhost 2020_graphql](https://user-images.githubusercontent.com/1822473/184113530-e0034112-c0a0-4286-abe7-eebe371e7c19.png)

Is there a way to tell `async-graphql` that we want a different name for the scalar? It looks like it currently derives it from the struct names.


Discussion / hints:
I think I just found an undocument macro argument for that! 

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85e8696e0..2506bb3d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Give error types of worker a string for better debugging [#194](https://github.com/p2panda/aquadoggo/pull/194)
 - Bump `p2panda-rs` which now supports log id's starting from `0` [#207](https://github.com/p2panda/aquadoggo/pull/207)
 - Removed unused field `entry_hash` from operation data model [#221](https://github.com/p2panda/aquadoggo/pull/221)
+- Remove `Scalar` suffix from scalar types in GraphQL schema [#231](https://github.com/p2panda/aquadoggo/pull/231)
 - Implement new API for untagged operations [#245](https://github.com/p2panda/aquadoggo/pull/235)
 - Use `DocumentStore` trait from `p2panda_rs`[#249](https://github.com/p2panda/aquadoggo/pull/249)
 
diff --git a/aquadoggo/src/db/models/entry.rs b/aquadoggo/src/db/models/entry.rs
index 923f66462..4c70b4f0d 100644
--- a/aquadoggo/src/db/models/entry.rs
+++ b/aquadoggo/src/db/models/entry.rs
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
-use serde::Serialize;
 use sqlx::FromRow;
 
 /// Representation of a row from the entries table as stored in the database. This is required
@@ -8,8 +7,7 @@ use sqlx::FromRow;
 ///
 /// We store the u64 integer values of `log_id` and `seq_num` as strings since SQLite doesn't
 /// support storing unsigned 64 bit integers.
-#[derive(FromRow, Debug, Serialize, Clone, PartialEq, Eq)]
-#[serde(rename_all = "camelCase")]
+#[derive(FromRow, Debug, Clone, PartialEq, Eq)]
 pub struct EntryRow {
     /// Public key of the author.
     pub author: String,
diff --git a/aquadoggo/src/db/models/task.rs b/aquadoggo/src/db/models/task.rs
index 67a1e80e4..e21b2a56d 100644
--- a/aquadoggo/src/db/models/task.rs
+++ b/aquadoggo/src/db/models/task.rs
@@ -1,13 +1,11 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
-use serde::Serialize;
 use sqlx::FromRow;
 
 /// Representation of a row from the `tasks` table as stored in the database.
 ///
 /// This table holds all "pending" tasks of the materialization service worker.
-#[derive(FromRow, Debug, Serialize, Clone, PartialEq, Eq)]
-#[serde(rename_all = "camelCase")]
+#[derive(FromRow, Debug, Clone, PartialEq, Eq)]
 pub struct TaskRow {
     /// Name of the task worker.
     pub name: String,
diff --git a/aquadoggo/src/graphql/client/dynamic_query.rs b/aquadoggo/src/graphql/client/dynamic_query.rs
index ae26c68cd..b971fef71 100644
--- a/aquadoggo/src/graphql/client/dynamic_query.rs
+++ b/aquadoggo/src/graphql/client/dynamic_query.rs
@@ -459,12 +459,12 @@ mod test {
     #[case::malformed_document_id(
         "id: \"verboten\"",
         Value::Null,
-        vec!["Failed to parse \"DocumentIdScalar\": invalid hex encoding in hash string".to_string()]
+        vec!["Failed to parse \"DocumentId\": invalid hex encoding in hash string".to_string()]
     )]
     #[case::malformed_view_id(
         "viewId: \"verboten\"",
         Value::Null,
-        vec!["Failed to parse \"DocumentViewIdScalar\": invalid hex encoding in hash string".to_string()]
+        vec!["Failed to parse \"DocumentViewId\": invalid hex encoding in hash string".to_string()]
     )]
     #[case::missing_parameters(
         "id: null",
diff --git a/aquadoggo/src/graphql/client/mutation.rs b/aquadoggo/src/graphql/client/mutation.rs
index 7a76764bb..7e3e447a5 100644
--- a/aquadoggo/src/graphql/client/mutation.rs
+++ b/aquadoggo/src/graphql/client/mutation.rs
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
-//! Mutation root.
 use anyhow::anyhow;
 use async_graphql::{Context, Object, Result};
 use p2panda_rs::entry::traits::AsEncodedEntry;
@@ -29,7 +28,7 @@ impl ClientMutationRoot {
         &self,
         ctx: &Context<'_>,
         #[graphql(name = "entry", desc = "Signed and encoded entry to publish")]
-        entry: scalars::EntrySignedScalar,
+        entry: scalars::EncodedEntryScalar,
         #[graphql(
             name = "operation",
             desc = "p2panda operation representing the entry payload."
@@ -292,11 +291,6 @@ mod tests {
     }
 
     #[rstest]
-    #[case::no_entry(
-        "",
-        &OPERATION_ENCODED,
-        "Bytes to decode had length of 0"
-    )]
     #[case::invalid_entry_bytes(
         "AB01",
         &OPERATION_ENCODED,
@@ -305,7 +299,12 @@ mod tests {
     #[case::invalid_entry_hex_encoding(
         "-/74='4,.=4-=235m-0   34.6-3",
         &OPERATION_ENCODED,
-        "Failed to parse \"EntrySignedScalar\": Invalid character '-' at position 0"
+        "Failed to parse \"EncodedEntry\": invalid hex encoding in entry"
+    )]
+    #[case::no_entry(
+        "",
+        &OPERATION_ENCODED,
+        "Bytes to decode had length of 0"
     )]
     #[case::no_operation(
         &EncodedEntry::from_bytes(&ENTRY_ENCODED).to_string(),
@@ -338,12 +337,12 @@ mod tests {
     #[case::valid_entry_with_extra_hex_char_at_end(
         &{EncodedEntry::from_bytes(&ENTRY_ENCODED).to_string() + "A"},
         &OPERATION_ENCODED,
-        "Failed to parse \"EntrySignedScalar\": Odd number of digits"
+        "Failed to parse \"EncodedEntry\": invalid hex encoding in entry"
     )]
     #[case::valid_entry_with_extra_hex_char_at_start(
         &{"A".to_string() + &EncodedEntry::from_bytes(&ENTRY_ENCODED).to_string()},
         &OPERATION_ENCODED,
-        "Failed to parse \"EntrySignedScalar\": Odd number of digits"
+        "Failed to parse \"EncodedEntry\": invalid hex encoding in entry"
     )]
     #[case::should_not_have_skiplink(
         &entry_signed_encoded_unvalidated(
diff --git a/aquadoggo/src/graphql/client/static_query.rs b/aquadoggo/src/graphql/client/static_query.rs
index 56d2cb4f8..f0964cac9 100644
--- a/aquadoggo/src/graphql/client/static_query.rs
+++ b/aquadoggo/src/graphql/client/static_query.rs
@@ -174,7 +174,7 @@ mod tests {
             let response: Response = response.json().await;
             assert_eq!(
                 response.errors[0].message,
-                "Failed to parse \"PublicKeyScalar\": invalid hex encoding in author string"
+                "Failed to parse \"PublicKey\": invalid hex encoding in author string"
             )
         })
     }
diff --git a/aquadoggo/src/graphql/client/static_types/next_entry_arguments.rs b/aquadoggo/src/graphql/client/static_types/next_entry_arguments.rs
index e2f7d79c1..d77b39b3d 100644
--- a/aquadoggo/src/graphql/client/static_types/next_entry_arguments.rs
+++ b/aquadoggo/src/graphql/client/static_types/next_entry_arguments.rs
@@ -1,12 +1,11 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
 use async_graphql::SimpleObject;
-use serde::{Deserialize, Serialize};
 
 use crate::graphql::scalars;
 
 /// Arguments required to sign and encode the next entry for an author.
-#[derive(SimpleObject, Serialize, Deserialize, Debug, Eq, PartialEq)]
+#[derive(SimpleObject, Debug, Eq, PartialEq)]
 pub struct NextEntryArguments {
     /// Log id of the entry.
     #[graphql(name = "logId")]
@@ -17,8 +16,8 @@ pub struct NextEntryArguments {
     pub seq_num: scalars::SeqNumScalar,
 
     /// Hash of the entry backlink.
-    pub backlink: Option<scalars::EntryHash>,
+    pub backlink: Option<scalars::EntryHashScalar>,
 
     /// Hash of the entry skiplink.
-    pub skiplink: Option<scalars::EntryHash>,
+    pub skiplink: Option<scalars::EntryHashScalar>,
 }
diff --git a/aquadoggo/src/graphql/replication/client.rs b/aquadoggo/src/graphql/replication/client.rs
index bdf786924..84d59d6a6 100644
--- a/aquadoggo/src/graphql/replication/client.rs
+++ b/aquadoggo/src/graphql/replication/client.rs
@@ -14,7 +14,7 @@ use crate::graphql::replication::response::EncodedEntryAndOperation;
 use crate::graphql::scalars;
 
 /// Response type of `entries_newer_than_seq_num` query.
-#[derive(Serialize, Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 struct Response {
     entries_newer_than_seq_num: Paginated<EncodedEntryAndOperation, scalars::SeqNumScalar>,
diff --git a/aquadoggo/src/graphql/replication/query.rs b/aquadoggo/src/graphql/replication/query.rs
index 32d49a070..9aed71dc0 100644
--- a/aquadoggo/src/graphql/replication/query.rs
+++ b/aquadoggo/src/graphql/replication/query.rs
@@ -31,7 +31,7 @@ impl ReplicationRoot {
     async fn entry_by_hash<'a>(
         &self,
         ctx: &Context<'a>,
-        hash: scalars::EntryHash,
+        hash: scalars::EntryHashScalar,
     ) -> Result<EncodedEntryAndOperation> {
         let store = ctx.data::<SqlStorage>()?;
         let result = store.get_entry_by_hash(&hash.clone().into()).await?;
diff --git a/aquadoggo/src/graphql/replication/response.rs b/aquadoggo/src/graphql/replication/response.rs
index 119bb47ba..f70939cd6 100644
--- a/aquadoggo/src/graphql/replication/response.rs
+++ b/aquadoggo/src/graphql/replication/response.rs
@@ -18,8 +18,8 @@ use crate::graphql::scalars;
 #[derive(SimpleObject, Serialize, Deserialize, Debug, Eq, PartialEq)]
 #[graphql(complex)]
 pub struct EncodedEntryAndOperation {
-    /// Signed and encoded bamboo entry.
-    pub entry: scalars::EntrySignedScalar,
+    /// Signed and encoded bamboo entry, encoded as hexadecimal string.
+    pub entry: scalars::EncodedEntryScalar,
 
     /// p2panda operation, CBOR bytes encoded as hexadecimal string.
     pub operation: Option<scalars::EncodedOperationScalar>,
@@ -31,7 +31,7 @@ impl EncodedEntryAndOperation {
     async fn certificate_pool<'a>(
         &self,
         ctx: &Context<'a>,
-    ) -> async_graphql::Result<Vec<scalars::EntrySignedScalar>> {
+    ) -> async_graphql::Result<Vec<scalars::EncodedEntryScalar>> {
         let store = ctx.data::<SqlStorage>()?;
 
         // Decode entry
diff --git a/aquadoggo/src/graphql/scalars/document_id_scalar.rs b/aquadoggo/src/graphql/scalars/document_id_scalar.rs
index 3e34f5824..d20f55850 100644
--- a/aquadoggo/src/graphql/scalars/document_id_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/document_id_scalar.rs
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
 use std::fmt::Display;
+use std::str::FromStr;
 
 use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
 use p2panda_rs::document::DocumentId;
@@ -9,12 +10,12 @@ use p2panda_rs::document::DocumentId;
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct DocumentIdScalar(DocumentId);
 
-#[Scalar]
+#[Scalar(name = "DocumentId")]
 impl ScalarType for DocumentIdScalar {
     fn parse(value: Value) -> InputValueResult<Self> {
         match &value {
             Value::String(str_value) => {
-                let document_id = str_value.as_str().parse::<DocumentId>()?;
+                let document_id = DocumentId::from_str(str_value)?;
                 Ok(DocumentIdScalar(document_id))
             }
             _ => Err(InputValueError::expected_type(value)),
diff --git a/aquadoggo/src/graphql/scalars/document_view_id_scalar.rs b/aquadoggo/src/graphql/scalars/document_view_id_scalar.rs
index 7ab3d3cac..d1239d087 100644
--- a/aquadoggo/src/graphql/scalars/document_view_id_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/document_view_id_scalar.rs
@@ -1,21 +1,20 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
-use std::fmt::Display;
+use std::{fmt::Display, str::FromStr};
 
 use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
 use p2panda_rs::document::DocumentViewId;
-use serde::{Deserialize, Serialize};
 
 /// Document view id as a GraphQL scalar.
-#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct DocumentViewIdScalar(DocumentViewId);
 
-#[Scalar]
+#[Scalar(name = "DocumentViewId")]
 impl ScalarType for DocumentViewIdScalar {
     fn parse(value: Value) -> InputValueResult<Self> {
         match &value {
             Value::String(str_value) => {
-                let view_id = str_value.parse::<DocumentViewId>()?;
+                let view_id = DocumentViewId::from_str(str_value)?;
                 Ok(DocumentViewIdScalar(view_id))
             }
             _ => Err(InputValueError::expected_type(value)),
diff --git a/aquadoggo/src/graphql/scalars/encoded_entry_scalar.rs b/aquadoggo/src/graphql/scalars/encoded_entry_scalar.rs
new file mode 100644
index 000000000..e89d84f55
--- /dev/null
+++ b/aquadoggo/src/graphql/scalars/encoded_entry_scalar.rs
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use anyhow::anyhow;
+use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
+use p2panda_rs::entry::EncodedEntry;
+use serde::{Deserialize, Serialize};
+
+/// Signed bamboo entry, encoded as a hexadecimal string.
+#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
+pub struct EncodedEntryScalar(EncodedEntry);
+
+#[Scalar(name = "EncodedEntry")]
+impl ScalarType for EncodedEntryScalar {
+    fn parse(value: Value) -> InputValueResult<Self> {
+        match &value {
+            Value::String(str_value) => {
+                let bytes =
+                    hex::decode(str_value).map_err(|_| anyhow!("invalid hex encoding in entry"))?;
+                Ok(EncodedEntryScalar(EncodedEntry::from_bytes(&bytes)))
+            }
+            _ => Err(InputValueError::expected_type(value)),
+        }
+    }
+
+    fn to_value(&self) -> Value {
+        Value::String(self.0.to_string())
+    }
+}
+
+impl From<EncodedEntry> for EncodedEntryScalar {
+    fn from(entry: EncodedEntry) -> Self {
+        Self(entry)
+    }
+}
+
+impl From<EncodedEntryScalar> for EncodedEntry {
+    fn from(entry: EncodedEntryScalar) -> EncodedEntry {
+        entry.0
+    }
+}
diff --git a/aquadoggo/src/graphql/scalars/encoded_operation_scalar.rs b/aquadoggo/src/graphql/scalars/encoded_operation_scalar.rs
index ef270c2b1..60bebe524 100644
--- a/aquadoggo/src/graphql/scalars/encoded_operation_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/encoded_operation_scalar.rs
@@ -1,19 +1,21 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
+use anyhow::anyhow;
 use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
 use p2panda_rs::operation::EncodedOperation;
 use serde::{Deserialize, Serialize};
 
 /// Entry payload and p2panda operation, CBOR bytes encoded as a hexadecimal string.
-#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
 pub struct EncodedOperationScalar(EncodedOperation);
 
-#[Scalar]
+#[Scalar(name = "EncodedOperation")]
 impl ScalarType for EncodedOperationScalar {
     fn parse(value: Value) -> InputValueResult<Self> {
         match &value {
             Value::String(str_value) => {
-                let bytes = hex::decode(str_value)?;
+                let bytes = hex::decode(str_value)
+                    .map_err(|_| anyhow!("invalid hex encoding in operation"))?;
                 Ok(EncodedOperationScalar(EncodedOperation::from_bytes(&bytes)))
             }
             _ => Err(InputValueError::expected_type(value)),
diff --git a/aquadoggo/src/graphql/scalars/entry_hash_scalar.rs b/aquadoggo/src/graphql/scalars/entry_hash_scalar.rs
index 171816690..4fda4f9e2 100644
--- a/aquadoggo/src/graphql/scalars/entry_hash_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/entry_hash_scalar.rs
@@ -1,49 +1,65 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
 use std::fmt::Display;
+use std::str::FromStr;
 
-use async_graphql::{scalar, Value};
+use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
 use p2panda_rs::hash::Hash;
-use serde::{Deserialize, Serialize};
+use serde::Serialize;
 
 /// Hash of a signed bamboo entry.
-#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
-pub struct EntryHash(Hash);
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct EntryHashScalar(Hash);
 
-impl From<EntryHash> for Hash {
-    fn from(hash: EntryHash) -> Self {
+#[Scalar(name = "EntryHash")]
+impl ScalarType for EntryHashScalar {
+    fn parse(value: Value) -> InputValueResult<Self> {
+        match &value {
+            Value::String(str_value) => {
+                let hash = Hash::from_str(str_value)?;
+                Ok(EntryHashScalar(hash))
+            }
+            _ => Err(InputValueError::expected_type(value)),
+        }
+    }
+
+    fn to_value(&self) -> Value {
+        Value::String(self.0.to_string())
+    }
+}
+
+impl From<EntryHashScalar> for Hash {
+    fn from(hash: EntryHashScalar) -> Self {
         hash.0
     }
 }
 
-impl From<Hash> for EntryHash {
+impl From<Hash> for EntryHashScalar {
     fn from(hash: Hash) -> Self {
         Self(hash)
     }
 }
 
-impl From<EntryHash> for Value {
-    fn from(entry: EntryHash) -> Self {
+impl From<EntryHashScalar> for Value {
+    fn from(entry: EntryHashScalar) -> Self {
         async_graphql::ScalarType::to_value(&entry)
     }
 }
 
-impl Display for EntryHash {
+impl Display for EntryHashScalar {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{}", self.0)
     }
 }
 
-scalar!(EntryHash);
-
 #[cfg(test)]
 mod tests {
     use p2panda_rs::document::DocumentViewId;
 
-    use super::EntryHash;
+    use super::EntryHashScalar;
 
-    impl From<EntryHash> for DocumentViewId {
-        fn from(hash: EntryHash) -> Self {
+    impl From<EntryHashScalar> for DocumentViewId {
+        fn from(hash: EntryHashScalar) -> Self {
             hash.0.into()
         }
     }
diff --git a/aquadoggo/src/graphql/scalars/entry_signed_scalar.rs b/aquadoggo/src/graphql/scalars/entry_signed_scalar.rs
deleted file mode 100644
index 90ae59c64..000000000
--- a/aquadoggo/src/graphql/scalars/entry_signed_scalar.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-use async_graphql::{InputValueError, Scalar, ScalarType, Value};
-use p2panda_rs::entry::EncodedEntry;
-use serde::{Deserialize, Serialize};
-
-/// Signed bamboo entry, encoded as a hexadecimal string.
-#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
-pub struct EntrySignedScalar(EncodedEntry);
-
-#[Scalar]
-impl ScalarType for EntrySignedScalar {
-    fn parse(value: Value) -> async_graphql::InputValueResult<Self> {
-        match &value {
-            Value::String(str_value) => {
-                let bytes = hex::decode(str_value)?;
-                Ok(EntrySignedScalar(EncodedEntry::from_bytes(&bytes)))
-            }
-            _ => Err(InputValueError::expected_type(value)),
-        }
-    }
-
-    fn to_value(&self) -> Value {
-        Value::String(self.0.to_string())
-    }
-}
-
-impl From<EncodedEntry> for EntrySignedScalar {
-    fn from(entry: EncodedEntry) -> Self {
-        Self(entry)
-    }
-}
-
-impl From<EntrySignedScalar> for EncodedEntry {
-    fn from(entry: EntrySignedScalar) -> EncodedEntry {
-        entry.0
-    }
-}
-
-impl From<EntrySignedScalar> for Value {
-    fn from(entry: EntrySignedScalar) -> Self {
-        async_graphql::ScalarType::to_value(&entry)
-    }
-}
diff --git a/aquadoggo/src/graphql/scalars/log_id_scalar.rs b/aquadoggo/src/graphql/scalars/log_id_scalar.rs
index e6ccd86f5..2f0f4cd9b 100644
--- a/aquadoggo/src/graphql/scalars/log_id_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/log_id_scalar.rs
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
 use std::fmt::Display;
+use std::str::FromStr;
 
-use async_graphql::scalar;
+use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
 use p2panda_rs::entry::LogId;
 use serde::{Deserialize, Serialize};
 
@@ -10,6 +11,23 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Copy, Eq, PartialEq, Debug)]
 pub struct LogIdScalar(LogId);
 
+#[Scalar(name = "LogId")]
+impl ScalarType for LogIdScalar {
+    fn parse(value: Value) -> InputValueResult<Self> {
+        match &value {
+            Value::String(str_value) => {
+                let log_id = LogId::from_str(str_value)?;
+                Ok(LogIdScalar(log_id))
+            }
+            _ => Err(InputValueError::expected_type(value)),
+        }
+    }
+
+    fn to_value(&self) -> Value {
+        Value::String(self.0.as_u64().to_string())
+    }
+}
+
 impl From<LogId> for LogIdScalar {
     fn from(log_id: LogId) -> Self {
         Self(log_id)
@@ -54,10 +72,9 @@ impl Display for LogIdScalar {
     }
 }
 
-scalar!(LogIdScalar);
-
 #[cfg(test)]
 mod tests {
+    use async_graphql::ScalarType;
     use p2panda_rs::entry::LogId;
     use serde::{Deserialize, Serialize};
 
@@ -78,4 +95,20 @@ mod tests {
         assert_eq!(serialised, "{\"log_id\":\"0\"}".to_string());
         assert_eq!(val, serde_json::from_str(&serialised).unwrap());
     }
+
+    #[test]
+    fn scalar_type() {
+        // Convert to gql value
+        let value = LogId::default();
+        let scalar_value: LogIdScalar = value.into();
+        let gql_value: async_graphql::Value = scalar_value.to_value();
+
+        // Convert back
+        let converted_value = LogIdScalar::parse(gql_value).unwrap().into();
+        assert_eq!(value, converted_value);
+
+        // Convert invalid type
+        let invalid_conversion = LogIdScalar::parse(async_graphql::Value::Boolean(true));
+        assert!(invalid_conversion.is_err())
+    }
 }
diff --git a/aquadoggo/src/graphql/scalars/mod.rs b/aquadoggo/src/graphql/scalars/mod.rs
index abc9a9148..c99fefab5 100644
--- a/aquadoggo/src/graphql/scalars/mod.rs
+++ b/aquadoggo/src/graphql/scalars/mod.rs
@@ -9,18 +9,18 @@
 //! item of the exact same name is being wrapped.
 mod document_id_scalar;
 mod document_view_id_scalar;
+mod encoded_entry_scalar;
 mod encoded_operation_scalar;
 mod entry_hash_scalar;
-mod entry_signed_scalar;
 mod log_id_scalar;
 mod public_key_scalar;
 mod seq_num_scalar;
 
 pub use document_id_scalar::DocumentIdScalar;
 pub use document_view_id_scalar::DocumentViewIdScalar;
+pub use encoded_entry_scalar::EncodedEntryScalar;
 pub use encoded_operation_scalar::EncodedOperationScalar;
-pub use entry_hash_scalar::EntryHash;
-pub use entry_signed_scalar::EntrySignedScalar;
+pub use entry_hash_scalar::EntryHashScalar;
 pub use log_id_scalar::LogIdScalar;
 pub use public_key_scalar::PublicKeyScalar;
 pub use seq_num_scalar::SeqNumScalar;
diff --git a/aquadoggo/src/graphql/scalars/public_key_scalar.rs b/aquadoggo/src/graphql/scalars/public_key_scalar.rs
index ac9624334..f8ef0805d 100644
--- a/aquadoggo/src/graphql/scalars/public_key_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/public_key_scalar.rs
@@ -1,21 +1,21 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
 use std::fmt::Display;
+use std::str::FromStr;
 
 use async_graphql::{InputValueError, Scalar, ScalarType, Value};
 use p2panda_rs::identity::Author;
-use serde::{Deserialize, Serialize};
 
 /// Public key that signed the entry.
-#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct PublicKeyScalar(Author);
 
-#[Scalar]
+#[Scalar(name = "PublicKey")]
 impl ScalarType for PublicKeyScalar {
     fn parse(value: Value) -> async_graphql::InputValueResult<Self> {
         match &value {
             Value::String(str_value) => {
-                let panda_value: Author = str_value.parse()?;
+                let panda_value = Author::from_str(str_value)?;
                 Ok(PublicKeyScalar(panda_value))
             }
             _ => Err(InputValueError::expected_type(value)),
diff --git a/aquadoggo/src/graphql/scalars/seq_num_scalar.rs b/aquadoggo/src/graphql/scalars/seq_num_scalar.rs
index 546335896..8084a1d00 100644
--- a/aquadoggo/src/graphql/scalars/seq_num_scalar.rs
+++ b/aquadoggo/src/graphql/scalars/seq_num_scalar.rs
@@ -5,7 +5,7 @@ use std::fmt::Display;
 use std::str::FromStr;
 
 use anyhow::Result;
-use async_graphql::scalar;
+use async_graphql::{InputValueError, Scalar, ScalarType, Value};
 use p2panda_rs::entry::error::SeqNumError;
 use p2panda_rs::entry::SeqNum;
 use serde::{Deserialize, Serialize};
@@ -19,15 +19,25 @@ impl SeqNumScalar {
     pub fn as_u64(&self) -> u64 {
         self.0.as_u64()
     }
+}
+
+#[Scalar(name = "SeqNum")]
+impl ScalarType for SeqNumScalar {
+    fn parse(value: Value) -> Result<Self, InputValueError<Self>> {
+        match &value {
+            Value::String(str_value) => {
+                let seq_num = SeqNum::from_str(str_value)?;
+                Ok(SeqNumScalar(seq_num))
+            }
+            _ => Err(InputValueError::expected_type(value)),
+        }
+    }
 
-    /// Convert sequence number to string.
-    #[allow(clippy::inherent_to_string_shadow_display)]
-    pub fn to_string(self) -> String {
-        self.as_u64().to_string()
+    fn to_value(&self) -> Value {
+        Value::String(self.0.as_u64().to_string())
     }
 }
 
-/// Convert from p2panda types to GraphQL scalars and back.
 impl From<SeqNum> for SeqNumScalar {
     fn from(seq_num: SeqNum) -> Self {
         Self(seq_num)
@@ -40,7 +50,6 @@ impl From<SeqNumScalar> for SeqNum {
     }
 }
 
-/// Convert from strings to sequence number.
 impl FromStr for SeqNumScalar {
     type Err = SeqNumError;
 
@@ -90,10 +99,9 @@ impl Display for SeqNumScalar {
     }
 }
 
-scalar!(SeqNumScalar);
-
 #[cfg(test)]
 mod tests {
+    use async_graphql::InputType;
     use p2panda_rs::entry::SeqNum;
     use serde::{Deserialize, Serialize};
 
@@ -114,4 +122,20 @@ mod tests {
         assert_eq!(serialised, "{\"seq_num\":\"1\"}".to_string());
         assert_eq!(val, serde_json::from_str(&serialised).unwrap());
     }
+
+    #[test]
+    fn scalar_type() {
+        // Convert to gql value
+        let value = SeqNum::default();
+        let scalar_value: SeqNumScalar = value.into();
+        let gql_value: async_graphql::Value = scalar_value.to_value();
+
+        // Convert back
+        let converted_value = SeqNumScalar::parse(Some(gql_value)).unwrap().into();
+        assert_eq!(value, converted_value);
+
+        // Convert invalid type
+        let invalid_conversion = SeqNumScalar::parse(Some(async_graphql::Value::Boolean(true)));
+        assert!(invalid_conversion.is_err())
+    }
 }


Test patch:
diff --git a/aquadoggo/src/graphql/client/dynamic_types/tests.rs b/aquadoggo/src/graphql/client/dynamic_types/tests.rs
index 32bd63c42..bd74443ca 100644
--- a/aquadoggo/src/graphql/client/dynamic_types/tests.rs
+++ b/aquadoggo/src/graphql/client/dynamic_types/tests.rs
@@ -169,7 +169,7 @@ fn application_schema_fields_type(#[from(test_db)] runner: TestDatabaseRunner) {
             .json(&json!({
                 "query": format!(
                     r#"{{
-                        schemaFields: __type(name: "{}") {{
+                        schemaFields: __type(name: "{}Fields") {{
                             description,
                             fields {{
                                 name,
@@ -180,7 +180,7 @@ fn application_schema_fields_type(#[from(test_db)] runner: TestDatabaseRunner) {
                             }}
                         }}
                     }}"#,
-                    format!("{}Fields", type_name),
+                    type_name,
                 ),
             }))
             .send()
@@ -252,13 +252,13 @@ fn metadata_type(#[from(test_db)] runner: TestDatabaseRunner) {
                 "fields": [{
                     "name": "documentId",
                     "type": {
-                        "name": "DocumentIdScalar"
+                        "name": "DocumentId"
                     }
                 },
                 {
                     "name": "viewId",
                     "type": {
-                        "name": "DocumentViewIdScalar"
+                        "name": "DocumentViewId"
                     }
                 }]
             }