Update
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Dominic Grimm 2023-01-31 21:24:18 +01:00
parent 1c164d6ed2
commit 94acd6205d
No known key found for this signature in database
GPG Key ID: 6F294212DEAAC530
18 changed files with 146 additions and 142 deletions

View File

@ -84,6 +84,10 @@ shards:
git: https://github.com/graphql-crystal/graphql.git
version: 0.4.0+git.commit.e3281bb0ef0ca301ccea176e6839422ac766465b
graphql-dataloader:
git: https://github.com/graphql-crystal/dataloader.git
version: 0.1.0+git.commit.6d1dcbcb88922ae29c3c919db4fa44de28bd489d
habitat:
git: https://github.com/luckyframework/habitat.git
version: 0.4.7

View File

@ -73,3 +73,6 @@ dependencies:
github: epoch/tallboy
wannabe_bool:
github: llamicron/wannabe_bool
graphql-dataloader:
github: graphql-crystal/dataloader
branch: main

View File

@ -34,13 +34,14 @@ module Backend
getter iat : Int64
getter exp : Int64
getter jti : UUID
getter user_id : Int32
# getter user_id : Int32
def initialize(
@iat : Int64,
@exp : Int64,
@jti : UUID,
@user_id : Int32
@jti : UUID
# @user_id : Int32
)
end
@ -52,8 +53,7 @@ module Backend
self.new(
iat: token["iat"].as_i64,
exp: token["exp"].as_i64,
jti: UUID.new(token["jti"].as_s),
user_id: token["user_id"].as_i
jti: UUID.new(token["jti"].as_s) # user_id: token["user_id"].as_i
)
end

View File

@ -42,6 +42,9 @@ module Backend
# JTI of request token
getter jti
# Dataloaders
getter loaders = Loaders::DataLoaders.new
def initialize(
@status : Status,
@user : Db::User?,
@ -63,13 +66,17 @@ module Backend
rescue
@status = Status::JWTError
else
if @user = Db::User.find(payload.user_id)
@jti = payload.jti
if Db::Token.query.where { (id == payload.jti) & active }.first.nil?
@status = Status::SessionExpired
else
@admin = user.not_nil!.admin
@role = user.not_nil!.role.to_api
token = Db::Token.query.where { (id == payload.jti) & active }
.with_user(&.with_teacher.with_student)
.first
if token.nil?
@status = Status::SessionExpired
else
@user = token.user
if @user
@jti = token.id
@admin = @user.not_nil!.admin
@role = @user.not_nil!.role.to_api
@external =
case @role.not_nil!
when .teacher?
@ -77,6 +84,8 @@ module Backend
when .student?
@user.not_nil!.student
end
else
@status = Status::SessionExpired
end
end
end

View File

@ -0,0 +1,21 @@
require "graphql-dataloader"
require "../db.cr"
require "./loaders/*"
module Backend::Api::Loaders
class DataLoaders
getter user = User.new
getter user_student = UserStudent.new
getter user_teacher = UserTeacher.new
getter student = Student.new
getter teacher = Teacher.new
getter vote = Vote.new
getter teacher_vote = TeacherVote.new
end
end

View File

@ -0,0 +1,8 @@
module Backend::Api::Loaders
class Student < GraphQL::DataLoader::Loader(Int32, Int32, Backend::Db::Student?)
def fetch(batch ids : Array(Int32)) : Array(Db::Student?)
students = Db::Student.query.where { id.in?(ids) }.to_a
ids.map { |id| students.find { |u| u.id == id } }
end
end
end

View File

@ -0,0 +1,8 @@
module Backend::Api::Loaders
class Teacher < GraphQL::DataLoader::Loader(Int32, Int32, Backend::Db::Teacher?)
def fetch(batch ids : Array(Int32)) : Array(Db::Teacher?)
teachers = Db::Teacher.query.where { id.in?(ids) }.to_a
ids.map { |id| teachers.find { |t| t.id == id } }
end
end
end

View File

@ -0,0 +1,8 @@
module Backend::Api::Loaders
class TeacherVote < GraphQL::DataLoader::Loader(Int32, Int32, Backend::Db::TeacherVote?)
def fetch(batch ids : Array(Int32)) : Array(Db::TeacherVote?)
teacher_votes = Db::TeacherVote.query.where { id.in?(ids) }.to_a
ids.map { |id| teacher_votes.find { |tv| tv.id == id } }
end
end
end

View File

@ -0,0 +1,32 @@
module Backend::Api::Loaders
class User < GraphQL::DataLoader::Loader(Int32, Int32, Backend::Db::User?)
def fetch(batch ids : Array(Int32)) : Array(Db::User?)
users = Db::User.query.where { id.in?(ids) }.to_a
ids.map { |id| users.find { |u| u.id == id } }
end
end
class UserStudent < GraphQL::DataLoader::Loader(Backend::Db::User, Int32, Backend::Db::Student?)
def key_for(user : Db::User) : Int32
user.id
end
def fetch(batch users : Array(Db::User)) : Array(Db::Student?)
user_ids = users.map(&.id)
students = Db::Student.query.where { user_id.in?(user_ids) }.to_a
users.map { |u| students.find { |s| s.user_id == u.id } }
end
end
class UserTeacher < GraphQL::DataLoader::Loader(Backend::Db::User, Int32, Backend::Db::Teacher?)
def key_for(user : Db::User) : Int32
user.id
end
def fetch(batch users : Array(Db::User)) : Array(Db::Teacher?)
user_ids = users.map(&.id)
teachers = Db::Teacher.query.where { user_id.in?(user_ids) }.to_a
users.map { |u| teachers.find { |t| t.user_id == u.id } }
end
end
end

View File

@ -0,0 +1,8 @@
module Backend::Api::Loaders
class Vote < GraphQL::DataLoader::Loader(Int32, Int32, Backend::Db::Vote?)
def fetch(batch ids : Array(Int32)) : Array(Db::Vote?)
votes = Db::Vote.query.where { id.in?(ids) }.to_a
ids.map { |id| votes.find { |v| v.id == id } }
end
end
end

View File

@ -44,8 +44,7 @@ module Backend
token: Auth::Token.new(
iat: token.iat.to_unix,
exp: token.exp.to_unix,
jti: token.id.not_nil!,
user_id: user.id.not_nil!
jti: token.id.not_nil! # user_id: user.id.not_nil!
).encode
)
end

View File

