Skip to content

Commit e894e3b

Browse files
committed
Updating new feature bar with this documentation + addressing comments
1 parent b6e3db2 commit e894e3b

File tree

5 files changed

+92
-160
lines changed

5 files changed

+92
-160
lines changed

docs/03-concepts/13-workflow-queries-formatted-data.md

Lines changed: 24 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,19 @@ permalink: /docs/concepts/workflow-queries-formatted-data
66

77
# Workflow Queries with Formatted Data
88

9-
<details>
10-
<summary><h2>Introduction</h2></summary>
11-
12-
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 various formats (Markdown, CSV, images, etc.) that can be rendered directly in the Cadence Web interface, providing richer visualization and better user experience.
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.
1310

1411
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.
1512

16-
</details>
13+
---
1714

18-
<details>
19-
<summary><h2>Overview</h2></summary>
15+
## Overview
2016

2117
### The Goal
2218

2319
Support rendering preformatted data on cadence-web in places such as the Query API. Examples of data that can be preformatted include:
2420

2521
- **Markdown** - Rich text with formatting, links, and structure
26-
- **CSV** - Tabular data for easy viewing
27-
- **JPEG/PNG images** - Visual content and charts
28-
- **SVG** - Scalable vector graphics
2922

3023
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.
3124

@@ -37,10 +30,9 @@ When a workflow query responds with data in a specific shape, Cadence Web can re
3730
2. A MIME type format specifier
3831
3. The actual formatted data
3932

40-
</details>
33+
---
4134

42-
<details>
43-
<summary><h2>Response Format</h2></summary>
35+
## Response Format
4436

4537
### Basic Structure
4638

@@ -54,21 +46,15 @@ To enable formatted rendering, your workflow query must respond with data in the
5446
}
5547
```
5648

57-
### Supported MIME Types
49+
### Supported MIME Type
5850

59-
The `format` field should contain well-known MIME type identifiers:
51+
The `format` field should contain the supported MIME type identifier:
6052

6153
- `text/markdown` - Markdown content
62-
- `text/csv` - Comma-separated values
63-
- `image/png` - PNG images
64-
- `image/jpeg` - JPEG images
65-
- `image/svg+xml` - SVG graphics
66-
- `text/html` - HTML content (sanitized)
6754

68-
</details>
55+
---
6956

70-
<details>
71-
<summary><h2>Examples</h2></summary>
57+
## Examples
7258

7359
### Markdown Response
7460

@@ -94,30 +80,9 @@ This would render as:
9480

9581
**Progress:** 75% complete
9682

97-
### CSV Response
98-
99-
```json
100-
{
101-
"cadenceResponseType": "formattedData",
102-
"format": "text/csv",
103-
"data": [["Task", "Status", "Duration"], ["Data Validation", "Complete", "2m 15s"], ["Processing", "In Progress", "5m 30s"], ["Verification", "Pending", "0s"]]
104-
}
105-
```
106-
107-
### Image Response
108-
109-
```json
110-
{
111-
"cadenceResponseType": "formattedData",
112-
"format": "image/svg+xml",
113-
"data": "<svg width=\"200\" height=\"100\"><rect width=\"200\" height=\"100\" fill=\"lightblue\"/><text x=\"100\" y=\"50\" text-anchor=\"middle\">Workflow Progress</text></svg>"
114-
}
115-
```
116-
117-
</details>
83+
---
11884

119-
<details>
120-
<summary><h2>Go Implementation</h2></summary>
85+
## Go Implementation
12186

12287
### Type Definition
12388

@@ -192,16 +157,6 @@ func NewMarkdownQueryResponse(md string) PrerenderedQueryResponse {
192157
}
193158
}
194159

195-
// Helper function for CSV responses
196-
func NewCSVQueryResponse(rows [][]string) PrerenderedQueryResponse {
197-
data, _ := json.Marshal(rows)
198-
return PrerenderedQueryResponse{
199-
CadenceResponseType: "formattedData",
200-
Format: "text/csv",
201-
Data: data,
202-
}
203-
}
204-
205160
// Register the query handler
206161
func init() {
207162
workflow.RegisterQueryHandler("workflow_status", WorkflowStatusQuery)
@@ -215,10 +170,6 @@ func DetailedWorkflowQuery(ctx workflow.Context, queryType string) (interface{},
215170
switch queryType {
216171
case "status":
217172
return createStatusMarkdown(ctx)
218-
case "metrics":
219-
return createMetricsCSV(ctx)
220-
case "diagram":
221-
return createProgressDiagram(ctx)
222173
default:
223174
return nil, fmt.Errorf("unknown query type: %s", queryType)
224175
}
@@ -253,52 +204,24 @@ func createStatusMarkdown(ctx workflow.Context) (PrerenderedQueryResponse, error
253204
}
254205
```
255206

