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

Backend/bugfix/4098 expired pending request showing new #5215

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Add SV to lite_users

Revision ID: 2fc64cf68321
Revises: 2fd7096c6d4f
Create Date: 2024-11-25 21:22:48.549271

"""

from alembic import op

# revision identifiers, used by Alembic.
revision = "2fc64cf68321"
down_revision = "2fd7096c6d4f"
branch_labels = None
depends_on = None


def upgrade():
op.execute("DROP MATERIALIZED VIEW lite_users;")
op.execute(
"""
CREATE MATERIALIZED VIEW lite_users AS
SELECT
users.id,
users.username,
users.name,
users.city,
date_part('year', age(users.birthdate)) AS age,
users.geom,
users.geom_radius AS radius,
(NOT (users.is_banned OR users.is_deleted)) AS is_visible,
uploads.filename AS avatar_filename,
((users.avatar_key IS NOT NULL) AND (character_length(users.about_me) >= 150)) AS has_completed_profile,
COALESCE(sv_subquery."true", false) AS has_strong_verification
FROM users
LEFT OUTER JOIN uploads ON uploads.key = users.avatar_key
LEFT OUTER JOIN
(SELECT DISTINCT
users_1.id,
true AS "true"
FROM strong_verification_attempts, users users_1
WHERE
((strong_verification_attempts.status = 'succeeded')
AND COALESCE(timezone('Etc/UTC', strong_verification_attempts.passport_expiry_date::timestamp without time zone) >= now(), false)
AND strong_verification_attempts.passport_date_of_birth = users_1.birthdate
AND (
(users_1.gender = 'Woman' AND strong_verification_attempts.passport_sex = 'female')
OR (users_1.gender = 'Man' AND strong_verification_attempts.passport_sex = 'male')
OR strong_verification_attempts.passport_sex = 'unspecified'
OR users_1.has_passport_sex_gender_exception = true
))
) sv_subquery
ON sv_subquery.id = users.id;

CREATE INDEX idx_lite_users_geom ON lite_users USING gist (geom);
CREATE UNIQUE INDEX uq_lite_users_id ON lite_users(id);
CREATE INDEX uq_lite_users_id_visible ON lite_users(id) WHERE is_visible;
CREATE INDEX uq_lite_users_username_visible ON lite_users(username) WHERE is_visible;
"""
)


def downgrade():
raise Exception("Can't downgrade")
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Remove unused profile fields

Revision ID: 2fd7096c6d4f
Revises: d3f2cb24b948
Create Date: 2024-11-25 15:24:12.393262

"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "2fd7096c6d4f"
down_revision = "d3f2cb24b948"
branch_labels = None
depends_on = None


def upgrade():
op.drop_column("users", "my_travels")
op.drop_column("users", "full_name")


def downgrade():
op.add_column("users", sa.Column("full_name", sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column("users", sa.Column("my_travels", sa.VARCHAR(), autoincrement=False, nullable=True))
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Add new languages to database

Revision ID: a84888e4cb0a
Revises: dc57b0bfab39
Create Date: 2024-12-13 20:52:46.143053

"""

from alembic import op
from sqlalchemy.orm.session import Session

from couchers.resources import copy_resources_to_database

# revision identifiers, used by Alembic.
revision = "a84888e4cb0a"
down_revision = "dc57b0bfab39"
branch_labels = None
depends_on = None


def upgrade():
session = Session(bind=op.get_bind())
copy_resources_to_database(session)
session.commit()


def downgrade():
raise Exception("Can't downgrade")
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Add SV duplicate

Revision ID: dc57b0bfab39
Revises: 2fc64cf68321
Create Date: 2024-12-13 20:45:39.172527

