Unverified Commit 9332f60a authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

prepare for follow relations and follow-only checkins

parent f3ae2637
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -1470,6 +1470,54 @@ my @migrations = (
			}
		);
	},

	# v34 -> v35
	sub {
		my ($db) = @_;

		# 1 : follows
		# 2 : follow requested
		# 3 : is blocked by
		$db->query(
			qq{
				create table relations (
					subject_id integer not null references users (id),
					predicate smallint not null,
					object_id integer not null references users (id),
					primary key (subject_id, object_id)
				);
				create view followers as select
					relations.object_id as self_id,
					users.id as id,
					users.name as name
					from relations
					join users on relations.subject_id = users.id
					where predicate = 1;
				create view followees as select
					relations.subject_id as self_id,
					users.id as id,
					users.name as name
					from relations
					join users on relations.object_id = users.id
					where predicate = 1;
				create view follow_requests as select
					relations.object_id as self_id,
					users.id as id,
					users.name as name
					from relations
					join users on relations.subject_id = users.id
					where predicate = 2;
				create view blocked_users as select
					relations.object_id as self_id,
					users.id as id,
					users.name as name
					from relations
					join users on relations.subject_id = users.id
					where predicate = 3;
				update schema_version set version = 35;
			}
		);
	},
);

sub sync_stations {
+194 −0
Original line number Diff line number Diff line
@@ -27,6 +27,18 @@ my %visibility_atoi = (
	private   => 10,
);

my %predicate_itoa = (
	1 => 'follows',
	2 => 'requests_follow',
	3 => 'is_blocked_by',
);

my %predicate_atoi = (
	follows         => 1,
	requests_follow => 2,
	is_blocked_by   => 3,
);

my @sb_templates = (
	undef,
	[ 'DBF',         'https://dbf.finalrewind.org/{name}?rt=1#{tt}{tn}' ],
@@ -710,4 +722,186 @@ sub update_webhook_status {
	);
}

# TODO irgendwo muss auch noch ne einstellung rein, um follows / follow requests global zu deaktivieren

sub get_relation {
	my ( $self, %opt ) = @_;

	my $db     = $opt{db} // $self->{pg}->db;
	my $uid    = $opt{uid};
	my $target = $opt{target};

	my $res_h = $db->select(
		'relations',
		['predicate'],
		{
			subject_id => $uid,
			object_id  => $target
		}
	)->hash;

	if ($res_h) {
		return $predicate_itoa{ $res_h->{predicate} };
	}
	return;

   #my $res_h = $db->select( 'relations', ['subject_id', 'predicate'],
   #	{ subject_id => [$uid, $target], object_id => [$target, $target] } )->hash;
}

sub request_follow {
	my ( $self, %opt ) = @_;

	my $db     = $opt{db} // $self->{pg}->db;
	my $uid    = $opt{uid};
	my $target = $opt{target};

	$db->insert(
		'relations',
		{
			subject_id => $uid,
			predicate  => $predicate_atoi{requests_follow},
			object_id  => $target,
		}
	);
}

sub accept_follow_request {
	my ( $self, %opt ) = @_;

	my $db        = $opt{db} // $self->{pg}->db;
	my $uid       = $opt{uid};
	my $applicant = $opt{applicant};

	$db->update(
		'relations',
		{
			predicate => $predicate_atoi{follows},
		},
		{
			subject_id => $applicant,
			predicate  => $predicate_atoi{requests_follow},
			object_id  => $uid
		}
	);
}

sub reject_follow_request {
	my ( $self, %opt ) = @_;

	my $db        = $opt{db} // $self->{pg}->db;
	my $uid       = $opt{uid};
	my $applicant = $opt{applicant};

	$db->delete(
		'relations',
		{
			subject_id => $applicant,
			predicate  => $predicate_atoi{requests_follow},
			object_id  => $uid
		}
	);
}

sub remove_follower {
	my ( $self, %opt ) = @_;

	my $db       = $opt{db} // $self->{pg}->db;
	my $uid      = $opt{uid};
	my $follower = $opt{follower};

	$db->delete(
		'relations',
		{
			subject_id => $follower,
			predicate  => $predicate_atoi{follows},
			object_id  => $uid
		}
	);
}

sub block {
	my ( $self, %opt ) = @_;

	my $db     = $opt{db} // $self->{pg}->db;
	my $uid    = $opt{uid};
	my $target = $opt{target};

	$db->insert(
		'relations',
		{
			subject_id => $target,
			predicate  => $predicate_atoi{is_blocked_by},
			object_id  => $uid
		},
		{
			on_conflict => \
'(subject_id, object_id) do update set predicate = EXCLUDED.predicate'
		},
	);
}

sub unblock {
	my ( $self, %opt ) = @_;

	my $db     = $opt{db} // $self->{pg}->db;
	my $uid    = $opt{uid};
	my $target = $opt{target};

	$db->delete(
		'relations',
		{
			subject_id => $target,
			predicate  => $predicate_atoi{is_blocked_by},
			object_id  => $uid
		},
	);
}

sub get_followers {
	my ( $self, %opt ) = @_;

	my $db  = $opt{db} // $self->{pg}->db;
	my $uid = $opt{uid};

	my $res = $db->select( 'followers', [ 'id', 'name' ], { self_id => $uid } );

	return $res->hashes->each;
}

sub get_follow_requests {
	my ( $self, %opt ) = @_;

	my $db  = $opt{db} // $self->{pg}->db;
	my $uid = $opt{uid};

	my $res
	  = $db->select( 'follow_requests', [ 'id', 'name' ], { self_id => $uid } );

	return $res->hashes->each;
}

sub get_followees {
	my ( $self, %opt ) = @_;

	my $db  = $opt{db} // $self->{pg}->db;
	my $uid = $opt{uid};

	my $res = $db->select( 'followees', [ 'id', 'name' ], { self_id => $uid } );

	return $res->hashes->each;
}

sub get_blocked_users {
	my ( $self, %opt ) = @_;

	my $db  = $opt{db} // $self->{pg}->db;
	my $uid = $opt{uid};

	my $res
	  = $db->select( 'blocked_users', [ 'id', 'name' ], { self_id => $uid } );

	return $res->hashes->each;
}

1;

t/21-relations.t

0 → 100644
+375 −0
Original line number Diff line number Diff line
#!/usr/bin/env perl

# Copyright (C) 2023 Birthe Friesel <derf@finalrewind.org>
#
# SPDX-License-Identifier: MIT

use Mojo::Base -strict;

# Tests journey entry and statistics

use Test::More;
use Test::Mojo;

# Include application
use FindBin;
require "$FindBin::Bin/../index.pl";

my $t = Test::Mojo->new('Travelynx');

if ( not $t->app->config->{db} ) {
	plan( skip_all => 'No database configured' );
}

$t->app->pg->db->query('drop schema if exists travelynx_test_21 cascade');
$t->app->pg->db->query('create schema travelynx_test_21');
$t->app->pg->db->query('set search_path to travelynx_test_21');
$t->app->pg->on(
	connection => sub {
		my ( $pg, $dbh ) = @_;
		$dbh->do('set search_path to travelynx_test_21');
	}
);

$t->app->config->{mail}->{disabled} = 1;

$t->app->start( 'database', 'migrate' );

my $u = $t->app->users;

my $uid1 = $u->add(
	name     => 'test1',
	email    => 'test1@example.org',
	token    => 'abcd',
	password => q{},
);

my $uid2 = $u->add(
	name     => 'test2',
	email    => 'test2@example.org',
	token    => 'efgh',
	password => q{},
);

$u->verify_registration_token(
	uid   => $uid1,
	token => 'abcd'
);
$u->verify_registration_token(
	uid   => $uid2,
	token => 'efgh'
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	undef
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );

$u->request_follow(
	uid    => $uid1,
	target => $uid2
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	'requests_follow'
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 1 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );
is_deeply(
	[ $u->get_follow_requests( uid => $uid2 ) ],
	[ { id => $uid1, name => 'test1' } ]
);

$u->reject_follow_request(
	uid       => $uid2,
	applicant => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	undef
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );

$u->request_follow(
	uid    => $uid1,
	target => $uid2
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	'requests_follow'
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 1 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );
is_deeply(
	[ $u->get_follow_requests( uid => $uid2 ) ],
	[ { id => $uid1, name => 'test1' } ]
);

$u->accept_follow_request(
	uid       => $uid2,
	applicant => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	'follows'
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       1 );
is( scalar $u->get_followees( uid => $uid1 ),       1 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );
is_deeply(
	[ $u->get_followers( uid => $uid2 ) ],
	[ { id => $uid1, name => 'test1' } ]
);
is_deeply(
	[ $u->get_followees( uid => $uid1 ) ],
	[ { id => $uid2, name => 'test2' } ]
);

$u->remove_follower(
	uid      => $uid2,
	follower => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	undef
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );

$u->request_follow(
	uid    => $uid1,
	target => $uid2
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	'requests_follow'
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);

$u->block(
	uid    => $uid2,
	target => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	'is_blocked_by'
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   1 );
is_deeply(
	[ $u->get_blocked_users( uid => $uid2 ) ],
	[ { id => $uid1, name => 'test1' } ]
);

$u->unblock(
	uid    => $uid2,
	target => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	undef
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   0 );

$u->block(
	uid    => $uid2,
	target => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	'is_blocked_by'
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);
is( scalar $u->get_followers( uid => $uid1 ),       0 );
is( scalar $u->get_followers( uid => $uid2 ),       0 );
is( scalar $u->get_followees( uid => $uid1 ),       0 );
is( scalar $u->get_followees( uid => $uid2 ),       0 );
is( scalar $u->get_follow_requests( uid => $uid1 ), 0 );
is( scalar $u->get_follow_requests( uid => $uid2 ), 0 );
is( scalar $u->get_blocked_users( uid => $uid1 ),   0 );
is( scalar $u->get_blocked_users( uid => $uid2 ),   1 );
is_deeply(
	[ $u->get_blocked_users( uid => $uid2 ) ],
	[ { id => $uid1, name => 'test1' } ]
);

$u->unblock(
	uid    => $uid2,
	target => $uid1
);

is(
	$u->get_relation(
		uid    => $uid1,
		target => $uid2
	),
	undef
);
is(
	$u->get_relation(
		uid    => $uid2,
		target => $uid1
	),
	undef
);

$t->app->pg->db->query('drop schema travelynx_test_21 cascade');
done_testing();