256-
</details>
207+
---
257208

258-
<details>
259-
<summary><h2>Security Considerations</h2></summary>
209+
## Security Considerations
260210

261211
### XSS Prevention
262212

263-
Taking input from a workflow and rendering it as HTML without sanitization is a potential XSS (Cross-Site Scripting) vector. An attacker could inject malicious HTML tags including:
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:
264214

265-
- `<script>` tags for JavaScript execution
266-
- `<img>` tags that make unauthorized HTTP requests
267-
- Other tags that could exfiltrate data or leak secure tokens
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
268218

269219
### Mitigation Strategies
270220

271221
1. **Server-Side Sanitization**: All content must be sanitized before rendering
272222
2. **Content Security Policy (CSP)**: Implement strict CSP headers
273223
3. **Input Validation**: Validate format types and data structure
274-
4. **Allowlist Approach**: Only allow known-safe MIME types
275-
276-
### Implementation Guidelines
277-
278-
```go
279-
// Example sanitization for markdown content
280-
func sanitizeMarkdown(input string) string {
281-
// Use a markdown sanitizer library
282-
policy := bluemonday.UGCPolicy()
283-
return policy.Sanitize(input)
284-
}
285-
286-
// Validate response format
287-
func validateFormat(format string) error {
288-
allowedFormats := map[string]bool{
289-
"text/markdown": true,
290-
"text/csv": true,
291-
"image/png": true,
292-
"image/jpeg": true,
293-
"image/svg+xml": true,
294-
}
295-
296-
if !allowedFormats[format] {
297-
return fmt.Errorf("unsupported format: %s", format)
298-
}
299-
return nil
300-
}
301-
```
224+
4. **Allowlist Approach**: Only allow the known-safe MIME type
302225

303226
### Best Practices
304227