@ -54,7 +54,7 @@ module Backend
def user(context : Context, id : Int32) : User?
context.admin!
User.from_id(id)
(user = context.loaders.user.load(id)) && User.new(user)
end
@[GraphQL::Field]
@ -62,14 +62,14 @@ module Backend
def users(context : Context) : Array(User)?
context.admin!
Db::User.query.map { |user| User.new(user) }
context.loaders.user.load(Db::User.query.select(:id).map(&.id)).map { |u| User.new(u.not_nil!) }
end
@[GraphQL::Field]
def user_by_username(context : Context, username : String) : User?
context.admin!
User.new(Db::User.query.find { var(:username) == username }.not_nil!)
(user = context.loaders.user.load(Db::User.query.select(:id).find { var(:username) == username }.not_nil!.id)) && User.new(user)
end
@[GraphQL::Field]
@ -77,19 +77,22 @@ module Backend
def admins(context : Context) : Array(User)?
context.admin!
Db::User.query.where(admin: true).map { |user| User.new(user) }
# Db::User.query.where(admin: true).map { |user| User.new(user) }
context.loaders.user.load(Db::User.query.select(:id).where(admin: true).map(&.id)).map { |u| User.new(u.not_nil!) }
end
@[GraphQL::Field]
# Teacher by ID
def teacher(id : Int32) : Teacher
Teacher.new(Db::Teacher.find!(id))
def teacher(context : Context, id : Int32) : Teacher?
# Teacher.new(Db::Teacher.find!(id))
(teacher = context.loaders.teacher.load(id)) && Teacher.new(teacher)
end
@[GraphQL::Field]
# All teachers
def teachers : Array(Teacher)
Db::Teacher.query.map { |teacher| Teacher.new(teacher) }
def teachers(context : Context) : Array(Teacher)
# Db::Teacher.query.map { |teacher| Teacher.new(teacher) }
context.loaders.teacher.load(Db::Teacher.query.select(:id).map(&.id)).map { |t| Teacher.new(t.not_nil!) }
end
@[GraphQL::Field]
@ -97,7 +100,7 @@ module Backend
def student(context : Context, id : Int32) : Student?
context.admin!
Student.new(Db::Student.find!(id))
(student = context.loaders.student.load(id)) && Student.new(student)
end
@[GraphQL::Field]
@ -105,7 +108,7 @@ module Backend
def students(context : Context) : Array(Student)?
context.admin!
Db::Student.query.map { |student| Student.new(student) }
context.loaders.student.load(Db::Student.query.select(:id).map(&.id)).map { |s| Student.new(s.not_nil!) }
end
@[GraphQL::Field]
@ -113,7 +116,8 @@ module Backend
def vote(context : Context, id : Int32) : Vote?
context.admin!
Vote.new(Db::Vote.find!(id))
# Vote.new(Db::Vote.find!(id))
(vote = context.loaders.vote.load(id)) && Vote.new(vote)
end
@[GraphQL::Field]
@ -121,7 +125,8 @@ module Backend
def votes(context : Context) : Array(Vote)?
context.admin!
Db::Vote.query.map { |vote| Vote.new(vote) }
# Db::Vote.query.map { |vote| Vote.new(vote) }
context.loaders.vote.load(Db::Vote.query.select(:id).map(&.id)).map { |v| Vote.new(v.not_nil!) }
end
@[GraphQL::Field]
@ -148,7 +153,8 @@ module Backend
def teacher_vote(context : Context, id : Int32) : TeacherVote?
context.admin!
TeacherVote.new(Db::TeacherVote.find!(id))
# TeacherVote.new(Db::TeacherVote.find!(id))
(tv = context.loaders.teacher_vote.load(id)) && TeacherVote.new(tv)
end
@[GraphQL::Field]
@ -156,7 +162,8 @@ module Backend
def teacher_votes(context : Context) : Array(TeacherVote)?
context.admin!
Db::TeacherVote.query.map { |vote| TeacherVote.new(vote) }
# Db::TeacherVote.query.map { |vote| TeacherVote.new(vote) }
context.loaders.teacher_vote.load(Db::TeacherVote.query.select(:id).map(&.id)).map { |tv| TeacherVote.new(tv.not_nil!) }
end
end
end

View File

@ -75,7 +75,6 @@ module Backend
@[GraphQL::Field]
# User's full name
def name(formal : Bool = true) : String
# ldap.name(formal)
if formal
"#{@model.last_name}, #{@model.first_name}"
else
@ -113,15 +112,15 @@ module Backend
end
@[GraphQL::Field]
# User's external teacher object
def teacher : Teacher?
@model.teacher.try { |t| Teacher.new(t) }
# User's external student object
def student(context : Context) : Student?
(student = context.loaders.user_student.load(@model)) && Student.new(student)
end
@[GraphQL::Field]
# User's external student object
def student : Student?
@model.student.try { |s| Student.new(s) }
# User's external teacher object
def teacher(context : Context) : Teacher?
(teacher = context.loaders.user_teacher.load(@model)) && Teacher.new(teacher)
end
end

View File

