Skip to content
Snippets Groups Projects
Verified Commit 492c8688 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement initial graphQL functionality without loading optimizations

parents
No related branches found
No related tags found
No related merge requests found
/target
/.idea
/.env
This diff is collapsed.
[package]
name = "media-backend"
version = "0.1.0"
authors = ["Janne Koschinski <janne@kuschku.de>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = "0.4.4"
chrono = "0.4.11"
uuid = "0.7.4"
diesel = { version = "1.4.4", features = ["postgres", "chrono", "uuidv07"] }
dotenv = "0.15.0"
env_logger = "0.7.1"
futures = "0.3.5"
juniper = "0.14.2"
juniper_rocket = "0.5.2"
serde = "1.0.110"
serde_derive = "1.0.110"
serde_json = "1.0.53"
anyhow = "1.0.30"
# For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
IF (
NEW IS DISTINCT FROM OLD AND
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
) THEN
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop table title_subtitles;
drop table title_ratings;
drop table title_names;
drop table title_media;
drop table title_images;
drop table title_genres;
drop table title_episodes;
drop table title_descriptions;
drop table title_casts;
drop table titles;
drop table people;
drop table genres;
create table if not exists genres
(
id uuid not null
constraint genres_pkey
primary key,
tmdb_id integer,
"name" text not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);
create table if not exists people
(
id uuid not null
constraint people_pkey
primary key,
imdb_id text,
"name" text not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);
create table if not exists titles
(
id uuid not null
constraint titles_pkey
primary key,
imdb_id text,
tmdb_id integer,
tvdb_id integer,
original_language text,
runtime integer,
year_start integer,
year_end integer,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
parent_id uuid
);
create table if not exists title_casts
(
id uuid not null
constraint title_casts_pkey
primary key,
category text,
characters text [] not null,
job text,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_casts_title_id_fkey
references titles
on delete set null
on update cascade,
person_id uuid not null
constraint title_casts_person_id_fkey
references people
on delete set null
on update cascade
);
create table if not exists title_descriptions
(
id uuid not null
constraint title_descriptions_pkey
primary key,
region text,
languages text [] not null,
kind text not null,
overview text not null,
tagline text,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_descriptions_title_id_fkey
references titles
on delete set null
on update cascade
);
create table if not exists title_episodes
(
id uuid not null
constraint title_episodes_pkey
primary key,
season_number text,
episode_number text,
air_date date,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
parent_id uuid not null
constraint title_episodes_parent_id_fkey
references titles
on delete set null
on update cascade
);
alter table titles add constraint title_parent_id_fkey foreign key (parent_id)
references title_episodes
on delete set null
on update cascade;
create index if not exists title_episodes_show_id_season_number_episode_number
on title_episodes (parent_id, season_number, episode_number);
create index if not exists title_episodes_show_id_air_date
on title_episodes (parent_id, air_date);
create table if not exists title_genres
(
id uuid not null
constraint title_genres_pkey
primary key,
title_id uuid not null
constraint title_genres_title_id_fkey
references titles
on delete set null
on update cascade,
genre_id uuid not null
constraint title_genres_genre_id_fkey
references genres
on delete set null
on update cascade,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);
create table if not exists title_images
(
id uuid not null
constraint title_images_pkey
primary key,
kind text,
mime text,
src text,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_images_title_id_fkey
references titles
on delete set null
on update cascade
);
create table if not exists title_media
(
id uuid not null
constraint title_media_pkey
primary key,
mime text,
codecs text [] not null,
languages text [] not null,
src text,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_media_title_id_fkey
references titles
on delete set null
on update cascade
);
create table if not exists title_names
(
id uuid not null
constraint title_names_pkey
primary key,
region text,
languages text [] not null,
kind text not null,
"name" text not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_names_title_id_fkey
references titles
on delete set null
on update cascade
);
create table if not exists title_ratings
(
id uuid not null
constraint title_ratings_pkey
primary key,
region text,
certification text,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_ratings_title_id_fkey
references titles
on delete set null
on update cascade
);
create table if not exists title_subtitles
(
id uuid not null
constraint title_subtitles_pkey
primary key,
format text,
language text,
region text,
specifier text,
src text,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
title_id uuid not null
constraint title_subtitles_title_id_fkey
references titles
on delete set null
on update cascade
);
\ No newline at end of file
#[macro_use]
extern crate diesel;
extern crate dotenv;
pub mod schema;
pub mod models;
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
extern crate media_backend;
use dotenv::dotenv;
use std::env;
use rocket::{response::content, State};
use juniper::{EmptyMutation, RootNode};
use media_backend::models::{Query, Context};
type Schema = RootNode<'static, Query, EmptyMutation<Context>>;
#[get("/")]
fn graphiql() -> content::Html<String> {
juniper_rocket::graphiql_source("/graphql")
}
#[get("/graphql?<request>")]
fn get_graphql_handler(
context: State<Context>,
request: juniper_rocket::GraphQLRequest,
schema: State<Schema>,
) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context)
}
#[post("/graphql", data = "<request>")]
fn post_graphql_handler(
context: State<Context>,
request: juniper_rocket::GraphQLRequest,
schema: State<Schema>,
) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context)
}
fn main() {
dotenv().ok();
rocket::ignite()
.manage(Context {
database_url: env::var("DATABASE_URL")
.expect("DATABASE_URL must be set")
})
.manage(Schema::new(
Query,
EmptyMutation::<Context>::new()
))
.mount(
"/",
rocket::routes![graphiql, get_graphql_handler, post_graphql_handler],
)
.launch();
}
extern crate diesel;
use super::schema::*;
use diesel::*;
use juniper::FieldResult;
use chrono::{Utc, DateTime, NaiveDate};
pub struct Context {
pub database_url: String,
}
impl juniper::Context for Context {}
impl Context {
fn connection(&self) -> PgConnection {
PgConnection::establish(&self.database_url)
.expect(&format!("Error connecting to {}", &self.database_url))
}
}
pub struct Query;
#[juniper::object(Context = Context)]
impl Query {
fn api_version() -> &str {
"1.0"
}
fn titles(context: &Context, id: Option<uuid::Uuid>, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<Title>> {
let query = match id {
Some(id) => titles::table.find(id).into_boxed(),
None => titles::table.into_boxed()
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<Title>(&context.connection())?)
}
fn genres(context: &Context, id: Option<uuid::Uuid>, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<Genre>> {
let query = match id {
Some(id) => genres::table.find(id).into_boxed(),
None => genres::table.into_boxed()
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<Genre>(&context.connection())?)
}
fn people(context: &Context, id: Option<uuid::Uuid>, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<Person>> {
let query = match id {
Some(id) => people::table.find(id).into_boxed(),
None => people::table.into_boxed()
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<Person>(&context.connection())?)
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
#[table_name = "genres"]
pub struct Genre {
pub id: uuid::Uuid,
pub tmdb_id: Option<i32>,
pub name: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[juniper::object(Context = Context)]
impl Genre {
pub fn id(&self) -> uuid::Uuid {
self.id
}
pub fn tmdb_id(&self) -> Option<i32> {
self.tmdb_id
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn created_at(&self) -> DateTime<Utc> {
self.created_at
}
pub fn updated_at(&self) -> DateTime<Utc> {
self.updated_at
}
pub fn titles(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<Title>> {
let query = TitleGenre::belonging_to(self).inner_join(titles::table).select(titles::all_columns).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<Title>(&context.connection())?)
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
#[table_name = "people"]
pub struct Person {
pub id: uuid::Uuid,
pub imdb_id: Option<String>,
pub name: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[juniper::object(Context = Context)]
impl Person {
pub fn id(&self) -> uuid::Uuid {
self.id
}
pub fn imdb_id(&self) -> Option<String> {
self.imdb_id.clone()
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn created_at(&self) -> DateTime<Utc> {
self.created_at
}
pub fn updated_at(&self) -> DateTime<Utc> {
self.updated_at
}
pub fn starring(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<TitleCast>> {
let query = TitleCast::belonging_to(self).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleCast>(&context.connection())?)
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(Title, foreign_key = "title_id")]
#[belongs_to(Person, foreign_key = "person_id")]
#[table_name = "title_casts"]
pub struct TitleCast {
pub id: uuid::Uuid,
pub category: Option<String>,
pub characters: Vec<String>,
pub job: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
pub person_id: uuid::Uuid,
}
#[juniper::object(Context = Context)]
impl TitleCast {
pub fn id(&self) -> uuid::Uuid {
self.id
}
pub fn category(&self) -> Option<String> {
self.category.clone()
}
pub fn characters(&self) -> Vec<String> {
self.characters.clone()
}
pub fn job(&self) -> Option<String> {
self.job.clone()
}
pub fn title(&self, context: &Context) -> FieldResult<Title> {
Ok(titles::table.find(self.title_id).first::<Title>(&context.connection())?)
}
pub fn person(&self, context: &Context) -> FieldResult<Person> {
Ok(people::table.find(self.person_id).first::<Person>(&context.connection())?)
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[table_name = "title_descriptions"]
pub struct TitleDescription {
pub id: uuid::Uuid,
pub region: Option<String>,
pub languages: Vec<String>,
pub kind: String,
pub overview: String,
pub tagline: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(Title, foreign_key = "parent_id")]
#[table_name = "title_episodes"]
pub struct TitleEpisode {
pub id: uuid::Uuid,
pub season_number: Option<String>,
pub episode_number: Option<String>,
pub air_date: Option<chrono::NaiveDate>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub parent_id: uuid::Uuid,
}
#[juniper::object(Context = Context)]
impl TitleEpisode {
pub fn id(&self) -> uuid::Uuid {
self.id
}
pub fn season_number(&self) -> Option<String> {
self.season_number.clone()
}
pub fn episode_number(&self) -> Option<String> {
self.episode_number.clone()
}
pub fn air_date(&self) -> Option<NaiveDate> {
self.air_date
}
pub fn episode(&self, context: &Context) -> FieldResult<Title> {
Ok(Title::belonging_to(self).first::<Title>(&context.connection())?)
}
pub fn show(&self, context: &Context) -> FieldResult<Title> {
Ok(titles::table.find(self.parent_id).first::<Title>(&context.connection())?)
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[belongs_to(Genre, foreign_key = "genre_id")]
#[table_name = "title_genres"]
pub struct TitleGenre {
pub id: uuid::Uuid,
pub title_id: uuid::Uuid,
pub genre_id: uuid::Uuid,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[table_name = "title_images"]
pub struct TitleImage {
pub id: uuid::Uuid,
pub kind: Option<String>,
pub mime: Option<String>,
pub src: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[table_name = "title_media"]
pub struct TitleMedium {
pub id: uuid::Uuid,
pub mime: Option<String>,
pub codecs: Vec<String>,
pub languages: Vec<String>,
pub src: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[table_name = "title_names"]
pub struct TitleName {
pub id: uuid::Uuid,
pub region: Option<String>,
pub languages: Vec<String>,
pub kind: String,
pub name: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[table_name = "title_ratings"]
pub struct TitleRating {
pub id: uuid::Uuid,
pub region: Option<String>,
pub certification: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, juniper::GraphQLObject)]
#[belongs_to(Title, foreign_key = "title_id")]
#[table_name = "title_subtitles"]
pub struct TitleSubtitle {
pub id: uuid::Uuid,
pub format: Option<String>,
pub language: Option<String>,
pub region: Option<String>,
pub specifier: Option<String>,
pub src: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub title_id: uuid::Uuid,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(TitleEpisode, foreign_key = "parent_id")]
#[table_name = "titles"]
pub struct Title {
pub id: uuid::Uuid,
pub imdb_id: Option<String>,
pub tmdb_id: Option<i32>,
pub tvdb_id: Option<i32>,
pub original_language: Option<String>,
pub runtime: Option<i32>,
pub year_start: Option<i32>,
pub year_end: Option<i32>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub parent_id: Option<uuid::Uuid>,
}
#[juniper::object(Context = Context)]
impl Title {
pub fn id(&self) -> uuid::Uuid {
self.id
}
pub fn imdb_id(&self) -> Option<String> {
self.imdb_id.clone()
}
pub fn tmdb_id(&self) -> Option<i32> {
self.tmdb_id
}
pub fn tvdb_id(&self) -> Option<i32> {
self.tvdb_id
}
pub fn original_language(&self) -> Option<String> {
self.original_language.clone()
}
pub fn runtime(&self) -> Option<i32> {
self.runtime
}
pub fn year_start(&self) -> Option<i32> {
self.year_start
}
pub fn year_end(&self) -> Option<i32> {
self.year_end
}
pub fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
self.created_at
}
pub fn updated_at(&self) -> chrono::DateTime<chrono::Utc> {
self.updated_at
}
pub fn cast(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<TitleCast>> {
let query = TitleCast::belonging_to(self).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleCast>(&context.connection())?)
}
pub fn descriptions(&self, context: &Context,
language: Option<String>, region: Option<String>,
limit: Option<i32>, offset: Option<i32>,
) -> FieldResult<Vec<TitleDescription>> {
let query = TitleDescription::belonging_to(self).into_boxed();
let query = match (language, region) {
(Some(language), Some(region)) =>
query.filter(
title_descriptions::kind.ne("localized").or(
title_descriptions::languages.contains(vec![language])
).or(
title_descriptions::region.eq(region)
)
),
(Some(language), None) =>
query.filter(
title_descriptions::kind.ne("localized").or(
title_descriptions::languages.contains(vec![language])
)
),
(None, Some(region)) =>
query.filter(
title_descriptions::kind.ne("localized").or(
title_descriptions::region.eq(region)
)
),
(None, None) =>
query
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleDescription>(&context.connection())?)
}
pub fn genres(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<Genre>> {
let query = TitleGenre::belonging_to(self).inner_join(genres::table).select(genres::all_columns).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<Genre>(&context.connection())?)
}
pub fn images(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<TitleImage>> {
let query = TitleImage::belonging_to(self).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleImage>(&context.connection())?)
}
pub fn media(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<TitleMedium>> {
let query = TitleMedium::belonging_to(self).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleMedium>(&context.connection())?)
}
pub fn names(&self, context: &Context,
language: Option<String>, region: Option<String>,
limit: Option<i32>, offset: Option<i32>,
) -> FieldResult<Vec<TitleName>> {
let query = TitleName::belonging_to(self).into_boxed();
let query = match (language, region) {
(Some(language), Some(region)) =>
query.filter(
title_names::kind.ne("localized").or(
title_names::languages.contains(vec![language])
).or(
title_names::region.eq(region)
)
),
(Some(language), None) =>
query.filter(
title_names::kind.ne("localized").or(
title_names::languages.contains(vec![language])
)
),
(None, Some(region)) =>
query.filter(
title_names::kind.ne("localized").or(
title_names::region.eq(region)
)
),
(None, None) =>
query
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleName>(&context.connection())?)
}
pub fn ratings(&self, context: &Context,
region: Option<String>,
limit: Option<i32>, offset: Option<i32>,
) -> FieldResult<Vec<TitleRating>> {
let query = TitleRating::belonging_to(self).into_boxed();
let query = match region {
Some(region) =>
query.filter(
title_ratings::region.eq(region)
),
None => query
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleRating>(&context.connection())?)
}
pub fn subtitles(&self, context: &Context, limit: Option<i32>, offset: Option<i32>) -> FieldResult<Vec<TitleSubtitle>> {
let query = TitleSubtitle::belonging_to(self).into_boxed();
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(query.load::<TitleSubtitle>(&context.connection())?)
}
pub fn children(&self, context: &Context,
limit: Option<i32>, offset: Option<i32>,
season_number: Option<String>, episode_number: Option<String>,
) -> FieldResult<Vec<TitleEpisode>> {
let query = TitleEpisode::belonging_to(self).into_boxed();
let query = match season_number {
Some(season_number) => query.filter(title_episodes::season_number.eq(season_number)),
None => query
};
let query = match episode_number {
Some(episode_number) => query.filter(title_episodes::episode_number.eq(episode_number)),
None => query
};
let query = match limit {
Some(limit) => query.limit(limit as i64),
None => query
};
let query = match offset {
Some(offset) => query.offset(offset as i64),
None => query
};
Ok(
query.order((title_episodes::season_number.asc(), title_episodes::episode_number.asc()))
.load::<TitleEpisode>(&context.connection())?
)
}
pub fn parent(&self, context: &Context) -> FieldResult<TitleEpisode> {
if let Some(parent_id) = self.parent_id {
Ok(title_episodes::table.find(parent_id).first::<TitleEpisode>(&context.connection())?)
} else {
Err(anyhow::anyhow!("Title has no parent"))?
}
}
}
\ No newline at end of file
table! {
genres (id) {
id -> Uuid,
tmdb_id -> Nullable<Int4>,
name -> Text,
created_at -> Timestamptz,
updated_at -> Timestamptz,
}
}
table! {
people (id) {
id -> Uuid,
imdb_id -> Nullable<Text>,
name -> Text,
created_at -> Timestamptz,
updated_at -> Timestamptz,
}
}
table! {
title_casts (id) {
id -> Uuid,
category -> Nullable<Text>,
characters -> Array<Text>,
job -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
person_id -> Uuid,
}
}
table! {
title_descriptions (id) {
id -> Uuid,
region -> Nullable<Text>,
languages -> Array<Text>,
kind -> Text,
overview -> Text,
tagline -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
}
}
table! {
title_episodes (id) {
id -> Uuid,
season_number -> Nullable<Text>,
episode_number -> Nullable<Text>,
air_date -> Nullable<Date>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
parent_id -> Uuid,
}
}
table! {
title_genres (id) {
id -> Uuid,
title_id -> Uuid,
genre_id -> Uuid,
created_at -> Timestamptz,
updated_at -> Timestamptz,
}
}
table! {
title_images (id) {
id -> Uuid,
kind -> Nullable<Text>,
mime -> Nullable<Text>,
src -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
}
}
table! {
title_media (id) {
id -> Uuid,
mime -> Nullable<Text>,
codecs -> Array<Text>,
languages -> Array<Text>,
src -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
}
}
table! {
title_names (id) {
id -> Uuid,
region -> Nullable<Text>,
languages -> Array<Text>,
kind -> Text,
name -> Text,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
}
}
table! {
title_ratings (id) {
id -> Uuid,
region -> Nullable<Text>,
certification -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
}
}
table! {
title_subtitles (id) {
id -> Uuid,
format -> Nullable<Text>,
language -> Nullable<Text>,
region -> Nullable<Text>,
specifier -> Nullable<Text>,
src -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
title_id -> Uuid,
}
}
table! {
titles (id) {
id -> Uuid,
imdb_id -> Nullable<Text>,
tmdb_id -> Nullable<Int4>,
tvdb_id -> Nullable<Int4>,
original_language -> Nullable<Text>,
runtime -> Nullable<Int4>,
year_start -> Nullable<Int4>,
year_end -> Nullable<Int4>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
parent_id -> Nullable<Uuid>,
}
}
joinable!(title_casts -> people (person_id));
joinable!(title_casts -> titles (title_id));
joinable!(title_descriptions -> titles (title_id));
joinable!(title_genres -> genres (genre_id));
joinable!(title_genres -> titles (title_id));
joinable!(title_images -> titles (title_id));
joinable!(title_media -> titles (title_id));
joinable!(title_names -> titles (title_id));
joinable!(title_ratings -> titles (title_id));
joinable!(title_subtitles -> titles (title_id));
allow_tables_to_appear_in_same_query!(
genres,
people,
title_casts,
title_descriptions,
title_episodes,
title_genres,
title_images,
title_media,
title_names,
title_ratings,
title_subtitles,
titles,
);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment