From 096a7789274ffbcffc083161c9ec2bc4b390cc5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrej=20Rama=C5=A1euski?= <andrej@x2.cz> Date: Wed, 14 Apr 2021 01:04:55 +0200 Subject: [PATCH] Zakladni podpora Events --- .gitlab-ci.yml | 2 +- lib/CF/Controller/Events.pm | 124 +++++++++++++++++++ lib/CF/Schema/Result/Event.pm | 38 ++++++ openapi.yaml | 226 +++++++++++++++++++++++++++++++++- sql/8/up.sql | 3 + 5 files changed, 388 insertions(+), 5 deletions(-) create mode 100644 lib/CF/Controller/Events.pm diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e93c90f..3dfe9d5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,7 @@ image: docker:19.03.12 variables: DOCKER_TLS_CERTDIR: "/certs" - IMAGE_VER: 1.12.0 + IMAGE_VER: 2.0.0 services: - docker:19.03.12-dind diff --git a/lib/CF/Controller/Events.pm b/lib/CF/Controller/Events.pm new file mode 100644 index 0000000..cdbc1ba --- /dev/null +++ b/lib/CF/Controller/Events.pm @@ -0,0 +1,124 @@ +package CF::Controller::Events; + +use feature 'signatures'; +no warnings qw{ experimental::signatures }; +use Mojo::Base 'Mojolicious::Controller'; +use UUID::URandom qw(create_uuid_string); + +sub create ($c) { + $c->openapi->valid_input or return; + + my $args = $c->req->json; + + my $event = $c->schema->resultset('Event')->create({ + uuid => create_uuid_string(), + type => $args->{type}, + owner_id => $c->user->{id}, + start => $args->{start}, + finish => $args->{finish}, + name => $args->{name}, + description => $args->{description}, + organizer => $args->{organizer} || $c->user->{name}, + }); + + $c->render( + status => 201, + openapi => { id => $event->id }, + ); +}; + +sub get ($c) { + $c->openapi->valid_input or return; + + my $event = $c->schema->resultset('Event')->find($c->stash->{id}); + return $c->error(404, 'Event not found') if ! $event; + + my $formatted = $event->format(); + $formatted->{acl} = []; + $formatted->{stream_url} = $event->stream_url + || '/hls/' . $event->uuid . '.m3u8'; + + my $roles = $event->user_roles( $c->user ); + + if ( $roles->{chairman} ) { + $formatted->{meet_url} = $c->cfg->{jitsi_base_url} . $event->uuid; + $formatted->{record_url} = $c->cfg->{codimd_base_url} . $event->uuid; + } + elsif ( ! $event->is_published ) { + # neni zverejnen + return $c->error(404, 'Event not found'); + } + + $c->render(openapi => $c->spec_filter($formatted, 'Event')); +} + +sub list ($c) { + $c->openapi->valid_input or return; + my $args = $c->validation->output; + + my ($cond, $attrs) = $c->search_parametrs( $args ); + + $cond->{deleted} = undef; + + $cond->{type} = $args->{type} if $args->{type}; + + if ( ! $args->{show_finished} ) { + $cond->{state} = [0, 1]; + } + + if ( ! $c->user_roles->{organizer} ) { + $cond->{is_published} = 't'; + } + + my @events = (); + my $count = $c->schema->resultset('Event')->count($cond); + + if ( $count ) { + my $events = $c->schema->resultset('Event')->search($cond, $attrs); + + EVENT: + while ( my $event = $events->next() ) { + my $formatted = $event->format(); + push @events, $c->spec_filter($formatted, 'EventInList'); + } + } + + $c->render(json => { + data => \@events, + total => $count, + }); +} + +sub update ($c) { + $c->openapi->valid_input or return; + + my $args = $c->req->json; + + my $event = $c->schema->resultset('Event')->find($c->stash->{id}); + return $c->error(404, 'Event not found') if ! $event; + + my $update = $c->prepare_update_data( $event, $c->req->json ); + my $guard = $c->schema->txn_scope_guard; + + $event->update({ + %{ $update }, + changed => \'now()', + }); + + $guard->commit; + $c->render(status => 204, text => ''); +} + +sub delete ($c) { + $c->openapi->valid_input or return; + + my $event = $c->schema->resultset('Event')->find($c->stash->{id}); + return $c->error(404, 'Event not found') if ! $event; + + my $guard = $c->schema->txn_scope_guard; + $event->update({ deleted => \'now()', }); + + $guard->commit; + $c->render(status => 204, text => ''); +} +1; diff --git a/lib/CF/Schema/Result/Event.pm b/lib/CF/Schema/Result/Event.pm index 3c2799d..01e7335 100644 --- a/lib/CF/Schema/Result/Event.pm +++ b/lib/CF/Schema/Result/Event.pm @@ -2,6 +2,8 @@ package CF::Schema::Result::Event; use strict; use warnings; +use feature 'signatures'; +no warnings qw{ experimental::signatures }; use base 'DBIx::Class::Core'; @@ -19,9 +21,12 @@ __PACKAGE__->add_columns( qw( uuid type + state owner_id + is_published start finish + deleted name description organizer @@ -43,4 +48,37 @@ __PACKAGE__->has_many( __PACKAGE__->set_primary_key('id'); +sub format ($self) { + + my $event = { + id => $self->id, + type => $self->type, + state => $self->state, + is_published => $self->is_published ? \1 : \0, + start => $self->start, + finish => $self->finish, + name => $self->name, + description => $self->description, + organizer => $self->organizer, + stream_url => $self->stream_url, + }; + +### ACL??? + + return $event; +} + +sub user_roles ($self, $user) { + + return undef if ! $user; + + my $roles = {}; + + if ( $self->owner_id == $user->{id} ) { + $roles->{chairman} = 1; + } + + return $roles; +} + 1; diff --git a/openapi.yaml b/openapi.yaml index c7294af..a393293 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: - version: 1.12 + version: 2.0 title: CF Online description: CF Online license: @@ -83,12 +83,14 @@ components: id: type: integer readOnly: true - uuid: - type: string - readOnly: true type: type: integer enum: [1, 2, 3] + state: + type: integer + enum: [0, 1, 2] + is_published: + type: boolean start: type: string maxLength: 20 @@ -104,16 +106,53 @@ components: type: string stream_url: type: string + nullable: true meet_url: type: string readOnly: true + nullable: true record_url: type: string readOnly: true + nullable: true acl: type: array items: $ref: '#/components/schemas/EventRole' + EventInList: + type: object + description: Udalost + properties: + id: + type: integer + readOnly: true + type: + type: integer + enum: [1, 2, 3] + state: + type: integer + enum: [0, 1, 2] + is_published: + type: boolean + start: + type: string + maxLength: 20 + finish: + type: string + maxLength: 20 + nullable: true + name: + type: string + description: + type: string + organizer: + type: string + stream_url: + type: string + nullable: true + banner: + type: string + nullable: true EventRole: type: object properties: @@ -276,6 +315,185 @@ paths: 204: description: Program entry updated + /events: + post: + x-mojo-to: events#create + security: + - Bearer: ['organizer'] + tags: + - events + summary: "Pridat udalost" + operationId: createEvent + requestBody: + content: + application/json: + schema: + type: object + properties: + type: + type: integer + enum: [1, 2, 3] + example: 1 + start: + type: string + maxLength: 20 + example: '18.07.2021 10:00' + finish: + type: string + maxLength: 20 + example: '18.07.2021 18:00' + name: + type: string + maxLength: 256 + example: 'Schůze KS Pardubický kraj' + description: + type: string + maxLength: 1024 + example: '' + organizer: + type: string + maxLength: 256 + example: 'PKS Pardubický kraj' + required: + - type + - start + - name + - description + responses: + 201: + description: Event created + content: + application/json: + schema: + type: object + properties: + id: + type: integer + description: Event id + get: + x-mojo-to: events#list + security: + - Bearer: ['optional', '*'] + tags: + - events + summary: "Udalosti" + operationId: getEvents + parameters: + - $ref: '#/components/parameters/offset' + - $ref: '#/components/parameters/limit' + - name: type + in: query + description: "Typ udalosti" + required: false + schema: + type: integer + enum: [1, 2, 3] + - name: show_finished + in: query + description: "Zobrazovat ukoncene" + required: false + schema: + type: boolean + - name: sort + description: "Razeni" + in: query + style: form + schema: + type: array + uniqueItems: true + items: + type: string + enum: [ start, -start] + default: [ start ] + responses: + 200: + description: Events + content: + application/json: + schema: + type: object + properties: + count: + type: integer + description: Celkovy pocet + data: + type: array + items: + $ref: '#/components/schemas/EventInList' + /events/{id}: + get: + x-mojo-to: events#get + security: + - Bearer: ['optional'] + tags: + - events + summary: "Detail udalosti" + operationId: getEvent + responses: + 200: + description: Udalost + content: + application/json: + schema: + $ref: '#/components/schemas/Event' + put: + x-mojo-to: events#update + security: + - Bearer: [] + tags: + - events + summary: "Uprava udalosti" + operationId: updateEvent + requestBody: + content: + application/json: + schema: + type: object + properties: + type: + type: integer + enum: [1, 2, 3] + state: + type: integer + enum: [0, 1, 2] + is_published: + type: boolean + start: + type: string + maxLength: 20 + example: '18.07.2021 10:00' + finish: + type: string + maxLength: 20 + example: '18.07.2021 18:00' + name: + type: string + maxLength: 256 + example: 'Schůze KS Pardubický kraj' + description: + type: string + maxLength: 1024 + example: '' + organizer: + type: string + maxLength: 256 + example: 'PKS Pardubický kraj' + responses: + 204: + description: Event updated + + delete: + x-mojo-to: events#delete + security: + - Bearer: ['organizer'] + tags: + - event + summary: "Smazat udalost" + operationId: deleteEvent + responses: + 204: + description: Event deleted + /sso/subjects: get: x-mojo-to: SSO#subjects diff --git a/sql/8/up.sql b/sql/8/up.sql index dc274ee..dfeabb9 100644 --- a/sql/8/up.sql +++ b/sql/8/up.sql @@ -2,9 +2,12 @@ create table "events" ( "id" integer not null default nextval('uid_seq'), "uuid" uuid not null, -- unique string "type" smallint not null default 1, --1 + "state" integer not null default 0, "owner_id" integer not null, + "is_published" bool not null default false, "start" timestamp(0), "finish" timestamp(0), + "deleted" timestamp(0), "name" text not null, "description" text, "organizer" text, -- GitLab