Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Create series" API and route #1325

Draft
wants to merge 14 commits into
base: next
Choose a base branch
from
59 changes: 0 additions & 59 deletions backend/src/api/common.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
use bincode::Options;
use juniper::{GraphQLScalar, InputValue, ScalarValue};
use serde::{Deserialize, Serialize};

use crate::{
api::{
Id,
Context,
err::{self, ApiResult},
model::{
event::AuthorizedEvent,
series::Series,
Expand All @@ -15,7 +10,6 @@ use crate::{
search::{SearchEvent, SearchRealm, SearchSeries},
},
},
prelude::*,
search::Playlist as SearchPlaylist,
};

Expand Down Expand Up @@ -46,56 +40,3 @@ pub(crate) trait Node {
pub(crate) struct NotAllowed;

super::util::impl_object_with_dummy_field!(NotAllowed);


/// Opaque cursor for pagination. Serializes as string.
///
/// Ideally, this would be generic over something `Serialize + Deserialize`.
/// However, junipers `graphql_scalar` macro doesn't work with generics
/// (sigh). So it's easier to just eagerly create the base64 string in `new`
/// and `deserialize`.
///
/// The actual cursor is a base64 encoded string. The encoded bytes are the
/// serialization format from `bincode`, a compact binary serializer. Of course
/// we could also have serialized it as JSON and base64 encoded it then, but
/// that would be a waste of network bandwidth.
#[derive(Debug, Clone, GraphQLScalar)]
#[graphql(
description = "An opaque cursor used for pagination",
parse_token(String),
)]
pub(crate) struct Cursor(String);

impl Cursor {
pub(crate) fn new(data: impl Serialize) -> Self {
let mut b64writer = base64::write::EncoderStringWriter::new(
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
);
bincode::DefaultOptions::new().serialize_into(&mut b64writer, &data)
.unwrap_or_else(|e| unreachable!("bincode serialize failed without size limit: {}", e));
Self(b64writer.into_inner())
}

pub(crate) fn deserialize<T>(&self) -> ApiResult<T>
where
for<'de> T: Deserialize<'de>,
{
let mut bytes = self.0.as_bytes();
let b64reader = base64::read::DecoderReader::new(
&mut bytes,
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
);
bincode::DefaultOptions::new()
.deserialize_from(b64reader)
.map_err(|e| err::invalid_input!("given cursor is invalid: {}", e))
}

fn to_output<S: ScalarValue>(&self) -> juniper::Value<S> {
juniper::Value::scalar(self.0.clone())
}

fn from_input<S: ScalarValue>(input: &InputValue<S>) -> Result<Self, String> {
let s = input.as_string_value().ok_or("expected string")?;
Ok(Self(s.into()))
}
}
2 changes: 1 addition & 1 deletion backend/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod subscription;
pub(crate) use self::{
id::Id,
context::Context,
common::{Cursor, Node, NodeValue},
common::{Node, NodeValue},
};


Expand Down
Loading
Loading