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

[Do not merge] Speed up LB tests #2094

Closed
wants to merge 180 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
180 commits
Select commit Hold shift + click to select a range
7d3afe1
Try passing around connection
amCap1712 Aug 1, 2022
9620959
Try passing around connection - 2
amCap1712 Aug 1, 2022
e9c0ca3
add small sleep as needed
amCap1712 Aug 1, 2022
4e1b461
try again
amCap1712 Aug 1, 2022
36c6616
try again - 3
amCap1712 Aug 1, 2022
a6c3951
update UserTimelineEventDatabaseTestCase
amCap1712 Aug 1, 2022
12975e2
update UserTimelineEventDatabaseTestCase - 2
amCap1712 Aug 1, 2022
555308b
update UserTimelineEventDatabaseTestCase - 3
amCap1712 Aug 1, 2022
bfa3b0f
update UserTestCase
amCap1712 Aug 1, 2022
b1bfb86
disable similar user test for now
amCap1712 Aug 1, 2022
7f5ae1f
update UserTestCase - 3
amCap1712 Aug 1, 2022
cec4f83
update UserTestCase - 4
amCap1712 Aug 1, 2022
925c552
update UserTestCase - 4
amCap1712 Aug 1, 2022
e07cbcc
Update external services oauth tests
amCap1712 Aug 1, 2022
21a9cbb
try again
amCap1712 Aug 1, 2022
923bb6e
try again
amCap1712 Aug 1, 2022
73bc578
try again -2
amCap1712 Aug 1, 2022
485bf6f
try again - 3
amCap1712 Aug 1, 2022
b936ef1
try again - 4
amCap1712 Aug 1, 2022
ab8ffa7
try again - 5
amCap1712 Aug 1, 2022
705517d
update tests listens importer
amCap1712 Aug 1, 2022
72d989c
update tests recommendations
amCap1712 Aug 1, 2022
773e1b8
update tests recommendations - 2
amCap1712 Aug 1, 2022
996833f
update feedback and pinned recordings
amCap1712 Aug 1, 2022
97efe96
update feedback and pinned recordings
amCap1712 Aug 1, 2022
424e635
Use clock_timestamp() instead of now()
amCap1712 Aug 1, 2022
ec5cca2
add setUpClass
amCap1712 Aug 1, 2022
831ff2a
update feedback tests
amCap1712 Aug 1, 2022
350bc3b
update feedback tests - 2
amCap1712 Aug 1, 2022
bdd47ee
Use clock_timestamp instead of now
amCap1712 Aug 1, 2022
33b6fc9
Update tests
amCap1712 Aug 1, 2022
e790f10
Update tests - 2
amCap1712 Aug 1, 2022
304e5ca
Update tests - 3
amCap1712 Aug 1, 2022
5ca05a5
fix ts url
amCap1712 Aug 1, 2022
a9055cc
update MappingTestCase
amCap1712 Aug 1, 2022
df52d50
update pinned recordings test
amCap1712 Aug 1, 2022
1287c01
update missing mb data tests
amCap1712 Aug 1, 2022
2294930
update lfm tests
amCap1712 Aug 2, 2022
ee402cf
update lfm tests - 2
amCap1712 Aug 2, 2022
484f6ba
update lfm tests - 3
amCap1712 Aug 2, 2022
090db09
update lfm tests - 4
amCap1712 Aug 2, 2022
b16a1bc
update tests
amCap1712 Aug 2, 2022
f50fe70
update dumps tests
amCap1712 Aug 2, 2022
0a6a341
update dumps tests -2
amCap1712 Aug 2, 2022
bcf1102
update dumps tests -3
amCap1712 Aug 2, 2022
3292f2a
update dumps tests - 4
amCap1712 Aug 2, 2022
7c25276
update dumps tests - 5
amCap1712 Aug 2, 2022
825539f
update dump tests
amCap1712 Aug 2, 2022
134c8d6
update dump - 2
amCap1712 Aug 2, 2022
873f16b
update dump - 3
amCap1712 Aug 2, 2022
a30d6b4
update dump - 4
amCap1712 Aug 2, 2022
d92eff3
update dump - 5
amCap1712 Aug 2, 2022
1a62c84
update stats tests
amCap1712 Aug 2, 2022
6c70357
update reset db tests
amCap1712 Aug 2, 2022
fe496fc
update more tests
amCap1712 Aug 2, 2022
d2a7408
update more tests - 2
amCap1712 Aug 2, 2022
360c338
move msb tests to transactional
amCap1712 Aug 2, 2022
2cf5d8c
add missing msb_conn
amCap1712 Aug 2, 2022
0930c42
add missing msb_conn - 2
amCap1712 Aug 2, 2022
300bb22
Update domain tests
amCap1712 Aug 2, 2022
c7d86dd
Update domain tests - 2
amCap1712 Aug 2, 2022
c42bc79
update spark handlers and spotify reader
amCap1712 Aug 2, 2022
1edc080
update spark handlers and spotify reader - 2
amCap1712 Aug 2, 2022
2735824
update flask tests
amCap1712 Aug 2, 2022
dd97d85
try fixing indextests
amCap1712 Aug 4, 2022
4b2fe89
Add LocalProxy for db_conn and update index tests
amCap1712 Aug 5, 2022
458c69d
Try fixing profile tests
amCap1712 Aug 5, 2022
1c772e5
Try fixing profile tests - 2
amCap1712 Aug 5, 2022
39ff686
Fix pushing and popping app ctx
amCap1712 Aug 5, 2022
e4c50f8
Update view tests
amCap1712 Aug 5, 2022
c1e28ea
Update view tests - 3
amCap1712 Aug 5, 2022
310cd11
Update view tests - 4
amCap1712 Aug 5, 2022
d35845d
Update view tests - 5
amCap1712 Aug 5, 2022
34b905e
Update view tests - 6
amCap1712 Aug 5, 2022
9efc0d8
fix imports
amCap1712 Aug 5, 2022
668fbc9
fix imports - 2
amCap1712 Aug 5, 2022
c813288
update tests
amCap1712 Aug 5, 2022
c5630bc
update tests - 2
amCap1712 Aug 5, 2022
b0ee5e6
update tests - 3
amCap1712 Aug 5, 2022
02914f6
update tests - 4
amCap1712 Aug 5, 2022
0c3c425
update tests - 5
amCap1712 Aug 5, 2022
918a41d
update tests - 6
amCap1712 Aug 5, 2022
4ce515b
update timescale listenstore tests
amCap1712 Aug 6, 2022
80786e7
fix raw conn access
amCap1712 Aug 6, 2022
ab92ccf
fix setup class
amCap1712 Aug 6, 2022
cb9ff11
fix setup class - 2
amCap1712 Aug 6, 2022
2bb9ed6
init cache module
amCap1712 Aug 6, 2022
f782a2c
update reset cleanup
amCap1712 Aug 6, 2022
8ea7c60
move insert to ts_conn as well
amCap1712 Aug 6, 2022
10c0999
add missing setUpClass
amCap1712 Aug 6, 2022
4960fc5
update tests
amCap1712 Aug 6, 2022
4cee42e
update tests - 2
amCap1712 Aug 6, 2022
687e473
update tests - 3
amCap1712 Aug 6, 2022
53c0688
replace resetdatabasetestcase
amCap1712 Aug 6, 2022
e1620d8
try reset again
amCap1712 Aug 6, 2022
b5592a5
try reset again - 2
amCap1712 Aug 6, 2022
9f95b36
try reset again - 3
amCap1712 Aug 6, 2022
947a541
try reset again - 4
amCap1712 Aug 6, 2022
5790fbe
try reset again - 5
amCap1712 Aug 6, 2022
2d5c88f
try reset again - 6
amCap1712 Aug 6, 2022
9141e8d
try reset again - 7
amCap1712 Aug 6, 2022
bcc3dbb
try reset again - 8
amCap1712 Aug 6, 2022
2abbe22
try reset again - 9
amCap1712 Aug 6, 2022
915f904
Revert ResetDatabaseTestCase
amCap1712 Aug 6, 2022
fec65b0
Revert ResetDatabaseTestCase - 2
amCap1712 Aug 6, 2022
b180899
Fix init
amCap1712 Aug 6, 2022
f7d1339
Fix DumpManagerTestCase
amCap1712 Aug 6, 2022
ad9540a
Fix DumpListenStore
amCap1712 Aug 6, 2022
8d87cba
Fix DumpListenStore - 2
amCap1712 Aug 6, 2022
0667dbe
Fix DumpListenStore - 3
amCap1712 Aug 6, 2022
49a86d5
Update ResetTimescaleTestCase
amCap1712 Aug 6, 2022
8654fb6
Update ResetTimescaleTestCase - 2
amCap1712 Aug 6, 2022
99b282f
Update ResetTimescaleTestCase - 3
amCap1712 Aug 6, 2022
8673c10
Update dump manager
amCap1712 Aug 6, 2022
a7a28a6
try 1 app per class
amCap1712 Aug 6, 2022
1a843e1
try 1 app per class - 2
amCap1712 Aug 6, 2022
a5c3ced
try 1 app per class - 3
amCap1712 Aug 6, 2022
0d6acae
try 1 app per class - 4
amCap1712 Aug 6, 2022
12e0d48
try 1 app per class - 5
amCap1712 Aug 6, 2022
0d54870
try 1 app per class - 6
amCap1712 Aug 6, 2022
813345c
try 1 app per class - 7
amCap1712 Aug 6, 2022
3093981
try 1 app per class - 8
amCap1712 Aug 6, 2022
9094307
try 1 app per class - 9
amCap1712 Aug 6, 2022
a9819dd
try 1 app per class - 10
amCap1712 Aug 6, 2022
b71a5d4
try 1 app per class - 11
amCap1712 Aug 6, 2022
9a1aa5c
update listen api integration tests
amCap1712 Aug 7, 2022
bd7e67f
update listen api integration tests - 2
amCap1712 Aug 7, 2022
bfff71e
update listen api integration tests - 3
amCap1712 Aug 8, 2022
1a4e0f5
update listen api integration tests - 4
amCap1712 Aug 8, 2022
391371f
update listen api integration tests - 5
amCap1712 Aug 8, 2022
342a3cd
update listen api integration tests - 6
amCap1712 Aug 8, 2022
31056ae
update listen api integration tests - 7
amCap1712 Aug 8, 2022
1ce9140
update listen api integration tests - 8
amCap1712 Aug 8, 2022
054fb4f
update listen api integration tests - 9
amCap1712 Aug 8, 2022
c28d5ff
update listen api integration tests - 10
amCap1712 Aug 8, 2022
f6301f8
update listen api integration tests - 11
amCap1712 Aug 8, 2022
ad17d92
update listen api integration tests - 12
amCap1712 Aug 8, 2022
1846f4b
update listen api integration tests - 13
amCap1712 Aug 8, 2022
61e2691
update listen api integration tests - 14
amCap1712 Aug 8, 2022
5dcd238
update listen api integration tests - 15
amCap1712 Aug 8, 2022
01f2681
update listen api integration tests - 16
amCap1712 Aug 8, 2022
1904df3
update listen api integration tests - 17
amCap1712 Aug 8, 2022
6690c3a
update listen api integration tests - 18
amCap1712 Aug 8, 2022
3330af1
update listen api integration tests - 19
amCap1712 Aug 8, 2022
0c5e1a2
update listen api integration tests - 20
amCap1712 Aug 8, 2022
a5399be
update listen api integration tests - 21
amCap1712 Aug 8, 2022
19ed881
update listen api integration tests - 22
amCap1712 Aug 8, 2022
5ade545
update listen api integration tests - 23
amCap1712 Aug 8, 2022
f085992
update listen api integration tests - 24
amCap1712 Aug 9, 2022
dc46be0
update listen api integration tests - 25
amCap1712 Aug 9, 2022
146301c
update listen api integration tests - 26
amCap1712 Aug 9, 2022
50630a5
update listen api integration tests - 27
amCap1712 Aug 9, 2022
4a36aac
update listen api integration tests - 28
amCap1712 Aug 9, 2022
3429236
update listen api integration tests - 29
amCap1712 Aug 9, 2022
92c8394
update listen api integration tests - 30
amCap1712 Aug 9, 2022
41019d4
update listen api integration tests - 31
amCap1712 Aug 9, 2022
fefba76
update listen api integration tests - 32
amCap1712 Aug 9, 2022
b4c4ad0
update listen api integration tests - 33
amCap1712 Aug 9, 2022
07c439a
update listen api integration tests - 34
amCap1712 Aug 9, 2022
809f3a8
update listen api integration tests - 35
amCap1712 Aug 9, 2022
55a8303
update listen api integration tests - 36
amCap1712 Aug 9, 2022
c96dc16
update listen api integration tests - 37
amCap1712 Aug 9, 2022
84609a3
update listen api integration tests - 38
amCap1712 Aug 9, 2022
2a2e2fe
update listen api integration tests - 39
amCap1712 Aug 9, 2022
71821bb
update listen api integration tests - 40
amCap1712 Aug 9, 2022
d254dca
update listen api integration tests - 41
amCap1712 Aug 9, 2022
aeb8b28
update listen api integration tests - 42
amCap1712 Aug 9, 2022
81cb63d
update listen api integration tests - 42
amCap1712 Aug 9, 2022
be2fdaa
update listen api integration tests - 43
amCap1712 Aug 9, 2022
ec10349
update listen api integration tests - 44
amCap1712 Aug 9, 2022
30cb6f1
update listen api integration tests - 45
amCap1712 Aug 9, 2022
d222075
update listen api integration tests - 46
amCap1712 Aug 9, 2022
51c49b4
update playlist api tests
amCap1712 Aug 9, 2022
4dc46d1
update playlist api tests - 2
amCap1712 Aug 9, 2022
a86abc7
ts writer tests try delete from
amCap1712 Aug 9, 2022
0c55962
fix tests
amCap1712 Aug 9, 2022
ba59b8d
fix tests - 2
amCap1712 Aug 9, 2022
82b2c8c
try delete from stats
amCap1712 Aug 9, 2022
247b207
try delete from stats - 2
amCap1712 Aug 9, 2022
8fb8873
try delete from stats - 3
amCap1712 Aug 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions admin/sql/truncate_tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
BEGIN;

TRUNCATE "user" CASCADE;
TRUNCATE data_dump CASCADE;
TRUNCATE spotify_auth CASCADE;
TRUNCATE external_service_oauth CASCADE;
TRUNCATE listens_importer CASCADE;
TRUNCATE recording_feedback CASCADE;
TRUNCATE missing_musicbrainz_data CASCADE;
TRUNCATE user_relationship CASCADE;
TRUNCATE recommendation_feedback CASCADE;
TRUNCATE user_timeline_event CASCADE;
TRUNCATE hide_user_timeline_event CASCADE;
TRUNCATE reported_users CASCADE;
TRUNCATE pinned_recording CASCADE;
TRUNCATE release_color CASCADE;
TRUNCATE user_setting CASCADE;

COMMIT;
3 changes: 2 additions & 1 deletion listenbrainz/db/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Optional

import sqlalchemy
from sqlalchemy import create_engine
Expand All @@ -13,7 +14,7 @@
# public dump
SCHEMA_VERSION_CORE = 8

engine = None
engine: Optional[sqlalchemy.engine.Engine] = None

DUMP_DEFAULT_THREAD_COUNT = 4

Expand Down
61 changes: 28 additions & 33 deletions listenbrainz/db/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ def copy_table(cursor, location, columns, table_name):
cursor.copy_expert(query, f)


