Skip to content

Commit

Permalink
v2 API touchups (#681)
Browse files Browse the repository at this point in the history
These were found during conversion of demo apps to v2:
* setAppConfig - used in unit tests (was provided by testingRuntime as
setConfig)
* Calling v1 transaction/step functions using DBOS.invoke(), from
outside of workflows
* Using DBOS.logger before launch
  • Loading branch information
chuck-dbos authored Dec 16, 2024
1 parent b779825 commit dfc0064
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ export function assertCurrentDBOSContext(): DBOSContext {
}

export function assertCurrentWorkflowContext(): WorkflowContextImpl {
if (!isInWorkflowCtx(asyncLocalCtx.getStore()!)) {
const ctxs = getCurrentContextStore();
if (!ctxs || !isInWorkflowCtx(ctxs)) {
throw new DBOSInvalidWorkflowTransitionError();
}
const ctx = assertCurrentDBOSContext();
Expand Down
46 changes: 45 additions & 1 deletion src/dbos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { ConfiguredInstance } from ".";
import { StoredProcedureFunc } from "./procedure";
import { APITypes } from "./httpServer/handlerTypes";
import { HandlerRegistrationBase } from "./httpServer/handler";
import { set } from "lodash";

// Declare all the HTTP applications a user can pass to the DBOS object during launch()
// This allows us to add a DBOS tracing middleware (extract W3C Trace context, set request ID, etc)
Expand Down Expand Up @@ -139,6 +140,13 @@ export class DBOS {
DBOS.runtimeConfig = runtimeConfig;
}

// For unit testing purposes only
static setAppConfig<T>(key: string, newValue: T): void {
const conf = DBOS.dbosConfig?.application;
if (!conf) throw new DBOSExecutorNotInitializedError();
set(conf, key, newValue);
}

static async launch(httpApps?: DBOSHttpApps) {
// Do nothing is DBOS is already initialized
if (DBOSExecutor.globalInstance) return;
Expand Down Expand Up @@ -263,7 +271,7 @@ export class DBOS {
static get logger(): DLogger {
const ctx = getCurrentDBOSContext();
if (ctx) return ctx.logger;
const executor = DBOS.executor;
const executor = DBOSExecutor.globalInstance;
if (executor) return executor.logger;
return new GlobalLogger();
}
Expand Down Expand Up @@ -565,6 +573,42 @@ export class DBOS {
static invoke<T extends ConfiguredInstance>(targetCfg: T): InvokeFuncsInst<T>;
static invoke<T extends object>(targetClass: T): InvokeFuncs<T>;
static invoke<T extends object>(object: T | ConfiguredInstance): InvokeFuncs<T> | InvokeFuncsInst<T> {
if (!DBOS.isWithinWorkflow()) {
// Run the temp workflow way...
if (typeof object === 'function') {
const ops = getRegisteredOperations(object);

const proxy: Record<string, unknown> = {};
for (const op of ops) {
proxy[op.name] = op.txnConfig
? (...args: unknown[]) => DBOSExecutor.globalInstance!.transaction(
op.registeredFunction as TransactionFunction<unknown[], unknown>, {}, ...args)
: op.commConfig
? (...args: unknown[]) => DBOSExecutor.globalInstance!.external(
op.registeredFunction as StepFunction<unknown[], unknown>, {}, ...args)
: op.procConfig
? (...args: unknown[]) => DBOSExecutor.globalInstance!.procedure<unknown>(op.registeredFunction as StoredProcedureFunc<unknown>, {}, ...args)
: undefined;
}
return proxy as InvokeFuncs<T>;
}
else {
const targetInst = object as ConfiguredInstance;
const ops = getRegisteredOperations(targetInst);

const proxy: Record<string, unknown> = {};
for (const op of ops) {
proxy[op.name] = op.txnConfig
? (...args: unknown[]) => DBOSExecutor.globalInstance!.transaction(
op.registeredFunction as TransactionFunction<unknown[], unknown>, {configuredInstance: targetInst}, ...args)
: op.commConfig
? (...args: unknown[]) => DBOSExecutor.globalInstance!.external(
op.registeredFunction as StepFunction<unknown[], unknown>, {configuredInstance: targetInst}, ...args)
: undefined;
}
return proxy as InvokeFuncsInst<T>;
}
}
const wfctx = assertCurrentWorkflowContext();
if (typeof object === 'function') {
const ops = getRegisteredOperations(object);
Expand Down
5 changes: 5 additions & 0 deletions tests/contextfreeapi.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { DBOS, WorkflowQueue } from '../src';
import { generateDBOSTestConfig, setUpDBOSTestDb, TestKvTable } from './helpers';

DBOS.logger.info("This should not cause a kaboom.");

class TestFunctions
{
@DBOS.transaction()
Expand All @@ -17,6 +19,7 @@ class TestFunctions
@DBOS.workflow()
static async doWorkflow() {
await TestFunctions.doTransaction("");
expect(DBOS.getConfig('is_in_unit_test', false)).toBe(true);
return 'done';
}

Expand Down Expand Up @@ -138,6 +141,7 @@ async function main() {
const config = generateDBOSTestConfig(); // Optional. If you don't, it'll open the YAML file...
await setUpDBOSTestDb(config);
DBOS.setConfig(config);
DBOS.setAppConfig('is_in_unit_test', true);
await DBOS.launch();

const res = await TestFunctions.doWorkflow();
Expand Down Expand Up @@ -212,6 +216,7 @@ async function main5() {
const config = generateDBOSTestConfig();
await setUpDBOSTestDb(config);
DBOS.setConfig(config);
DBOS.setAppConfig('is_in_unit_test', true);
await DBOS.launch();

const res = await DBOS.withWorkflowQueue(wfq.name, async ()=>{
Expand Down
6 changes: 6 additions & 0 deletions tests/v2v1apimix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ async function mainInst() {
const res211 = await inst.doWorkflowV2_V1V1();
expect(res211).toBe('wv2selected tv1step sv1 done');

const resX = await DBOS.invoke(ChildWorkflowsV1).childTx();
expect(resX.startsWith('selected')).toBeTruthy();

const resS = await DBOS.invoke(TestFunctions).doStepV1('bare');
expect(resS).toBe('step bare done');

await DBOS.shutdown();
}

Expand Down

0 comments on commit dfc0064

Please sign in to comment.