Skip to content

Instantly share code, notes, and snippets.

@mattiamanzati
Last active March 16, 2022 20:24
Show Gist options
  • Save mattiamanzati/a0a0519ce16b004ee243817343916c7c to your computer and use it in GitHub Desktop.
Save mattiamanzati/a0a0519ce16b004ee243817343916c7c to your computer and use it in GitHub Desktop.
/* Just a fake NodeJS db api exposed by mysql, mssql, etc...*/
interface FakeTrans {}
interface FakeConnection {
query(sql: string, trans?: FakeTrans): Promise<ResultSet>;
beginTransaction(): Promise<FakeTrans>;
commitTransaction(trans: FakeTrans): Promise<void>;
rollbackTransaction(trans: FakeTrans): Promise<void>;
close(): Promise<void>;
}
declare function fakeConnect(): Promise<FakeConnection>;
/* The domain of errors and result of the DB service */
type QueryError = unknown;
type ConnectionError = unknown;
type ResultSet = any[][];
class OpenConnection extends CS.Tagged("OpenConnection")<{
connection: FakeConnection;
}> {}
class OpenTransaction extends CS.Tagged("OpenTransaction")<{
connection: FakeConnection;
transaction: FakeTrans;
}> {}
type Connection = OpenConnection | OpenTransaction;
const Connection = tag<Connection>();
function withConnection<R, E, A>(
eff: T.Effect<R & Has<Connection>, E, A>
): T.Effect<R, ConnectionError | E, A> {
return T.bracketExit_(
T.promise(fakeConnect),
(connection) =>
T.provideService(Connection)(new OpenConnection({ connection }))(eff),
(connection, exit) => T.promise(connection.close)
);
}
function query(sql: string): T.Effect<Has<Connection>, QueryError, ResultSet> {
return T.accessServiceM(Connection)((state) => {
if (state._tag === "OpenConnection")
return T.promise(() => state.connection.query(sql));
return T.promise(() => state.connection.query(sql, state.transaction));
});
}
function transaction<R, E, A>(
eff: T.Effect<R & Has<Connection>, E, A>
): T.Effect<R & Has<Connection>, QueryError | E, A> {
return T.accessServiceM(Connection)((state) => {
if (state._tag === "OpenTransaction") return eff; // do not open more transactions
return T.bracketExit_(
T.promise(state.connection.beginTransaction),
(transaction) =>
T.provideService_(
eff,
Connection,
new OpenTransaction({ connection: state.connection, transaction })
),
(transaction, exit) =>
exit._tag === "Failure"
? T.promise(() => state.connection.rollbackTransaction(transaction))
: T.promise(() => state.connection.commitTransaction(transaction))
);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment