From e9b6e27598f661ef9a99adbd1acf460484e9611c Mon Sep 17 00:00:00 2001 From: Mike Lang Date: Mon, 12 Aug 2024 16:53:13 +1000 Subject: [PATCH] sheetsync: Make the middleware interface explicit with a base class The main purpose here is just documentation, so it's clear what the semantics of a middleware is. --- sheetsync/sheetsync/middleware.py | 35 +++++++++++++++++++++++++++++++ sheetsync/sheetsync/sheets.py | 4 +++- sheetsync/sheetsync/streamlog.py | 11 ++++------ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 sheetsync/sheetsync/middleware.py diff --git a/sheetsync/sheetsync/middleware.py b/sheetsync/sheetsync/middleware.py new file mode 100644 index 0000000..bc65303 --- /dev/null +++ b/sheetsync/sheetsync/middleware.py @@ -0,0 +1,35 @@ + + +class Middleware: + """A common interface for connecting sheetsync to a "sheet" source, + including specifics for a certain row type.""" + + def get_rows(self): + """Fetch rows from the sheet, parsed into a list of dicts. + The returned dicts have the following guarenteed keys: + id: A unique identifier for the row + sheet_name: The worksheet associated with the row. + The concept of a worksheet is not common to all backends, but some identifying string + is still required. + _parse_errors: A list of error messages encountered when parsing, to be surfaced to the + user if possible. + In addition to the list of dicts, should return an "is_full" boolean which is True + if all rows were fetched or False if only some subset was fetched (eg. for quota management reasons). + Returns (is_full, rows). + """ + raise NotImplementedError + + def write_value(self, row, key, value): + """Write key=value to the given row. Takes the full row object so any identifying info + can be read from it as needed.""" + raise NotImplementedError + + def mark_modified(self, row): + """Called if any sync action was performed due to this row. + Intended as a way to keep track of recently-changed rows for quota optimization.""" + pass + + def create_row(self, worksheet, id): + """Create a new row with given id in the given worksheet and return it. + Only used for reverse sync.""" + raise NotImplementedError diff --git a/sheetsync/sheetsync/sheets.py b/sheetsync/sheetsync/sheets.py index 34746cf..8ba90d0 100644 --- a/sheetsync/sheetsync/sheets.py +++ b/sheetsync/sheetsync/sheets.py @@ -7,6 +7,8 @@ from monotonic import monotonic import common from common.googleapis import GoogleAPIClient +from .middleware import Middleware + class SheetsClient(object): """Manages Google Sheets API operations""" @@ -70,7 +72,7 @@ class SheetsClient(object): return ''.join(digits) -class SheetsMiddleware(): +class SheetsMiddleware(Middleware): # How many syncs of active sheets to do before checking inactive sheets. # By checking inactive sheets less often, we stay within our API limits. # For example, 4 syncs per inactive check * 5 seconds between syncs = 20s between inactive checks diff --git a/sheetsync/sheetsync/streamlog.py b/sheetsync/sheetsync/streamlog.py index 6ac0122..9634310 100644 --- a/sheetsync/sheetsync/streamlog.py +++ b/sheetsync/sheetsync/streamlog.py @@ -6,6 +6,9 @@ import requests from common.dateutil import parse_utc_only +from .middleware import Middleware + + class StreamLogClient(): """Client for Stream Log server""" @@ -41,7 +44,7 @@ class StreamLogClient(): return None -class StreamLogMiddleware: +class StreamLogMiddleware(Middleware): def __init__(self, client): self.client = client # Maps DB column names to streamlog fields. @@ -121,9 +124,3 @@ class StreamLogMiddleware: if key in self.column_encode: value = self.column_encode[key](value) self.client.write_value(row["id"], self.write_map[key], value) - - def mark_modified(self, row): - pass # not a concept we have - - def create_row(self, worksheet, id): - raise NotImplementedError