Skip to content

Commit 998fdf2

Browse files
Sergio WongSergio Wong
authored andcommitted
addresed code review comments
1 parent c28e432 commit 998fdf2

File tree

6 files changed

+161
-40
lines changed

6 files changed

+161
-40
lines changed

components/breeze/actions/create-project/create-project.mjs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,10 @@ export default {
1414
props: {
1515
breeze,
1616
workspaceId: {
17-
type: "string",
18-
label: "Workspace ID",
19-
description: "The workspace to associate the project with",
20-
optional: true,
21-
async options() {
22-
const workspaces = await this.breeze.getWorkspaces();
23-
return workspaces.map((workspace) => ({
24-
label: workspace.name || workspace.id,
25-
value: workspace.id,
26-
}));
27-
},
17+
propDefinition: [
18+
"breeze",
19+
"workspaceId",
20+
],
2821
},
2922
projectName: {
3023
type: "string",

components/breeze/breeze.app.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ export default {
44
type: "app",
55
app: "breeze",
66
propDefinitions: {
7+
workspaceId: {
8+
type: "string",
9+
label: "Workspace ID",
10+
description: "The workspace to associate the project with",
11+
optional: true,
12+
async options() {
13+
const workspaces = await this.getWorkspaces();
14+
return workspaces.map((workspace) => ({
15+
label: workspace.name || workspace.id,
16+
value: workspace.id,
17+
}));
18+
},
19+
},
720
projectId: {
821
type: "string",
922
label: "Project ID",
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
alwaysApply: true
3+
---
4+
Write the actions for the Breeze app.
5+
6+
### General guidelines:
7+
- Avoid manual truthiness checks for optional parameters, as @pipedream/platform utilities (@pipedream/axios) automatically exclude undefined values.
8+
- Use the Breeze API documentation at https://www.breeze.pm/api as the primary reference for all implementation details.
9+
- Make sure to define and set the annotations property of the components.
10+
11+
### API Call Guidelines
12+
- Always wrap API calls with the `_makeRequest` method.
13+
- Component methods must call **wrappers** of `_makeRequest`.
14+
- **Never** call `axios` directly in component methods.
15+
- **Never** call `_makeRequest` directly in component methods.
16+
17+
### Prop Type Guidelines
18+
- Do not use the `"number"` datatype in props. Use `"string"` instead when representing numeric values.
19+
- Valid datatypes for props are `"string"`, `"integer"`, `"string[]"`, `"object"`, `"boolean"`
20+
21+
### Boolean Handling
22+
- When API documentation uses `"string"` or `"integer"` values to simulate booleans (e.g., `1` for `true`, `"true"` as a string), define the corresponding props using the native `boolean` type.
23+
24+
This applies to:
25+
1. **Prop type definition** → Use `boolean` for clarity and semantic accuracy.
26+
2. **Default values and options** → Express as `true` or `false`, not simulated values.
27+
28+
Within the component logic, convert the `boolean` prop into the format expected by the API (e.g., `1`/`0` or `"true"`/`"false"`).
29+
30+
For example:
31+
```js
32+
include_performers: this.includePerformers ? 1 : 0
33+
```
34+
- In the package.json file essentially you'll only set the dependencies property, set version prop to "0.1.0", you can refer to the following example that was created for Leonardo AI components:
35+
```
36+
{
37+
"name": "@pipedream/leonardo_ai",
38+
"version": "0.1.0",
39+
"description": "Pipedream Leonardo AI Components",
40+
"main": "leonardo_ai.app.mjs",
41+
"keywords": [
42+
"pipedream",
43+
"leonardo_ai"
44+
],
45+
"homepage": "https://pipedream.com/apps/leonardo_ai",
46+
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
47+
"publishConfig": {
48+
"access": "public"
49+
},
50+
"dependencies": {
51+
"@pipedream/platform": "^3.1.0",
52+
"form-data": "^4.0.4"
53+
}
54+
}
55+
```
56+
--as a reference of an example API call to Breeze, including authorization, you can use the one we feature at Pipedream. Remember, you'll wrap API calls inside _makeRequest and action, source methods need to call _makeRequest.
57+
```
58+
import { axios } from "@pipedream/platform"
59+
export default defineComponent({
60+
props: {
61+
breeze: {
62+
type: "app",
63+
app: "breeze",
64+
}
65+
},
66+
async run({steps, $}) {
67+
return await axios($, {
68+
url: `https://api.breeze.pm/me.json`,
69+
params: {
70+
api_token: `${this.breeze.$auth.api_token}`,
71+
},
72+
})
73+
},
74+
})
75+
```
76+
--For each prop that is used more than one create a propDefinition for it and reuse it in component code, changing description, detaults as needed.
77+
--For props that handle date values, if a specific format is required, ensure prop descriptions has the format string is enclosed in backticks to enhance clarity and readability. For example:
78+
```ts
79+
description: "Only retrieve events that start AFTER the specified UTC date (format: `YYYY-MM-DD`)."
80+
```
81+
- When writing descriptions for props of type `boolean`, always be explicit about the conditional behavior by including phrases like **“If true,”** or **“If false,”** rather than leaving the logic implicit. For example:
82+
```ts
83+
description: "If true, returns associated performers/artists with the event."
84+
```
85+
is preferred over:
86+
```ts
87+
description: "Returns any associated performers/artists with the event."
88+
89+
**Polling Sources**
90+
new-task
91+
Prompt: Emit new event when a new task is created.
92+
- Require a `project_id` prop which the new task needs to be associated with.
93+
- Dynamic options must be populated via the **Get Projects** endpoint ([docs](https://www.breeze.pm/api#:~:text=Get%20projects)).
94+
95+
task-moved
96+
Prompt: Emit new event when a task is moved to another list. Required prop 'taskID' to identify the task.
97+
- Dynamic options for 'taskID' must be populated via the **Get lists** endpoint ([docs](https://www.breeze.pm/api#:~:text=Get%20cards)).
98+
99+
task-status-updated
100+
Prompt: Emit new event when a task's status is updated. Required prop 'taskID' to identify the task.
101+
- Dynamic options for 'taskID' must be populated via the **Get lists** endpoint ([docs](https://www.breeze.pm/api#:~:text=Get%20cards)).
102+
103+
**Actions**
104+
create-task
105+
Prompt: Generates a new task within an existing project in breeze. Required props: 'project ID', 'task name'. Optional props: 'task description', 'due date'.
106+
-When you come up for the description for this component follow format: <component description>. [See documentation](https://www.breeze.pm/api#:~:text=Create%20a%20card)
107+
This action should:
108+
- Require a `stage_id` parameter to associate the task to.
109+
- Dynamic options must be populated via the **Get lists** endpoint ([docs](https://www.breeze.pm/api#:~:text=Get%20lists)).
110+
- Require a `swimlane_id` parameter to associate the task to.
111+
- Dynamic options must be populated via the **Get Swimlanes** endpoint ([docs](https://www.breeze.pm/api#:~:text=Get%20swimlanes)).
112+
113+
create-project
114+
Prompt: Establishes a new project in breeze. Required props: 'project name'. Optional props: 'project description', 'start date', 'end date'.
115+
-When you come up for the description for this component follow format: <component description>. [See documentation](https://www.breeze.pm/api#:~:text=Create%20a%20project
116+
)
117+
This action should:https://www.breeze.pm/api#:~:text=Get%20swimlanes)
118+
- Require a `workspace_id` parameter to associate the project to.
119+
- Dynamic options must be populated via the **Get a Workspaces** endpoint ([docs](https://www.breeze.pm/api#:~:text=Get%20a%20workspaces)).
120+
121+
find-project
122+
Prompt: Searches for a specific project in breeze using the name. Required props: 'project name'.
123+
-When you come up for the description for this component follow format: <component description>. [See documentation](https://www.breeze.pm/api#:~:text=Get%20Projects)

components/breeze/sources/new-task/new-task.mjs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ export default {
3333
},
3434
methods: {
3535
generateMeta(task) {
36+
const ts = Date.parse(task.created_at || "") || Date.now();
3637
return {
3738
id: task.id,
3839
summary: `New task created: ${task.name || task.id}`,
39-
ts: Date.parse(task.created_at || new Date()),
40+
ts,
4041
};
4142
},
4243
},
@@ -49,46 +50,37 @@ export default {
4950

5051
// Sort by created_at to process in order
5152
const sortedTasks = tasks.sort((a, b) => {
52-
const dateA = Date.parse(a.created_at || 0);
53-
const dateB = Date.parse(b.created_at || 0);
53+
const dateA = a.created_at
54+
? Date.parse(a.created_at)
55+
: 0;
56+
const dateB = b.created_at
57+
? Date.parse(b.created_at)
58+
: 0;
5459
return dateA - dateB;
5560
});
5661

5762
const lastTaskIdInt = lastTaskId
5863
? parseInt(lastTaskId)
5964
: null;
6065
let newLastTaskId = lastTaskIdInt;
61-
let foundLastTask = !lastTaskIdInt;
6266
const MAX_INITIAL_EVENTS = 8;
63-
let initialEventsEmitted = 0;
64-
let shouldStopEmitting = false;
6567

66-
for (const task of sortedTasks) {
68+
// Track highest ID and filter to tasks newer than the stored ID
69+
let newTasks = sortedTasks.filter((task) => {
6770
const taskIdInt = parseInt(task.id);
68-
69-
// Always track the highest task ID we've seen
7071
if (!newLastTaskId || taskIdInt > newLastTaskId) {
7172
newLastTaskId = taskIdInt;
7273
}
74+
return !lastTaskIdInt || taskIdInt > lastTaskIdInt;
75+
});
7376

74-
if (!lastTaskIdInt) {
75-
// First run - emit up to MAX_INITIAL_EVENTS tasks
76-
if (!shouldStopEmitting) {
77-
this.$emit(task, this.generateMeta(task));
78-
initialEventsEmitted++;
79-
if (initialEventsEmitted >= MAX_INITIAL_EVENTS) {
80-
shouldStopEmitting = true;
81-
}
82-
}
83-
// Continue iterating to track highest task ID even after cap
84-
} else if (taskIdInt === lastTaskIdInt) {
85-
// Found the last task we processed, mark that we should start emitting new ones
86-
foundLastTask = true;
87-
} else if (foundLastTask) {
88-
// We've passed the last task, emit all new tasks
89-
this.$emit(task, this.generateMeta(task));
90-
}
91-
// If still looking for last task, we just track the highest ID (already done above)
77+
// On first run, cap the initial backfill to the most recent tasks
78+
if (!lastTaskIdInt && newTasks.length > MAX_INITIAL_EVENTS) {
79+
newTasks = newTasks.slice(-MAX_INITIAL_EVENTS);
80+
}
81+
82+
for (const task of newTasks) {
83+
this.$emit(task, this.generateMeta(task));
9284
}
9385

9486
if (newLastTaskId) {

components/breeze/sources/task-moved/task-moved.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default {
4343
methods: {
4444
generateMeta(task) {
4545
return {
46-
id: `${task.id}-${task.stage_id}-${Date.now()}`,
46+
id: `${task.id}-${task.stage_id}`,
4747
summary: `Task "${task.name || task.id}" moved to stage ${task.stage?.name || task.stage_id}`,
4848
ts: Date.now(),
4949
};

components/breeze/sources/task-status-updated/task-status-updated.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default {
4343
methods: {
4444
generateMeta(task) {
4545
return {
46-
id: `${task.id}-${task.status_id || "null"}-${Date.now()}`,
46+
id: `${task.id}-${task.status_id}`,
4747
summary: `Task "${task.name || task.id}" status updated to ${task.status_name || "No Status"}`,
4848
ts: Date.now(),
4949
};

0 commit comments

Comments
 (0)