Skip to content

Commit ea2dd24

Browse files
authored
Documentation for Workflow Qureies with Markdown (#267)
* Documentation for Workflow Qureies with Markdown * Updating new feature bar with this documentation + addressing comments
1 parent fe017c0 commit ea2dd24

File tree

5 files changed

+384
-34
lines changed

5 files changed

+384
-34
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
---
2+
layout: default
3+
title: Workflow Queries with Formatted Data
4+
permalink: /docs/concepts/workflow-queries-formatted-data
5+
---
6+
7+
# Workflow Queries with Formatted Data
8+
9+
This guide explains how to implement workflow queries that return preformatted data for enhanced rendering in Cadence Web UI. This feature allows workflow authors to return structured data in Markdown format that can be rendered directly in the Cadence Web interface, providing richer visualization and better user experience.
10+
11+
The formatted data feature enables workflows to respond to queries with data that includes rendering instructions, allowing the UI to display content beyond simple text responses.
12+
13+
---
14+
15+
## Overview
16+
17+
### The Goal
18+
19+
Support rendering preformatted data on cadence-web in places such as the Query API. Examples of data that can be preformatted include:
20+
21+
- **Markdown** - Rich text with formatting, links, and structure
22+
23+
The reason for prerendering is that workflow authors have access to workflow data that they may wish to render on the Cadence UI, and such rendering entirely client-side is difficult given the current Cadence workflow API.
24+
25+
### How It Works
26+
27+
When a workflow query responds with data in a specific shape, Cadence Web can render it with appropriate formatting. The response must include:
28+
29+
1. A response type identifier
30+
2. A MIME type format specifier
31+
3. The actual formatted data
32+
33+
---
34+
35+
## Response Format
36+
37+
### Basic Structure
38+
39+
To enable formatted rendering, your workflow query must respond with data in the following shape:
40+
41+
```json
42+
{
43+
"cadenceResponseType": "formattedData",
44+
"format": "<mime-type format>",
45+
"data": "<formatted data specific to the format>"
46+
}
47+
```
48+
49+
### Supported MIME Type
50+
51+
The `format` field should contain the supported MIME type identifier:
52+
53+
- `text/markdown` - Markdown content
54+
55+
---
56+
57+
## Examples
58+
59+
### Markdown Response
60+
61+
```json
62+
{
63+
"cadenceResponseType": "formattedData",
64+
"format": "text/markdown",
65+
"data": "### Workflow Status Report\n\n**Current Stage:** Processing\n\n- [x] Data validation completed\n- [x] Initial processing done\n- [ ] Final verification pending\n\n[View detailed logs](https://internal.example.com/logs/workflow-123)\n\n**Progress:** 75% complete"
66+
}
67+
```
68+
69+
This would render as:
70+
71+
### Workflow Status Report
72+
73+
**Current Stage:** Processing
74+
75+
- [x] Data validation completed
76+
- [x] Initial processing done
77+
- [ ] Final verification pending
78+
79+
[View detailed logs](https://internal.example.com/logs/workflow-123)
80+
81+
**Progress:** 75% complete
82+
83+
---
84+
85+
## Go Implementation
86+
87+
### Type Definition
88+
89+
```go
90+
// PrerenderedQueryResponse defines the structure for formatted query responses
91+
type PrerenderedQueryResponse struct {
92+
CadenceResponseType string `json:"cadenceResponseType"`
93+
Format string `json:"format"`
94+
Data json.RawMessage `json:"data"`
95+
}
96+
```
97+
98+
### Example Usage
99+
100+
```go
101+
package main
102+
103+
import (
104+
"context"
105+
"encoding/json"
106+
"go.uber.org/cadence/workflow"
107+
)
108+
109+
// Example workflow with formatted query response
110+
func SampleWorkflow(ctx workflow.Context) error {
111+
// Workflow implementation...
112+
return nil
113+
}
114+
115+
// Query handler that returns formatted markdown
116+
func WorkflowStatusQuery(ctx workflow.Context) (PrerenderedQueryResponse, error) {
117+
// Get current workflow state
118+
progress := getWorkflowProgress(ctx)
119+
120+
// Create markdown content
121+
markdown := fmt.Sprintf(`### Workflow Status Report
122+
123+
**Current Stage:** %s
124+
125+
**Progress:** %d%% complete
126+
127+
**Tasks Completed:**
128+
%s
129+
130+
**Next Steps:**
131+
%s
132+
133+
---
134+
*Last updated: %s*
135+
`,
136+
progress.CurrentStage,
137+
progress.PercentComplete,
138+
formatTaskList(progress.CompletedTasks),
139+
formatTaskList(progress.PendingTasks),
140+
time.Now().Format("2006-01-02 15:04:05"),
141+
)
142+
143+
return PrerenderedQueryResponse{
144+
CadenceResponseType: "formattedData",
145+
Format: "text/markdown",
146+
Data: json.RawMessage(fmt.Sprintf(`"%s"`, markdown)),
147+
}, nil
148+
}
149+
150+
// Helper function for creating markdown responses
151+
func NewMarkdownQueryResponse(md string) PrerenderedQueryResponse {
152+
data, _ := json.Marshal(md)
153+
return PrerenderedQueryResponse{
154+
CadenceResponseType: "formattedData",
155+
Format: "text/markdown",
156+
Data: data,
157+
}
158+
}
159+
160+
// Register the query handler
161+
func init() {
162+
workflow.RegisterQueryHandler("workflow_status", WorkflowStatusQuery)
163+
}
164+
```
165+
166+
### Advanced Example with Error Handling
167+
168+
```go
169+
func DetailedWorkflowQuery(ctx workflow.Context, queryType string) (interface{}, error) {
170+
switch queryType {
171+
case "status":
172+
return createStatusMarkdown(ctx)
173+
default:
174+
return nil, fmt.Errorf("unknown query type: %s", queryType)
175+
}
176+
}
177+
178+
func createStatusMarkdown(ctx workflow.Context) (PrerenderedQueryResponse, error) {
179+
status := getCurrentStatus(ctx)
180+
181+
markdown := fmt.Sprintf(`# Workflow Execution Report
182+
183+
## Summary
184+
- **ID:** %s
185+
- **Status:** %s
186+
- **Started:** %s
187+
- **Duration:** %s
188+
189+
## Recent Activities
190+
%s
191+
192+
## Errors
193+
%s
194+
`,
195+
workflow.GetInfo(ctx).WorkflowExecution.ID,
196+
status.State,
197+
status.StartTime.Format("2006-01-02 15:04:05"),
198+
time.Since(status.StartTime).String(),
199+
formatActivities(status.Activities),
200+
formatErrors(status.Errors),
201+
)
202+
203+
return NewMarkdownQueryResponse(markdown), nil
204+
}
205+
```
206+
207+
---
208+
209+
## Security Considerations
210+
211+
### XSS Prevention
212+
213+
Taking input from a workflow and rendering it as Markdown without sanitization is a potential XSS (Cross-Site Scripting) vector. An attacker could inject malicious content including:
214+
215+
- Raw HTML tags that might be processed by the Markdown renderer
216+
- JavaScript in HTML tags embedded within Markdown
217+
- Malicious links or images that could exfiltrate data
218+
219+
### Mitigation Strategies
220+
221+
1. **Server-Side Sanitization**: All content must be sanitized before rendering
222+
2. **Content Security Policy (CSP)**: Implement strict CSP headers
223+
3. **Input Validation**: Validate format types and data structure
224+
4. **Allowlist Approach**: Only allow the known-safe MIME type
225+
226+
### Best Practices
227+
228+
- Always validate the `cadenceResponseType` field
229+
- Sanitize all user-provided content before rendering
230+
- Use Content Security Policy headers
231+
- Regularly audit and update sanitization libraries
232+
- Consider implementing rate limiting for query requests
233+
234+
---
235+
236+
## Integration with Cadence Web
237+
238+
### Client-Side Rendering
239+
240+
The Cadence Web UI automatically detects formatted responses and renders them appropriately:
241+
242+
1. **Detection**: Check for `cadenceResponseType: "formattedData"`
243+
2. **Format Processing**: Parse the `format` field to determine renderer
244+
3. **Data Rendering**: Apply appropriate rendering logic based on MIME type
245+
4. **Sanitization**: Apply security sanitization before display
246+
247+
### Supported Renderers
248+
249+
- **Markdown**: Rendered using a markdown parser with syntax highlighting
250+
251+
### Fallback Behavior
252+
253+
If the formatted response cannot be rendered:
254+
255+
1. Display the raw data as JSON
256+
2. Show an error message indicating the format issue
257+
3. Provide option to view raw response data
258+
259+
---
260+
261+
## Testing and Debugging
262+
263+
### Testing Formatted Responses
264+
265+
```go
266+
func TestFormattedQueryResponse(t *testing.T) {
267+
// Create test workflow environment
268+
env := testsuite.NewTestWorkflowEnvironment()
269+
270+
// Register workflow and query
271+
env.RegisterWorkflow(SampleWorkflow)
272+
env.SetQueryHandler("status", WorkflowStatusQuery)
273+
274+
// Start workflow
275+
env.ExecuteWorkflow(SampleWorkflow)
276+
277+
// Query with formatted response
278+
result, err := env.QueryWorkflow("status")
279+
require.NoError(t, err)
280+
281+
var response PrerenderedQueryResponse
282+
err = result.Get(&response)
283+
require.NoError(t, err)
284+
285+
// Validate response structure
286+
assert.Equal(t, "formattedData", response.CadenceResponseType)
287+
assert.Equal(t, "text/markdown", response.Format)
288+
assert.NotEmpty(t, response.Data)
289+
}
290+
```
291+
### Debugging Tips
292+
293+
1. **Validate JSON Structure**: Ensure response matches expected format
294+
2. **Check MIME Types**: Verify format field contains valid MIME type
295+
3. **Test Sanitization**: Confirm content is properly sanitized
296+
4. **Monitor Performance**: Large formatted responses may impact query performance
297+
298+
---
299+
300+
## Additional Resources
301+
302+
### Related Documentation
303+
304+
- [Basic Workflow Queries](/docs/concepts/queries) - Overview of standard query functionality
305+
- [Cadence Web UI Documentation](https://github.com/uber/cadence-web) - UI components and rendering
306+
- [OWASP XSS Prevention](https://owasp.org/www-community/attacks/xss/) - Security best practices
307+
308+
### Code References
309+
310+
- [Go Implementation Example](https://sg.uberinternal.com/code.uber.internal/uber-code/go-code/-/blob/src/code.uber.internal/cadence/operations/workflow/history-db-scan/instructions.go?L22)
311+
- [Cadence Go Client Documentation](https://pkg.go.dev/go.uber.org/cadence)
312+
313+
### Community Resources
314+
315+
- [Cadence Community Slack](https://join.slack.com/t/cadenceworkflow/shared_invite/enQtNDczNTgxMjYyNzlmLTJmZDlkMjNhZjRmNjhkYjdlN2I0NGQ0YzgwZGUxM2JmNWFlZTI0OTM0NDgzZTZjNTk4YWYyOGQ3YjgzNzUwNjQ)
316+
- [GitHub Discussions](https://github.com/cadence-workflow/cadence/discussions)

sidebars.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const sidebars: SidebarsConfig = {
6363
{ type: 'doc', id: 'concepts/http-api' },
6464
{ type: 'doc', id: 'concepts/data-converter' },
6565
{ type: 'doc', id: 'concepts/grafana-helm-setup' },
66+
{ type: 'doc', id: 'concepts/workflow-queries-formatted-data' },
6667
],
6768
},
6869
{

src/components/GrafanaSetupPopup/index.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const GrafanaSetupPopup: React.FC = () => {
77
// Prevent multiple instances and add debug logging
88
React.useEffect(() => {
99
// Check if popup is already being rendered
10-
const existingPopup = document.querySelector('[data-popup-id="grafana_helm_setup_2025_ultra_v2"]');
10+
const existingPopup = document.querySelector('[data-popup-id="workflow_queries_formatted_data_2025"]');
1111
if (!existingPopup) {
1212
console.log('🚀 GrafanaSetupPopup component mounted - rendering popup');
1313
setShouldRender(true);
@@ -21,13 +21,13 @@ const GrafanaSetupPopup: React.FC = () => {
2121
}
2222

2323
return (
24-
<div data-popup-id="grafana_helm_setup_2025_ultra_v2">
24+
<div data-popup-id="workflow_queries_formatted_data_2025">
2525
<NewFeaturePopup
26-
featureId="grafana_helm_setup_2025_ultra_v2"
27-
title="✨ Brand New: Grafana Helm Setup Guide"
28-
description="🎯 Get production-ready monitoring for your Cadence workflows! Our comprehensive guide covers ServiceMonitor setup, automated metrics scraping, and pre-built dashboards. Perfect for Kubernetes deployments."
29-
linkUrl="/docs/concepts/grafana-helm-setup"
30-
linkText="🚀 Explore the Guide"
26+
featureId="workflow_queries_formatted_data_2025"
27+
title="🎨 Workflow Queries with Formatted Data "
28+
description="Transform your workflow queries with rich formatting! Return Markdown directly in Cadence Web UI. Create interactive status reports, data tables for better workflow monitoring."
29+
linkUrl="/docs/concepts/workflow-queries-formatted-data"
30+
linkText="📖 Learn More"
3131
showDays={365}
3232
/>
3333
</div>

0 commit comments

Comments
 (0)