@ -6,14 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Benutzeraccounts | Mentorenwahl</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code&display=swap" rel="stylesheet">
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=jetbrains-mono:400" rel="stylesheet" />
<style>
html,
body {
font-family: "Fira Code", monospace;
font-family: "JetBrains Mono", monospace;
}
.column {
@ -38,7 +37,7 @@
}
code {
font-family: "Fira Code", monospace;
font-family: "JetBrains Mono", monospace;
background: #f4f4f4;
word-wrap: break-word;
box-decoration-break: clone;

View File

@ -23,7 +23,7 @@ type Query {
student(id: Int!): Student
students: [Student!]
studentsCanVote: Boolean!
teacher(id: Int!): Teacher!
teacher(id: Int!): Teacher
teacherVote(id: Int!): TeacherVote
teacherVotes: [TeacherVote!]
teachers: [Teacher!]!

View File

@ -3,7 +3,6 @@ use yew::prelude::*;
use crate::components;
pub mod assignments;
pub mod new_user_modal;
pub mod tokens;
pub mod users;

View File

@ -1,74 +0,0 @@
use yew::prelude::*;
pub enum Msg {
CloseModal,
Save,
}
#[derive(Properties, PartialEq)]
pub struct NewUserModalProps {
pub close: Callback<()>,
pub active: bool,
}
pub struct NewUserModal {
username: NodeRef,
admin: NodeRef,
}
impl Component for NewUserModal {
type Message = Msg;
type Properties = NewUserModalProps;
fn create(_ctx: &Context<Self>) -> Self {
Self {
username: NodeRef::default(),
admin: NodeRef::default(),
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::CloseModal => {
ctx.props().close.emit(());
false
}
Msg::Save => {
log::debug!("self.username = {:?}", self.username);
log::debug!("self.admin = {:?}", self.admin);
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let close = ctx.link().callback(|_| Msg::CloseModal);
html! {
<div class={classes!("modal", if ctx.props().active { Some("is-active") } else { None })}>
<div onclick={close.clone()}
class={classes!("modal-background")}
/>
<div class={classes!("modal-card")}>
<header class={classes!("modal-card-head")}>
<p class={classes!("modal-card-title")}>{ "Neuer Benutzer" }</p>
<button onclick={close.clone()} class={classes!("delete")} aria-label="close" />
</header>
<section class={classes!("modal-card-body")}>
<form onsubmit={ctx.link().callback(|e: SubmitEvent| { e.prevent_default(); Msg::Save })}>
<div class={classes!("field")}>
</div>
</form>
</section>
<footer class={classes!("modal-card-foot")}>
<button onclick={close} class={classes!("button")}>{ "Abbrechen" }</button>
<button class={classes!("button", "is-success")}>{ "Speichern" }</button>
</footer>
</div>
</div>
}
}
}

View File

@ -3,7 +3,6 @@ use yew::prelude::*;
use crate::components;
use crate::graphql;
use crate::routes::settings::new_user_modal;
pub enum Msg {
DoneFetching {
@ -12,8 +11,6 @@ pub enum Msg {
teachers: Option<Vec<graphql::queries::users_by_role::teachers::TeachersTeachers>>,
},
SwitchTab(UsersTab),
OpenModal,
CloseModal,
}
#[derive(Properties, PartialEq, Eq)]
@ -34,7 +31,6 @@ pub struct Users {
errors: graphql::Errors,
students: Option<Vec<graphql::queries::users_by_role::students::StudentsStudents>>,
teachers: Option<Vec<graphql::queries::users_by_role::teachers::TeachersTeachers>>,
modal_active: bool,
}
impl Component for Users {
@ -86,7 +82,6 @@ impl Component for Users {
errors: None,
students: None,
teachers: None,
modal_active: false,
}
}
@ -112,14 +107,6 @@ impl Component for Users {
true
}
}
Msg::OpenModal => {
self.modal_active = true;
true
}
Msg::CloseModal => {
self.modal_active = false;
true
}
}
}
@ -285,19 +272,6 @@ impl Component for Users {
},
}
}
<button onclick={ctx.link().callback(|_| Msg::OpenModal)}
class={classes!("button", "is-success", "is-fullwidth")}
>
<span class={classes!("icon")}>
<i class={classes!("fas", "fa-user-plus")} />
</span>
<span>{ "Neuer Benutzer" }</span>
</button>
<new_user_modal::NewUserModal close={ctx.link().callback(|_| Msg::CloseModal)}
active={self.modal_active}
/>
</div>
<components::graphql_errors::GraphQLErrors errors={self.errors.to_owned()} />