Loading IVOA.js +9 −9 Original line number Original line Diff line number Diff line Loading @@ -51,9 +51,9 @@ const IVOA_capabilitiesEndpoint = staticEndpoint("Stat/capabilities.xml"); */ */ const IVOA_tablesEndpoint = (req) => async (IO) => { const IVOA_tablesEndpoint = (req) => async (IO) => { const query = await readFile("Query/tables.sql")(IO); const query = await readFile("Query/tables.sql")(IO); const conn = await connPostgreSQL()(IO); const pg = await connPostgreSQL()(IO); const result = await fetchQueryResult(conn, query, [])(IO); const result = await fetchQueryResult(pg, query, [])(IO); await quitPostgreSQL(conn)(IO); await quitPostgreSQL(pg)(IO); if (result.tag === "nothing") { if (result.tag === "nothing") { return '<?xml version="1.0" encoding="UTF-8"?><error>No tables found</error>'; return '<?xml version="1.0" encoding="UTF-8"?><error>No tables found</error>'; Loading Loading @@ -96,9 +96,9 @@ const IVOA_syncEndpoint = (req) => async (IO) => { } } // Execute the SQL query // Execute the SQL query const conn = await connPostgreSQL()(IO); const pg = await connPostgreSQL()(IO); const result = await fetchQueryResult(conn, query, [])(IO); const result = await fetchQueryResult(pg, query, [])(IO); await quitPostgreSQL(conn)(IO); await quitPostgreSQL(pg)(IO); if (!result || result.tag === "nothing" || !result.value) { if (!result || result.tag === "nothing" || !result.value) { return makeSyncError("No results"); return makeSyncError("No results"); Loading Loading @@ -314,9 +314,9 @@ export const datalinkTransitsEndpoint = (req) => async (IO) => { // SQL query to fetch transits for a given source_id // SQL query to fetch transits for a given source_id const sql = await readFile("Query/transits.sql")(IO); const sql = await readFile("Query/transits.sql")(IO); const conn = await connPostgreSQL()(IO); const pg = await connPostgreSQL()(IO); const result = await fetchQueryResult(conn, sql, [sourceId])(IO); const result = await fetchQueryResult(pg, sql, [sourceId])(IO); await quitPostgreSQL(conn)(IO); await quitPostgreSQL(pg)(IO); if (result.tag === "nothing") { if (result.tag === "nothing") { return makeDatalinkError(`No transits found for source_id ${sourceId}`); return makeDatalinkError(`No transits found for source_id ${sourceId}`); Loading IVOA.py +9 −9 Original line number Original line Diff line number Diff line Loading @@ -67,9 +67,9 @@ def ivoa_tables_endpoint(req) -> Callable[[object], Awaitable[str]]: async def inner(io): async def inner(io): query = await read_file("Query/tables.sql")(io) query = await read_file("Query/tables.sql")(io) conn = await conn_postgresql()(io) pg = await conn_postgresql()(io) result = await fetch_query_result(conn, query, [])(io) result = await fetch_query_result(pg, query, [])(io) await quit_postgresql(conn)(io) await quit_postgresql(pg)(io) if result["tag"] == "nothing": if result["tag"] == "nothing": return ( return ( Loading Loading @@ -120,9 +120,9 @@ def ivoa_sync_endpoint(req) -> Callable[[object], Awaitable[str]]: ) ) # Execute the SQL query # Execute the SQL query conn = await conn_postgresql()(io) pg = await conn_postgresql()(io) result = await fetch_query_result(conn, query, [])(io) result = await fetch_query_result(pg, query, [])(io) await quit_postgresql(conn)(io) await quit_postgresql(pg)(io) if result["tag"] == "nothing": if result["tag"] == "nothing": return make_sync_error("No results") return make_sync_error("No results") Loading Loading @@ -343,9 +343,9 @@ def ivoa_transits_endpoint(req) -> Callable[[object], Awaitable[str]]: # SQL query to fetch transits for a given source_id # SQL query to fetch transits for a given source_id query = await read_file("Query/transits.sql")(io) query = await read_file("Query/transits.sql")(io) conn = await conn_postgresql()(io) pg = await conn_postgresql()(io) result_query = await fetch_query_result(conn, query, [int(source_id)])(io) result_query = await fetch_query_result(pg, query, [int(source_id)])(io) await quit_postgresql(conn)(io) await quit_postgresql(pg)(io) if result_query["tag"] == "nothing": if result_query["tag"] == "nothing": return make_datalink_error(f"No transits found for source_id {source_id}") return make_datalink_error(f"No transits found for source_id {source_id}") Loading Lib/IO.js +4 −4 Original line number Original line Diff line number Diff line Loading @@ -33,19 +33,19 @@ export const IO = Symbol("IO"); * Functional signature: ((Symbol) -> a) -> a * Functional signature: ((Symbol) -> a) -> a * * * @param {function(Symbol): any} f - A CPS function expecting an IO token * @param {function(Symbol): any} f - A CPS function expecting an IO token * @returns {*} - The result of applying the function to the "IO world" * @returns {*} - The result of applying the function to the IO world */ */ export function createIO(f) { export function createIO(f) { return f(IO); return f(IO); } } /** /** * Alias for createIO which semantically marks a point where an effect is executed. * Semantically marks a point where an effectful IO computation is performed. * * * Functional signature: ((Symbol) -> IO a) -> IO a * Functional signature: ((IO) -> IO a) -> IO a * * * @param {function(Symbol): any} f - A CPS function * @param {function(Symbol): any} f - A CPS function * @returns {*} - The result of the function performing IO operations * @returns {*} - A CPS IO action that performs the given effectful computation */ */ export function performIO(f) { export function performIO(f) { return createIO((w) => f(w)); return createIO((w) => f(w)); Loading Lib/IO.py +12 −4 Original line number Original line Diff line number Diff line Loading @@ -15,7 +15,7 @@ class InternalError(Exception): Functional signature: String -> InternalError Functional signature: String -> InternalError Args: Args: message (str): The error message to associate with the exception. message (str): The error message. """ """ def __init__(self, message: str): def __init__(self, message: str): Loading @@ -26,9 +26,12 @@ class InternalError(Exception): return f"InternalError: {self.message}" return f"InternalError: {self.message}" # Token representing the 'IO world' in an implicit continuation-passing style. # Functional signature: Symbol IO = object() IO = object() """ Token representing the 'IO world' in an implicit continuation-passing style. Functional signature: Symbol """ def create_io(f: Callable[[object], Any]) -> Any: def create_io(f: Callable[[object], Any]) -> Any: Loading Loading @@ -91,7 +94,7 @@ def read_file(path: str) -> Callable[[object], Awaitable[str]]: path: Path to the file. path: Path to the file. Returns: Returns: Callable[[object], Awaitable[str]]: A CPS IO action that reads the file contents and returns them as a string. Callable[[object], Awaitable[str]]: A CPS IO action that reads the file content and returns it as a string. """ """ async def inner(io: object) -> str: async def inner(io: object) -> str: Loading @@ -102,6 +105,11 @@ def read_file(path: str) -> Callable[[object], Awaitable[str]]: nothing = {"tag": "nothing"} nothing = {"tag": "nothing"} """ Represents the absence of a value. Functional signature: Maybe a """ def just(value: Any) -> dict: def just(value: Any) -> dict: Loading Lib/PostgreSQL.js +3 −26 Original line number Original line Diff line number Diff line Loading @@ -14,7 +14,7 @@ import config from "../Conf/PostgreSQL.json" with { type: "json" }; const { Client } = pkg; const { Client } = pkg; /** /** * @typedef {import("pg").Client} PGConn - A PostgreSQL connection in the CPS model * @typedef {object} PGConn - A PostgreSQL connection in the CPS model */ */ /** /** Loading @@ -22,7 +22,7 @@ const { Client } = pkg; * * * Functional signature: () -> IO -> IO PGConn * Functional signature: () -> IO -> IO PGConn * * * @returns {function(Symbol): Promise<PGConn>} - A CPS IO action that returns a connected PostgreSQL connection * @returns {function(Symbol): Promise<PGConn>} - A CPS IO action that returns a PostgreSQL connection * @throws {InternalError} - If the connection fails * @throws {InternalError} - If the connection fails */ */ export function connPostgreSQL() { export function connPostgreSQL() { Loading @@ -38,29 +38,6 @@ export function connPostgreSQL() { }; }; } } /** * Executes an SQL query with parameters, returning no result. * * Functional signature: PGConn -> String -> [String] -> IO -> IO () * * @param {PGConn} conn - A connected PostgreSQL client * @param {string} sql - The SQL statement * @param {Array<string>} params - A list of parameters to bind * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that executes an SQL query without returning any result * @throws {InternalError} - If the query execution fails */ export function execQuery(conn, sql, params) { return async (IO) => { try { await conn.query(sql, params); await putStr("Query executed")(IO); return IO; } catch (err) { throw new InternalError("Execution failed: " + err.message); } }; } /** /** * Executes an SQL query and returns both field metadata and row data. * Executes an SQL query and returns both field metadata and row data. * * Loading Loading @@ -100,7 +77,7 @@ export function fetchQueryResult(conn, sql, params) { * Functional signature: PGConn -> IO -> IO () * Functional signature: PGConn -> IO -> IO () * * * @param {PGConn} conn - A connected PostgreSQL client * @param {PGConn} conn - A connected PostgreSQL client * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that closes the PostgreSQL connection * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that closes the PostgreSQL connection and returns the IO token */ */ export function quitPostgreSQL(conn) { export function quitPostgreSQL(conn) { return async (IO) => { return async (IO) => { Loading Loading
IVOA.js +9 −9 Original line number Original line Diff line number Diff line Loading @@ -51,9 +51,9 @@ const IVOA_capabilitiesEndpoint = staticEndpoint("Stat/capabilities.xml"); */ */ const IVOA_tablesEndpoint = (req) => async (IO) => { const IVOA_tablesEndpoint = (req) => async (IO) => { const query = await readFile("Query/tables.sql")(IO); const query = await readFile("Query/tables.sql")(IO); const conn = await connPostgreSQL()(IO); const pg = await connPostgreSQL()(IO); const result = await fetchQueryResult(conn, query, [])(IO); const result = await fetchQueryResult(pg, query, [])(IO); await quitPostgreSQL(conn)(IO); await quitPostgreSQL(pg)(IO); if (result.tag === "nothing") { if (result.tag === "nothing") { return '<?xml version="1.0" encoding="UTF-8"?><error>No tables found</error>'; return '<?xml version="1.0" encoding="UTF-8"?><error>No tables found</error>'; Loading Loading @@ -96,9 +96,9 @@ const IVOA_syncEndpoint = (req) => async (IO) => { } } // Execute the SQL query // Execute the SQL query const conn = await connPostgreSQL()(IO); const pg = await connPostgreSQL()(IO); const result = await fetchQueryResult(conn, query, [])(IO); const result = await fetchQueryResult(pg, query, [])(IO); await quitPostgreSQL(conn)(IO); await quitPostgreSQL(pg)(IO); if (!result || result.tag === "nothing" || !result.value) { if (!result || result.tag === "nothing" || !result.value) { return makeSyncError("No results"); return makeSyncError("No results"); Loading Loading @@ -314,9 +314,9 @@ export const datalinkTransitsEndpoint = (req) => async (IO) => { // SQL query to fetch transits for a given source_id // SQL query to fetch transits for a given source_id const sql = await readFile("Query/transits.sql")(IO); const sql = await readFile("Query/transits.sql")(IO); const conn = await connPostgreSQL()(IO); const pg = await connPostgreSQL()(IO); const result = await fetchQueryResult(conn, sql, [sourceId])(IO); const result = await fetchQueryResult(pg, sql, [sourceId])(IO); await quitPostgreSQL(conn)(IO); await quitPostgreSQL(pg)(IO); if (result.tag === "nothing") { if (result.tag === "nothing") { return makeDatalinkError(`No transits found for source_id ${sourceId}`); return makeDatalinkError(`No transits found for source_id ${sourceId}`); Loading
IVOA.py +9 −9 Original line number Original line Diff line number Diff line Loading @@ -67,9 +67,9 @@ def ivoa_tables_endpoint(req) -> Callable[[object], Awaitable[str]]: async def inner(io): async def inner(io): query = await read_file("Query/tables.sql")(io) query = await read_file("Query/tables.sql")(io) conn = await conn_postgresql()(io) pg = await conn_postgresql()(io) result = await fetch_query_result(conn, query, [])(io) result = await fetch_query_result(pg, query, [])(io) await quit_postgresql(conn)(io) await quit_postgresql(pg)(io) if result["tag"] == "nothing": if result["tag"] == "nothing": return ( return ( Loading Loading @@ -120,9 +120,9 @@ def ivoa_sync_endpoint(req) -> Callable[[object], Awaitable[str]]: ) ) # Execute the SQL query # Execute the SQL query conn = await conn_postgresql()(io) pg = await conn_postgresql()(io) result = await fetch_query_result(conn, query, [])(io) result = await fetch_query_result(pg, query, [])(io) await quit_postgresql(conn)(io) await quit_postgresql(pg)(io) if result["tag"] == "nothing": if result["tag"] == "nothing": return make_sync_error("No results") return make_sync_error("No results") Loading Loading @@ -343,9 +343,9 @@ def ivoa_transits_endpoint(req) -> Callable[[object], Awaitable[str]]: # SQL query to fetch transits for a given source_id # SQL query to fetch transits for a given source_id query = await read_file("Query/transits.sql")(io) query = await read_file("Query/transits.sql")(io) conn = await conn_postgresql()(io) pg = await conn_postgresql()(io) result_query = await fetch_query_result(conn, query, [int(source_id)])(io) result_query = await fetch_query_result(pg, query, [int(source_id)])(io) await quit_postgresql(conn)(io) await quit_postgresql(pg)(io) if result_query["tag"] == "nothing": if result_query["tag"] == "nothing": return make_datalink_error(f"No transits found for source_id {source_id}") return make_datalink_error(f"No transits found for source_id {source_id}") Loading
Lib/IO.js +4 −4 Original line number Original line Diff line number Diff line Loading @@ -33,19 +33,19 @@ export const IO = Symbol("IO"); * Functional signature: ((Symbol) -> a) -> a * Functional signature: ((Symbol) -> a) -> a * * * @param {function(Symbol): any} f - A CPS function expecting an IO token * @param {function(Symbol): any} f - A CPS function expecting an IO token * @returns {*} - The result of applying the function to the "IO world" * @returns {*} - The result of applying the function to the IO world */ */ export function createIO(f) { export function createIO(f) { return f(IO); return f(IO); } } /** /** * Alias for createIO which semantically marks a point where an effect is executed. * Semantically marks a point where an effectful IO computation is performed. * * * Functional signature: ((Symbol) -> IO a) -> IO a * Functional signature: ((IO) -> IO a) -> IO a * * * @param {function(Symbol): any} f - A CPS function * @param {function(Symbol): any} f - A CPS function * @returns {*} - The result of the function performing IO operations * @returns {*} - A CPS IO action that performs the given effectful computation */ */ export function performIO(f) { export function performIO(f) { return createIO((w) => f(w)); return createIO((w) => f(w)); Loading
Lib/IO.py +12 −4 Original line number Original line Diff line number Diff line Loading @@ -15,7 +15,7 @@ class InternalError(Exception): Functional signature: String -> InternalError Functional signature: String -> InternalError Args: Args: message (str): The error message to associate with the exception. message (str): The error message. """ """ def __init__(self, message: str): def __init__(self, message: str): Loading @@ -26,9 +26,12 @@ class InternalError(Exception): return f"InternalError: {self.message}" return f"InternalError: {self.message}" # Token representing the 'IO world' in an implicit continuation-passing style. # Functional signature: Symbol IO = object() IO = object() """ Token representing the 'IO world' in an implicit continuation-passing style. Functional signature: Symbol """ def create_io(f: Callable[[object], Any]) -> Any: def create_io(f: Callable[[object], Any]) -> Any: Loading Loading @@ -91,7 +94,7 @@ def read_file(path: str) -> Callable[[object], Awaitable[str]]: path: Path to the file. path: Path to the file. Returns: Returns: Callable[[object], Awaitable[str]]: A CPS IO action that reads the file contents and returns them as a string. Callable[[object], Awaitable[str]]: A CPS IO action that reads the file content and returns it as a string. """ """ async def inner(io: object) -> str: async def inner(io: object) -> str: Loading @@ -102,6 +105,11 @@ def read_file(path: str) -> Callable[[object], Awaitable[str]]: nothing = {"tag": "nothing"} nothing = {"tag": "nothing"} """ Represents the absence of a value. Functional signature: Maybe a """ def just(value: Any) -> dict: def just(value: Any) -> dict: Loading
Lib/PostgreSQL.js +3 −26 Original line number Original line Diff line number Diff line Loading @@ -14,7 +14,7 @@ import config from "../Conf/PostgreSQL.json" with { type: "json" }; const { Client } = pkg; const { Client } = pkg; /** /** * @typedef {import("pg").Client} PGConn - A PostgreSQL connection in the CPS model * @typedef {object} PGConn - A PostgreSQL connection in the CPS model */ */ /** /** Loading @@ -22,7 +22,7 @@ const { Client } = pkg; * * * Functional signature: () -> IO -> IO PGConn * Functional signature: () -> IO -> IO PGConn * * * @returns {function(Symbol): Promise<PGConn>} - A CPS IO action that returns a connected PostgreSQL connection * @returns {function(Symbol): Promise<PGConn>} - A CPS IO action that returns a PostgreSQL connection * @throws {InternalError} - If the connection fails * @throws {InternalError} - If the connection fails */ */ export function connPostgreSQL() { export function connPostgreSQL() { Loading @@ -38,29 +38,6 @@ export function connPostgreSQL() { }; }; } } /** * Executes an SQL query with parameters, returning no result. * * Functional signature: PGConn -> String -> [String] -> IO -> IO () * * @param {PGConn} conn - A connected PostgreSQL client * @param {string} sql - The SQL statement * @param {Array<string>} params - A list of parameters to bind * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that executes an SQL query without returning any result * @throws {InternalError} - If the query execution fails */ export function execQuery(conn, sql, params) { return async (IO) => { try { await conn.query(sql, params); await putStr("Query executed")(IO); return IO; } catch (err) { throw new InternalError("Execution failed: " + err.message); } }; } /** /** * Executes an SQL query and returns both field metadata and row data. * Executes an SQL query and returns both field metadata and row data. * * Loading Loading @@ -100,7 +77,7 @@ export function fetchQueryResult(conn, sql, params) { * Functional signature: PGConn -> IO -> IO () * Functional signature: PGConn -> IO -> IO () * * * @param {PGConn} conn - A connected PostgreSQL client * @param {PGConn} conn - A connected PostgreSQL client * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that closes the PostgreSQL connection * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that closes the PostgreSQL connection and returns the IO token */ */ export function quitPostgreSQL(conn) { export function quitPostgreSQL(conn) { return async (IO) => { return async (IO) => { Loading