def add_dump_entry(timestamp):
def add_dump_entry(connection, timestamp):
""" Adds an entry to the data_dump table with specified time.

Args:
Expand All @@ -648,44 +648,39 @@ def add_dump_entry(timestamp):
Returns:
id (int): the id of the new entry added
"""

with db.engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
INSERT INTO data_dump (created)
VALUES (TO_TIMESTAMP(:ts))
RETURNING id
"""), {
'ts': timestamp,
})
return result.fetchone()['id']
result = connection.execute(sqlalchemy.text("""
INSERT INTO data_dump (created)
VALUES (TO_TIMESTAMP(:ts))
RETURNING id
"""), {
'ts': timestamp,
})
return result.fetchone()['id']


def get_dump_entries():
def get_dump_entries(connection):
""" Returns a list of all dump entries in the data_dump table
"""

with db.engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
SELECT id, created
FROM data_dump
ORDER BY created DESC
"""))

return [dict(row) for row in result]


def get_dump_entry(dump_id):
with db.engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
result = connection.execute(sqlalchemy.text("""
SELECT id, created
FROM data_dump
WHERE id = :dump_id
"""), {
'dump_id': dump_id,
})
if result.rowcount > 0:
return dict(result.fetchone())
return None
ORDER BY created DESC
"""))

return [dict(row) for row in result]


def get_dump_entry(connection, dump_id):
result = connection.execute(sqlalchemy.text("""
SELECT id, created
FROM data_dump
WHERE id = :dump_id
"""), {
'dump_id': dump_id,
})
if result.rowcount > 0:
return dict(result.fetchone())
return None


def import_postgres_dump(private_dump_archive_path=None,
Expand Down
14 changes: 7 additions & 7 deletions listenbrainz/db/dump_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from listenbrainz.db.year_in_music import insert_playlists
from listenbrainz.listenstore.dump_listenstore import DumpListenStore
from listenbrainz.utils import create_path
from listenbrainz.webserver import create_app
from listenbrainz.webserver import create_app, db_conn, ts_conn
from listenbrainz.db.dump import check_ftp_dump_ages


Expand Down Expand Up @@ -92,9 +92,9 @@ def create_full(location, threads, dump_id, do_listen_dump: bool, do_spark_dump:
ls = DumpListenStore(app)
if dump_id is None:
end_time = datetime.now()
dump_id = db_dump.add_dump_entry(int(end_time.strftime('%s')))
dump_id = db_dump.add_dump_entry(db_conn, int(end_time.strftime('%s')))
else:
dump_entry = db_dump.get_dump_entry(dump_id)
dump_entry = db_dump.get_dump_entry(db_conn, dump_id)
if dump_entry is None:
current_app.logger.error("No dump with ID %d found", dump_id)
sys.exit(-1)
Expand Down Expand Up @@ -158,16 +158,16 @@ def create_incremental(location, threads, dump_id):
ls = DumpListenStore(app)
if dump_id is None:
end_time = datetime.now()
dump_id = db_dump.add_dump_entry(int(end_time.strftime('%s')))
dump_id = db_dump.add_dump_entry(db_conn, int(end_time.strftime('%s')))
else:
dump_entry = db_dump.get_dump_entry(dump_id)
dump_entry = db_dump.get_dump_entry(db_conn, dump_id)
if dump_entry is None:
current_app.logger.error(
"No dump with ID %d found, exiting!", dump_id)
sys.exit(-1)
end_time = dump_entry['created']

prev_dump_entry = db_dump.get_dump_entry(dump_id - 1)
prev_dump_entry = db_dump.get_dump_entry(db_conn, dump_id - 1)
if prev_dump_entry is None: # incremental dumps must have a previous dump in the series
current_app.logger.error(
"Invalid dump ID %d, could not find previous dump", dump_id)
Expand Down Expand Up @@ -292,7 +292,7 @@ def import_dump(private_archive, private_timescale_archive,
threads)
if listen_archive:
from listenbrainz.webserver.timescale_connection import _ts as ls
ls.import_listens_dump(listen_archive, threads)
ls.import_listens_dump(ts_conn, listen_archive, threads)

sys.exit(0)

Expand Down
188 changes: 94 additions & 94 deletions listenbrainz/db/external_service_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import sqlalchemy


def save_token(user_id: int, service: ExternalServiceType, access_token: str, refresh_token: str,
token_expires_ts: int, record_listens: bool, scopes: List[str]):
def save_token(connection, user_id: int, service: ExternalServiceType, access_token: str,
refresh_token: str, token_expires_ts: int, record_listens: bool, scopes: List[str]):
""" Add a row to the external_service_oauth table for specified user with corresponding tokens and information.

Args:
Expand All @@ -26,80 +26,80 @@ def save_token(user_id: int, service: ExternalServiceType, access_token: str, re
# be explicitly set to the default value (which would have been used if the row was
# inserted instead).
token_expires = utils.unix_timestamp_to_datetime(token_expires_ts)
with db.engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
INSERT INTO external_service_oauth
(user_id, service, access_token, refresh_token, token_expires, scopes)
result = connection.execute(sqlalchemy.text("""
INSERT INTO external_service_oauth
(user_id, service, access_token, refresh_token, token_expires, scopes)
VALUES
(:user_id, :service, :access_token, :refresh_token, :token_expires, :scopes)
ON CONFLICT (user_id, service)
DO UPDATE SET
user_id = EXCLUDED.user_id,
service = EXCLUDED.service,
access_token = EXCLUDED.access_token,
refresh_token = EXCLUDED.refresh_token,
token_expires = EXCLUDED.token_expires,
scopes = EXCLUDED.scopes,
-- using clock_timestamp because value returned by now() is always fixed during a transaction
-- this creates issues during tests where we want to test that the last_updated field was updated
last_updated = clock_timestamp()
RETURNING id
"""), {
"user_id": user_id,
"service": service.value,
"access_token": access_token,
"refresh_token": refresh_token,
"token_expires": token_expires,
"scopes": scopes,
})

if record_listens:
external_service_oauth_id = result.fetchone()['id']
connection.execute(sqlalchemy.text("""
INSERT INTO listens_importer
(external_service_oauth_id, user_id, service)
VALUES
(:user_id, :service, :access_token, :refresh_token, :token_expires, :scopes)
ON CONFLICT (user_id, service)
DO UPDATE SET
(:external_service_oauth_id, :user_id, :service)
ON CONFLICT (user_id, service) DO UPDATE SET
external_service_oauth_id = EXCLUDED.external_service_oauth_id,
user_id = EXCLUDED.user_id,
service = EXCLUDED.service,
access_token = EXCLUDED.access_token,
refresh_token = EXCLUDED.refresh_token,
token_expires = EXCLUDED.token_expires,
scopes = EXCLUDED.scopes,
last_updated = NOW()
RETURNING id
last_updated = NULL,
latest_listened_at = NULL,
error_message = NULL
"""), {
"user_id": user_id,
"service": service.value,
"access_token": access_token,
"refresh_token": refresh_token,
"token_expires": token_expires,
"scopes": scopes,
})

if record_listens:
external_service_oauth_id = result.fetchone()['id']
connection.execute(sqlalchemy.text("""
INSERT INTO listens_importer
(external_service_oauth_id, user_id, service)
VALUES
(:external_service_oauth_id, :user_id, :service)
ON CONFLICT (user_id, service) DO UPDATE SET
external_service_oauth_id = EXCLUDED.external_service_oauth_id,
user_id = EXCLUDED.user_id,
service = EXCLUDED.service,
last_updated = NULL,
latest_listened_at = NULL,
error_message = NULL
"""), {
"external_service_oauth_id": external_service_oauth_id,
"user_id": user_id,
"service": service.value
})
"external_service_oauth_id": external_service_oauth_id,
"user_id": user_id,
"service": service.value
})