@@ -308,10 +231,9 @@ func validateFormat(format string) error {
308231
- Regularly audit and update sanitization libraries
309232
- Consider implementing rate limiting for query requests
310233

311-
</details>
234+
---
312235

313-
<details>
314-
<summary><h2>Integration with Cadence Web</h2></summary>
236+
## Integration with Cadence Web
315237

316238
### Client-Side Rendering
317239

@@ -325,9 +247,6 @@ The Cadence Web UI automatically detects formatted responses and renders them ap
325247
### Supported Renderers
326248

327249
- **Markdown**: Rendered using a markdown parser with syntax highlighting
328-
- **CSV**: Displayed as interactive tables with sorting/filtering
329-
- **Images**: Embedded directly with proper sizing
330-
- **SVG**: Rendered as scalable graphics with interaction support
331250

332251
### Fallback Behavior
333252

@@ -337,10 +256,9 @@ If the formatted response cannot be rendered:
337256
2. Show an error message indicating the format issue
338257
3. Provide option to view raw response data
339258

340-
</details>
259+
---
341260

342-
<details>
343-
<summary><h2>Testing and Debugging</h2></summary>
261+
## Testing and Debugging
344262

345263
### Testing Formatted Responses
346264

@@ -370,34 +288,16 @@ func TestFormattedQueryResponse(t *testing.T) {
370288
assert.NotEmpty(t, response.Data)
371289
}
372290
```
373-
374-
### CLI Testing
375-
376-
```bash
377-
# Query workflow with formatted response
378-
cadence --domain samples-domain workflow query \
379-
--workflow_id my-workflow-123 \
380-
--query_type status
381-
382-
# Expected output structure:
383-
{
384-
"cadenceResponseType": "formattedData",
385-
"format": "text/markdown",
386-
"data": "### Status Report\n..."
387-
}
388-
```
389-
390291
### Debugging Tips
391292

392293
1. **Validate JSON Structure**: Ensure response matches expected format
393294
2. **Check MIME Types**: Verify format field contains valid MIME type
394295
3. **Test Sanitization**: Confirm content is properly sanitized
395296
4. **Monitor Performance**: Large formatted responses may impact query performance
396297

397-
</details>
298+
---
398299

399-
<details>
400-
<summary><h2>Additional Resources</h2></summary>
300+
## Additional Resources
401301

402302
### Related Documentation
403303

@@ -414,5 +314,3 @@ cadence --domain samples-domain workflow query \
414314

415315
- [Cadence Community Slack](https://join.slack.com/t/cadenceworkflow/shared_invite/enQtNDczNTgxMjYyNzlmLTJmZDlkMjNhZjRmNjhkYjdlN2I0NGQ0YzgwZGUxM2JmNWFlZTI0OTM0NDgzZTZjNTk4YWYyOGQ3YjgzNzUwNjQ)
416316
- [GitHub Discussions](https://github.com/cadence-workflow/cadence/discussions)
417-
418-
</details>

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>

src/components/NewFeaturePopup/index.tsx

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ interface NewFeaturePopupProps {
77
title: string;
88
description: string;
99
linkUrl: string;
10-
linkText: string;
1110
showDays?: number;
1211
}
1312

@@ -16,7 +15,6 @@ const NewFeaturePopup: React.FC<NewFeaturePopupProps> = ({
1615
title,
1716
description,
1817
linkUrl,
19-
linkText,
2018
showDays = 7,
2119
}) => {
2220
const [isVisible, setIsVisible] = useState(false);
@@ -65,7 +63,10 @@ const NewFeaturePopup: React.FC<NewFeaturePopupProps> = ({
6563
setTimeout(() => setIsVisible(false), 400);
6664
};
6765

68-
const handleDismiss = () => {
66+
const handleDismiss = (e?: React.MouseEvent) => {
67+
if (e) {
68+
e.stopPropagation(); // Prevent popup click event from firing
69+
}
6970
localStorage.setItem(`popup_${featureId}_dismissed`, 'true');
7071
setIsAnimating(false);
7172
setTimeout(() => setIsVisible(false), 400);
@@ -87,7 +88,11 @@ const NewFeaturePopup: React.FC<NewFeaturePopupProps> = ({
8788
className={`${styles.overlay} ${isAnimating ? styles.overlayVisible : styles.overlayHidden}`}
8889
onClick={handleClose}
8990
/>
90-
<div className={`${styles.popup} ${isAnimating ? styles.popupVisible : styles.popupHidden}`}>
91+
<div
92+
className={`${styles.popup} ${isAnimating ? styles.popupVisible : styles.popupHidden}`}
93+
onClick={handleNavigate}
94+
style={{ cursor: 'pointer' }}
95+
>
9196
{/* Decorative elements */}
9297
<div className={styles.decorativeTop}></div>
9398
<div className={styles.floatingOrbs}>
@@ -126,32 +131,14 @@ const NewFeaturePopup: React.FC<NewFeaturePopupProps> = ({
126131
{/* Description */}
127132
<p className={styles.description}>{description}</p>
128133

129-
{/* Feature highlights */}
130-
<div className={styles.features}>
131-
<div className={styles.feature}>
132-
<div className={styles.featureIcon}></div>
133-
<span>Quick Setup</span>
134-
</div>
135-
<div className={styles.feature}>
136-
<div className={styles.featureIcon}>📊</div>
137-
<span>Pre-built Dashboards</span>
138-
</div>
139-
<div className={styles.feature}>
140-
<div className={styles.featureIcon}>🔄</div>
141-
<span>Auto ServiceMonitor</span>
142-
</div>
134+
{/* Click instruction */}
135+
<div className={styles.clickInstruction}>
136+
<p className={styles.clickText}>Click anywhere to explore!!</p>
137+
<div className={styles.clickIndicator}>👆</div>
143138
</div>
144139

145-
{/* Actions */}
140+
{/* Keep Maybe later button */}
146141
<div className={styles.actions}>
147-
<button className={styles.primaryButton} onClick={handleNavigate}>
148-
<span className={styles.buttonText}>{linkText}</span>
149-
<div className={styles.buttonShine}></div>
150-
<svg className={styles.buttonIcon} width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
151-
<path d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
152-
<path d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
153-
</svg>
154-
</button>
155142
<button className={styles.secondaryButton} onClick={handleDismiss}>
156143
<span>Maybe later</span>
157144
</button>

0 commit comments

Comments
 (0)