Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ internal class WorkerWorkflow<OutputT>(
ImpostorWorkflow {

override val realIdentifier: WorkflowIdentifier = unsnapshottableIdentifier(workerType)
override fun describeRealIdentifier(): String = "worker $workerType"
override fun describeRealIdentifier(): String = workerType.toString()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the workers have names that make it clear they're workers, no need for this.


override fun initialState(
props: Worker<OutputT>,
Expand Down Expand Up @@ -90,7 +90,7 @@ private class EmitWorkerOutputAction<P, S, O>(
) : WorkflowAction<P, S, O>() {
override fun toString(): String =
WorkflowIdentifierTypeNamer.uniqueName(EmitWorkerOutputAction::class) +
"(worker=$worker, key=\"$renderKey\")"
"(worker=$worker, key=$renderKey)"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically don't use " in logged names.


override fun Updater.apply() {
setOutput(output)
Expand Down
8 changes: 8 additions & 0 deletions workflow-runtime/api/workflow-runtime.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ public final class com/squareup/workflow1/NoopWorkflowInterceptor : com/squareup
public fun onInitialState (Ljava/lang/Object;Lcom/squareup/workflow1/Snapshot;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onPropsChanged (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRender (Ljava/lang/Object;Ljava/lang/Object;Lcom/squareup/workflow1/BaseRenderContext;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRenderAndSnapshot (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/RenderingAndSnapshot;
public fun onSessionStarted (Lkotlinx/coroutines/CoroutineScope;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)V
public fun onSnapshotState (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/Snapshot;
public fun onSnapshotStateWithChildren (Lkotlin/jvm/functions/Function0;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/TreeSnapshot;
}

public final class com/squareup/workflow1/RenderWorkflowKt {
Expand Down Expand Up @@ -45,8 +47,10 @@ public class com/squareup/workflow1/SimpleLoggingWorkflowInterceptor : com/squar
public fun onInitialState (Ljava/lang/Object;Lcom/squareup/workflow1/Snapshot;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onPropsChanged (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRender (Ljava/lang/Object;Ljava/lang/Object;Lcom/squareup/workflow1/BaseRenderContext;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRenderAndSnapshot (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/RenderingAndSnapshot;
public fun onSessionStarted (Lkotlinx/coroutines/CoroutineScope;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)V
public fun onSnapshotState (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/Snapshot;
public fun onSnapshotStateWithChildren (Lkotlin/jvm/functions/Function0;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/TreeSnapshot;
}

public final class com/squareup/workflow1/TreeSnapshot {
Expand All @@ -68,16 +72,20 @@ public abstract interface class com/squareup/workflow1/WorkflowInterceptor {
public abstract fun onInitialState (Ljava/lang/Object;Lcom/squareup/workflow1/Snapshot;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public abstract fun onPropsChanged (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public abstract fun onRender (Ljava/lang/Object;Ljava/lang/Object;Lcom/squareup/workflow1/BaseRenderContext;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public abstract fun onRenderAndSnapshot (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/RenderingAndSnapshot;
public abstract fun onSessionStarted (Lkotlinx/coroutines/CoroutineScope;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)V
public abstract fun onSnapshotState (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/Snapshot;
public abstract fun onSnapshotStateWithChildren (Lkotlin/jvm/functions/Function0;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/TreeSnapshot;
}

public final class com/squareup/workflow1/WorkflowInterceptor$DefaultImpls {
public static fun onInitialState (Lcom/squareup/workflow1/WorkflowInterceptor;Ljava/lang/Object;Lcom/squareup/workflow1/Snapshot;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public static fun onPropsChanged (Lcom/squareup/workflow1/WorkflowInterceptor;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public static fun onRender (Lcom/squareup/workflow1/WorkflowInterceptor;Ljava/lang/Object;Ljava/lang/Object;Lcom/squareup/workflow1/BaseRenderContext;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public static fun onRenderAndSnapshot (Lcom/squareup/workflow1/WorkflowInterceptor;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/RenderingAndSnapshot;
public static fun onSessionStarted (Lcom/squareup/workflow1/WorkflowInterceptor;Lkotlinx/coroutines/CoroutineScope;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)V
public static fun onSnapshotState (Lcom/squareup/workflow1/WorkflowInterceptor;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/Snapshot;
public static fun onSnapshotStateWithChildren (Lcom/squareup/workflow1/WorkflowInterceptor;Lkotlin/jvm/functions/Function0;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/TreeSnapshot;
}

public abstract interface class com/squareup/workflow1/WorkflowInterceptor$RenderContextInterceptor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ public interface WorkflowInterceptor {
session: WorkflowSession
): S = proceed(old, new, state)

/**
* Intercept a full rendering pass which involves rendering then snapshotting the workflow tree.
* This is useful for tracing purposes.
*/
public fun <P, R> onRenderAndSnapshot(
renderProps: P,
proceed: (P) -> RenderingAndSnapshot<R>,
session: WorkflowSession
): RenderingAndSnapshot<R> =
proceed(renderProps)

/**
* Intercepts calls to [StatefulWorkflow.render].
*/
Expand All @@ -98,6 +109,15 @@ public interface WorkflowInterceptor {
session: WorkflowSession
): R = proceed(renderProps, renderState, null)

/**
* Intercept calls to [StatefulWorkflow.snapshotState] including the children calls.
* This is useful to intercept a rendering + snapshot traversal for tracing purposes.
*/
public fun onSnapshotStateWithChildren(
proceed: () -> TreeSnapshot,
session: WorkflowSession
): TreeSnapshot = proceed()

/**
* Intercepts calls to [StatefulWorkflow.snapshotState].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.squareup.workflow1.internal

import com.squareup.workflow1.BaseRenderContext
import com.squareup.workflow1.NoopWorkflowInterceptor
import com.squareup.workflow1.RenderingAndSnapshot
import com.squareup.workflow1.Snapshot
import com.squareup.workflow1.TreeSnapshot
import com.squareup.workflow1.Workflow
import com.squareup.workflow1.WorkflowAction
import com.squareup.workflow1.WorkflowInterceptor
Expand Down Expand Up @@ -57,6 +59,19 @@ internal class ChainedWorkflowInterceptor(
return chainedProceed(old, new, state)
}

override fun <P, R> onRenderAndSnapshot(
renderProps: P,
proceed: (P) -> RenderingAndSnapshot<R>,
session: WorkflowSession
): RenderingAndSnapshot<R> {
val chainedProceed = interceptors.foldRight(proceed) { workflowInterceptor, proceedAcc ->
{ renderProps ->
workflowInterceptor.onRenderAndSnapshot(renderProps, proceedAcc, session)
}
}
return chainedProceed(renderProps)
}

override fun <P, S, O, R> onRender(
renderProps: P,
renderState: S,
Expand All @@ -81,6 +96,18 @@ internal class ChainedWorkflowInterceptor(
return chainedProceed(renderProps, renderState, null)
}

override fun onSnapshotStateWithChildren(
proceed: () -> TreeSnapshot,
session: WorkflowSession
): TreeSnapshot {
val chainedProceed = interceptors.foldRight(proceed) { workflowInterceptor, proceedAcc ->
{
workflowInterceptor.onSnapshotStateWithChildren(proceedAcc, session)
}
}
return chainedProceed()
}

override fun <S> onSnapshotState(
state: S,
proceed: (S) -> Snapshot?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,16 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
fun snapshot(workflow: StatefulWorkflow<*, *, *, *>): TreeSnapshot {
@Suppress("UNCHECKED_CAST")
val typedWorkflow = workflow as StatefulWorkflow<PropsT, StateT, OutputT, RenderingT>
val childSnapshots = subtreeManager.createChildSnapshots()
val rootSnapshot = interceptor.intercept(typedWorkflow, this)
.snapshotState(state)
return TreeSnapshot(
workflowSnapshot = rootSnapshot,
// Create the snapshots eagerly since subtreeManager is mutable.
childTreeSnapshots = { childSnapshots }
)
return interceptor.onSnapshotStateWithChildren({
val childSnapshots = subtreeManager.createChildSnapshots()
val rootSnapshot = interceptor.intercept(typedWorkflow, this)
.snapshotState(state)
TreeSnapshot(
workflowSnapshot = rootSnapshot,
// Create the snapshots eagerly since subtreeManager is mutable.
childTreeSnapshots = { childSnapshots }
)
}, this)
}

override fun runningSideEffect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.squareup.workflow1.writeByteStringWithLength
import com.squareup.workflow1.writeUtf8WithLength
import okio.Buffer
import okio.ByteString
import kotlin.LazyThreadSafetyMode.NONE

/**
* Value type that can be used to distinguish between different workflows of different types or
Expand All @@ -23,6 +24,14 @@ internal data class WorkflowNodeId(
name: String = ""
) : this(workflow.identifier, name)

private val debugString by lazy(NONE) {
if (name.isBlank()) {
identifier.toString()
} else {
"$identifier named $name"
}
}

fun matches(
otherWorkflow: Workflow<*, *, *>,
otherName: String
Expand All @@ -48,6 +57,8 @@ internal data class WorkflowNodeId(
return WorkflowNodeId(identifier, name)
}
}

override fun toString() = debugString
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing WorkflowNodeId(identifier=foo, name=bar) to just foo and foo named bar

}

internal fun <W : Workflow<I, O, R>, I, O, R>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class WorkflowRunner<PropsT, OutputT, RenderingT>(
protoWorkflow: Workflow<PropsT, OutputT, RenderingT>,
props: StateFlow<PropsT>,
snapshot: TreeSnapshot?,
interceptor: WorkflowInterceptor,
private val interceptor: WorkflowInterceptor,
private val runtimeConfig: RuntimeConfig
) {
private val workflow = protoWorkflow.asStatefulWorkflow()
Expand Down Expand Up @@ -65,9 +65,11 @@ internal class WorkflowRunner<PropsT, OutputT, RenderingT>(
* between every subsequent call to [processAction].
*/
fun nextRendering(): RenderingAndSnapshot<RenderingT> {
val rendering = rootNode.render(workflow, currentProps)
val snapshot = rootNode.snapshot(workflow)
return RenderingAndSnapshot(rendering, snapshot)
return interceptor.onRenderAndSnapshot(currentProps, { props ->
val rendering = rootNode.render(workflow, props)
val snapshot = rootNode.snapshot(workflow)
RenderingAndSnapshot(rendering, snapshot)
}, rootNode)
}

/**
Expand Down
2 changes: 2 additions & 0 deletions workflow-testing/api/workflow-testing.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ public final class com/squareup/workflow1/testing/RenderIdempotencyChecker : com
public fun onInitialState (Ljava/lang/Object;Lcom/squareup/workflow1/Snapshot;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onPropsChanged (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRender (Ljava/lang/Object;Ljava/lang/Object;Lcom/squareup/workflow1/BaseRenderContext;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRenderAndSnapshot (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/RenderingAndSnapshot;
public fun onSessionStarted (Lkotlinx/coroutines/CoroutineScope;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)V
public fun onSnapshotState (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/Snapshot;
public fun onSnapshotStateWithChildren (Lkotlin/jvm/functions/Function0;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/TreeSnapshot;
}

public abstract interface class com/squareup/workflow1/testing/RenderTestResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ internal class RealRenderTesterTest {

assertEquals(
"Expected only one output to be expected: " +
"child worker ${typeOf<Worker<Unit>>()} expected to emit " +
"child ${typeOf<Worker<Unit>>()} expected to emit " +
"kotlin.Unit but WorkflowAction.noAction() was already processed.",
failure.message
)
Expand Down Expand Up @@ -591,7 +591,7 @@ internal class RealRenderTesterTest {
tester.render()
}
assertEquals(
"Tried to render unexpected child worker ${typeOf<MySpecialWorker>()}",
"Tried to render unexpected child ${typeOf<MySpecialWorker>()}",
error.message
)
}
Expand Down Expand Up @@ -698,7 +698,7 @@ internal class RealRenderTesterTest {
}
assertEquals(
"""
Multiple expectations matched child worker ${typeOf<EmptyWorker>()}:
Multiple expectations matched child ${typeOf<EmptyWorker>()}:
worker TestWorker
duplicate expectation
""".trimIndent(),
Expand Down Expand Up @@ -754,7 +754,7 @@ internal class RealRenderTesterTest {
tester.render()
}
assertEquals(
"Expected keys to be unique for worker " +
"Expected keys to be unique for " +
"com.squareup.workflow1.Worker<kotlin.Unit>: key=\"\"",
error.message
)
Expand Down
2 changes: 2 additions & 0 deletions workflow-tracing/api/workflow-tracing.api
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ public final class com/squareup/workflow1/diagnostic/tracing/TracingWorkflowInte
public fun onInitialState (Ljava/lang/Object;Lcom/squareup/workflow1/Snapshot;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onPropsChanged (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRender (Ljava/lang/Object;Ljava/lang/Object;Lcom/squareup/workflow1/BaseRenderContext;Lkotlin/jvm/functions/Function3;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Ljava/lang/Object;
public fun onRenderAndSnapshot (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/RenderingAndSnapshot;
public fun onSessionStarted (Lkotlinx/coroutines/CoroutineScope;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)V
public fun onSnapshotState (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/Snapshot;
public fun onSnapshotStateWithChildren (Lkotlin/jvm/functions/Function0;Lcom/squareup/workflow1/WorkflowInterceptor$WorkflowSession;)Lcom/squareup/workflow1/TreeSnapshot;
}

public final class com/squareup/workflow1/diagnostic/tracing/TracingWorkflowInterceptorKt {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
{"name":"used/free memory","ph":"C","ts":0,"pid":0,"tid":0,"args":{"usedMemory":1,"freeMemory":42}},
{"name":"Snapshot","ph":"B","ts":0,"pid":0,"tid":0,"args":{}},
{"name":"Snapshot","ph":"E","ts":0,"pid":0,"tid":0,"args":{}},
{"name":"Sink received: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"t","args":{"action":"sendAndAwaitApplication(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=\"\"))"}},
{"name":"WorkflowAction: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"p","args":{"action":"sendAndAwaitApplication(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=\"\"))","oldState":"0","newState":"{no change}","output":"fired!"}},
{"name":"Sink received: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"t","args":{"action":"sendAndAwaitApplication(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=))"}},
{"name":"WorkflowAction: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"p","args":{"action":"sendAndAwaitApplication(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=))","oldState":"0","newState":"{no change}","output":"fired!"}},
{"name":"Worker<String> (2)","ph":"O","ts":0,"pid":0,"tid":0,"id":"2","args":{"snapshot":"0"}},
{"name":"Render Pass","cat":"rendering","ph":"B","ts":0,"pid":0,"tid":0,"args":{"props":"3"}},
{"name":"used/free memory","ph":"C","ts":0,"pid":0,"tid":0,"args":{"usedMemory":1,"freeMemory":42}},
Expand Down