def delete_token(user_id: int, service: ExternalServiceType, remove_import_log: bool):
def delete_token(connection, user_id: int, service: ExternalServiceType, remove_import_log: bool):
""" Delete a user from the external service table.

Args:
user_id: the ListenBrainz row ID of the user
service: the service for which the token should be deleted
remove_import_log: whether the (user, service) combination should be removed from the listens_importer table also
"""
with db.engine.connect() as connection:
connection.execute(sqlalchemy.text("""
DELETE FROM external_service_oauth
WHERE user_id = :user_id AND service = :service
"""), {
"user_id": user_id,
"service": service.value
})
if remove_import_log:
connection.execute(sqlalchemy.text("""
DELETE FROM external_service_oauth
WHERE user_id = :user_id AND service = :service
DELETE FROM listens_importer
WHERE user_id = :user_id AND service = :service
"""), {
"user_id": user_id,
"service": service.value
})
if remove_import_log:
connection.execute(sqlalchemy.text("""
DELETE FROM listens_importer
WHERE user_id = :user_id AND service = :service
"""), {
"user_id": user_id,
"service": service.value
})


def update_token(user_id: int, service: ExternalServiceType, access_token: str,
refresh_token: str, expires_at: int):
def update_token(connection, user_id: int, service: ExternalServiceType,
access_token: str, refresh_token: str, expires_at: int):
""" Update the token for user with specified LB user ID and external service.

Args:
Expand All @@ -110,49 +110,49 @@ def update_token(user_id: int, service: ExternalServiceType, access_token: str,
expires_at: the unix timestamp at which the access token expires
"""
token_expires = utils.unix_timestamp_to_datetime(expires_at)
with db.engine.connect() as connection:
connection.execute(sqlalchemy.text("""
UPDATE external_service_oauth
SET access_token = :access_token
, refresh_token = :refresh_token
, token_expires = :token_expires
, last_updated = now()
WHERE user_id = :user_id AND service = :service
"""), {
"access_token": access_token,
"refresh_token": refresh_token,
"token_expires": token_expires,
"user_id": user_id,
"service": service.value
})
connection.execute(sqlalchemy.text("""
UPDATE external_service_oauth
SET access_token = :access_token
, refresh_token = :refresh_token
, token_expires = :token_expires
-- using clock_timestamp because value returned by now() is always fixed during a transaction
-- this creates issues during tests where we want to test that the last_updated field was updated
, last_updated = clock_timestamp()
WHERE user_id = :user_id AND service = :service
"""), {
"access_token": access_token,
"refresh_token": refresh_token,
"token_expires": token_expires,
"user_id": user_id,
"service": service.value
})


def get_token(user_id: int, service: ExternalServiceType) -> Union[dict, None]:
def get_token(connection, user_id: int, service: ExternalServiceType) -> Union[dict, None]:
""" Get details for user with specified user ID and service.

Args:
user_id: the ListenBrainz row ID of the user
service: the service for which the token should be fetched
"""
with db.engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
SELECT user_id
, "user".musicbrainz_id
, "user".musicbrainz_row_id
, service
, access_token
, refresh_token
, last_updated
, token_expires
, scopes
FROM external_service_oauth
JOIN "user"
ON "user".id = external_service_oauth.user_id
WHERE user_id = :user_id AND service = :service
"""), {
'user_id': user_id,
'service': service.value
})
if result.rowcount > 0:
return dict(result.fetchone())
return None
result = connection.execute(sqlalchemy.text("""
SELECT user_id
, "user".musicbrainz_id
, "user".musicbrainz_row_id
, service
, access_token
, refresh_token
, last_updated
, token_expires
, scopes
FROM external_service_oauth
JOIN "user"
ON "user".id = external_service_oauth.user_id
WHERE user_id = :user_id AND service = :service
"""), {
'user_id': user_id,
'service': service.value
})
if result.rowcount > 0:
return dict(result.fetchone())
return None