"""

from alembic import op

# revision identifiers, used by Alembic.
revision = "dc57b0bfab39"
down_revision = "2fc64cf68321"
branch_labels = None
depends_on = None


def upgrade():
op.execute("ALTER TYPE notificationtopicaction ADD VALUE 'verification__sv_fail'")
op.execute("ALTER TYPE notificationtopicaction ADD VALUE 'verification__sv_success'")
op.execute("ALTER TYPE strongverificationattemptstatus ADD VALUE 'duplicate'")
op.drop_constraint(
op.f("ck_strong_verification_attempts_deleted_implies_minimal_data"), "strong_verification_attempts"
)
# enum values have to be committed before they can be used
op.execute("COMMIT")
op.create_check_constraint(
"deleted_duplicate_implies_minimal_data",
"strong_verification_attempts",
"(NOT ((status = 'deleted') OR (status = 'duplicate'))) OR (has_minimal_data IS TRUE)",
)


def downgrade():
raise Exception("Can't downgrade")
1 change: 1 addition & 0 deletions app/backend/src/couchers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,7 @@ class HostRequestStatus(enum.Enum):
rejected = enum.auto()
confirmed = enum.auto()
cancelled = enum.auto()
expired = enum.auto()


class Message(Base):
Expand Down
19 changes: 15 additions & 4 deletions app/backend/src/couchers/servicers/requests.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import logging
from datetime import timedelta

#testing a diff

import grpc
from google.protobuf import empty_pb2
from sqlalchemy import Float
from sqlalchemy.orm import aliased
from sqlalchemy.sql import and_, func, or_
from sqlalchemy.sql import and_, func, or_, update
from sqlalchemy.sql.functions import percentile_disc

from couchers import errors
Expand Down Expand Up @@ -42,6 +44,8 @@
HostRequestStatus.rejected: conversations_pb2.HOST_REQUEST_STATUS_REJECTED,
HostRequestStatus.confirmed: conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED,
HostRequestStatus.cancelled: conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
HostRequestStatus.expired: conversations_pb2.HOST_REQUEST_STATUS_EXPIRED,

}


Expand Down Expand Up @@ -220,8 +224,9 @@ def CreateHostRequest(self, request, context, session):
)

return requests_pb2.CreateHostRequestRes(host_request_id=host_request.conversation_id)


def GetHostRequest(self, request, context, session):
def GetHostRequest(session, context, request):
host_request = session.execute(
select(HostRequest)
.where_users_column_visible(context, HostRequest.surfer_user_id)
Expand All @@ -241,7 +246,13 @@ def ListHostRequests(self, request, context, session):

pagination = request.number if request.number > 0 else DEFAULT_PAGINATION_LENGTH
pagination = min(pagination, MAX_PAGE_SIZE)


# update status of pending requests that have expired
statement = (update(HostRequest).where(HostRequest.status == HostRequest.pending, HostRequest.start_time <= func.now())
.values(HostRequest.status == HostRequestStatus.expired))
session.execute(statement)
session.commit()

# By outer joining messages on itself where the second id is bigger, only the highest IDs will have
# none as message_2.id. So just filter for these ones to get highest messages only.
# See https://stackoverflow.com/a/27802817/6115336
Expand All @@ -256,6 +267,7 @@ def ListHostRequests(self, request, context, session):
.where(message_2.id == None)
.where(or_(Message.id < request.last_request_id, request.last_request_id == 0))
)


if request.only_sent:
statement = statement.where(HostRequest.surfer_user_id == context.user_id)
Expand All @@ -277,7 +289,6 @@ def ListHostRequests(self, request, context, session):
HostRequest.status == HostRequestStatus.confirmed,
)
)
statement = statement.where(HostRequest.end_time <= func.now())

statement = statement.order_by(Message.id.desc()).limit(pagination + 1)
results = session.execute(statement).all()
Expand Down
28 changes: 28 additions & 0 deletions app/backend/src/tests/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ def test_create_request(db):
assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
assert e.value.details() == errors.DATE_TO_AFTER_ONE_YEAR

def test_expiring_request(db):
user1, token1 = generate_user()
user2, token2 = generate_user()
now = (today() + timedelta(days=2)).isoformat()
today_plus_2 = (today() + timedelta(days=2)).isoformat()
with requests_session(token1) as api:
host_request_id = api.CreateHostRequest(
requests_pb2.CreateHostRequestReq(
host_user_id=user2.id, from_date=now, to_date= today_plus_2, text="Test request 1"
)
).host_request_id






def test_create_request_incomplete_profile(db):
user1, token1 = generate_user(complete_profile=False)
Expand Down Expand Up @@ -251,6 +267,8 @@ def test_ListHostRequests_pagination_regression(db):
user2, token2 = generate_user()
today_plus_2 = (today() + timedelta(days=2)).isoformat()
today_plus_3 = (today() + timedelta(days=3)).isoformat()
now = now()

with requests_session(token1) as api:
host_request_1 = api.CreateHostRequest(
requests_pb2.CreateHostRequestReq(
Expand All @@ -269,6 +287,16 @@ def test_ListHostRequests_pagination_regression(db):
host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
)
).host_request_id

host_request_4 = api.CreateHostRequest(
requests_pb2.CreateHostRequestReq(
host_user_id=user2.id, from_date=today, to_date=today_plus_3, text="Test request 4"
)
).host_request_id

# how to expire this request
# check if gets filtered out after expiring


with requests_session(token2) as api:
res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
subject: "Duplicate Strong Verification attempt"
---

A user tried to do Strong Verification, but that passport is already tied to another user.

* User who tried to reverify with an existing passport
Verification attempt ID: {{ new_attempt_id }}
Name: {{ new_user.name }}
Email: {{ new_user.email }}
Username: {{ new_user.username }}
User ID: {{ new_user.id }}


* User who the passport is already tied to
Verification attempt ID: {{ old_attempt_id }}
Name: {{ old_user.name }}
Email: {{ old_user.email }}
Username: {{ old_user.username }}
User ID: {{ old_user.id }}
12 changes: 12 additions & 0 deletions app/web/__mocks__/nextFontMock 2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = new Proxy(
{},
{
get: function getter() {
return () => ({
className: "className",
variable: "variable",
style: { fontFamily: "fontFamily" },
});
},
}
);
30 changes: 30 additions & 0 deletions app/web/components/CenteredSpinner/CenteredSpinner 2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { CircularProgress, styled } from "@mui/material";

interface CenteredSpinnerProps {
minHeight?: string;
}

interface StyleProps {
minHeight?: string;
}

const StyledCenteredLoaderContainer = styled("div")<StyleProps>(
({ theme, minHeight }) => ({
minHeight: minHeight,
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
marginBlockStart: theme.spacing(6),
})
);

export default function CenteredSpinner({
minHeight = "auto",
}: CenteredSpinnerProps) {
return (
<StyledCenteredLoaderContainer minHeight={minHeight}>
<CircularProgress />
</StyledCenteredLoaderContainer>
);
}
27 changes: 27 additions & 0 deletions app/web/components/StrongVerificationBadge 2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { VerifiedUser } from "@mui/icons-material";
import { styled, Tooltip } from "@mui/material";
import { useTranslation } from "i18n";
import { GLOBAL } from "i18n/namespaces";
import React from "react";

const StyledSpan = styled("span")(({ theme }) => ({
display: "inline-block",
verticalAlign: "middle",
marginLeft: theme.spacing(0.5),
}));

export default function StrongVerificationBadge() {
const { t } = useTranslation(GLOBAL);

return (
<StyledSpan>
<Tooltip title={t("strong_verification.helper_text")}>
<VerifiedUser
data-testid="strong-verification-id"
fontSize="inherit"
color="primary"
/>
</Tooltip>
</StyledSpan>
);
}
Loading