Skip to content

Commit 6151a73

Browse files
marijahorvat171igorbeslic
authored andcommitted
2298 - add linear component
1 parent 91a3006 commit 6151a73

32 files changed

+2829
-0
lines changed

docs/content/docs/reference/components/linear.mdx

Lines changed: 527 additions & 0 deletions
Large diffs are not rendered by default.

server/apps/server-app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ implementation(project(":server:libs:modules:components:bolna"))
233233
implementation(project(":server:libs:modules:components:json-file"))
234234
implementation(project(":server:libs:modules:components:json-helper"))
235235
implementation(project(":server:libs:modules:components:keap"))
236+
implementation(project(":server:libs:modules:components:linear"))
236237
implementation(project(":server:libs:modules:components:logger"))
237238
implementation(project(":server:libs:modules:components:mailchimp"))
238239
implementation(project(":server:libs:modules:components:mailerlite"))

server/ee/apps/worker-app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ implementation(project(":server:libs:modules:components:graphql-client"))
155155
implementation(project(":server:libs:modules:components:json-file"))
156156
implementation(project(":server:libs:modules:components:json-helper"))
157157
implementation(project(":server:libs:modules:components:keap"))
158+
implementation(project(":server:libs:modules:components:linear"))
158159
implementation(project(":server:libs:modules:components:logger"))
159160
implementation(project(":server:libs:modules:components:mailchimp"))
160161
implementation(project(":server:libs:modules:components:mailerlite"))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
version="1.0"
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2025 ByteChef
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.bytechef.component.linear;
18+
19+
import static com.bytechef.component.definition.ComponentDsl.component;
20+
import static com.bytechef.component.definition.ComponentDsl.tool;
21+
22+
import com.bytechef.component.ComponentHandler;
23+
import com.bytechef.component.definition.ComponentCategory;
24+
import com.bytechef.component.definition.ComponentDefinition;
25+
import com.bytechef.component.linear.action.LinearCreateCommentAction;
26+
import com.bytechef.component.linear.action.LinearCreateIssueAction;
27+
import com.bytechef.component.linear.action.LinearCreateProjectAction;
28+
import com.bytechef.component.linear.action.LinearRawGraphqlQueryAction;
29+
import com.bytechef.component.linear.action.LinearUpdateIssueAction;
30+
import com.bytechef.component.linear.action.LinearUpdateProjectAction;
31+
import com.bytechef.component.linear.connection.LinearConnection;
32+
import com.bytechef.component.linear.trigger.LinearNewIssueTrigger;
33+
import com.bytechef.component.linear.trigger.LinearRemovedIssueTrigger;
34+
import com.bytechef.component.linear.trigger.LinearUpdatedIssueTrigger;
35+
import com.google.auto.service.AutoService;
36+
37+
/**
38+
* @author Marija Horvat
39+
*/
40+
@AutoService(ComponentHandler.class)
41+
public class LinearComponentHandler implements ComponentHandler {
42+
43+
private static final ComponentDefinition COMPONENT_DEFINITION = component("linear")
44+
.title("Linear")
45+
.description("Linear is a project management and issue tracking tool designed primarily for software teams.")
46+
.icon("path:assets/linear.svg")
47+
.categories(ComponentCategory.PROJECT_MANAGEMENT)
48+
.connection(LinearConnection.CONNECTION_DEFINITION)
49+
.actions(
50+
LinearCreateIssueAction.ACTION_DEFINITION,
51+
LinearUpdateIssueAction.ACTION_DEFINITION,
52+
LinearCreateProjectAction.ACTION_DEFINITION,
53+
LinearUpdateProjectAction.ACTION_DEFINITION,
54+
LinearCreateCommentAction.ACTION_DEFINITION,
55+
LinearRawGraphqlQueryAction.ACTION_DEFINITION)
56+
.triggers(
57+
LinearNewIssueTrigger.TRIGGER_DEFINITION,
58+
LinearUpdatedIssueTrigger.TRIGGER_DEFINITION,
59+
LinearRemovedIssueTrigger.TRIGGER_DEFINITION)
60+
.clusterElements(
61+
tool(LinearCreateIssueAction.ACTION_DEFINITION),
62+
tool(LinearUpdateIssueAction.ACTION_DEFINITION),
63+
tool(LinearCreateProjectAction.ACTION_DEFINITION),
64+
tool(LinearUpdateProjectAction.ACTION_DEFINITION),
65+
tool(LinearCreateCommentAction.ACTION_DEFINITION),
66+
tool(LinearRawGraphqlQueryAction.ACTION_DEFINITION));
67+
68+
@Override
69+
public ComponentDefinition getDefinition() {
70+
return COMPONENT_DEFINITION;
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2025 ByteChef
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.bytechef.component.linear.action;
18+
19+
import static com.bytechef.component.definition.ComponentDsl.ModifiableActionDefinition;
20+
import static com.bytechef.component.definition.ComponentDsl.action;
21+
import static com.bytechef.component.definition.ComponentDsl.bool;
22+
import static com.bytechef.component.definition.ComponentDsl.object;
23+
import static com.bytechef.component.definition.ComponentDsl.outputSchema;
24+
import static com.bytechef.component.definition.ComponentDsl.string;
25+
import static com.bytechef.component.linear.constant.LinearConstants.BODY;
26+
import static com.bytechef.component.linear.constant.LinearConstants.ISSUE_ID;
27+
import static com.bytechef.component.linear.constant.LinearConstants.TEAM_ID;
28+
import static com.bytechef.component.linear.util.LinearUtils.executeGraphQLQuery;
29+
30+
import com.bytechef.component.definition.Context;
31+
import com.bytechef.component.definition.OptionsDataSource.ActionOptionsFunction;
32+
import com.bytechef.component.definition.Parameters;
33+
import com.bytechef.component.linear.util.LinearUtils;
34+
import java.util.Map;
35+
36+
/**
37+
* @author Marija Horvat
38+
*/
39+
public class LinearCreateCommentAction {
40+
41+
public static final ModifiableActionDefinition ACTION_DEFINITION = action("createComment")
42+
.title("Create Comment")
43+
.description("Creates a new comment.")
44+
.properties(
45+
string(TEAM_ID)
46+
.label("Team ID")
47+
.description("The ID of the team where this issue should be created.")
48+
.options((ActionOptionsFunction<String>) LinearUtils::getTeamOptions)
49+
.required(true),
50+
string(ISSUE_ID)
51+
.label("Issue ID")
52+
.description("The identifier of the issue to update.")
53+
.options((ActionOptionsFunction<String>) LinearUtils::getIssueOptions)
54+
.optionsLookupDependsOn(TEAM_ID)
55+
.required(true),
56+
string(BODY)
57+
.label("Comment Body")
58+
.description("The comment content in markdown format.")
59+
.required(true))
60+
.output(
61+
outputSchema(
62+
object()
63+
.properties(
64+
bool("success")
65+
.description(
66+
"Whether the operation was successful."),
67+
object("comment")
68+
.description("The comment that was created.")
69+
.properties(
70+
string("id")
71+
.description("The ID of the comment."),
72+
object("issue")
73+
.description("The issue ID that the comment is associated with.")
74+
.properties(string("id")),
75+
string("body")
76+
.description("The comment content in markdown format.")))))
77+
.perform(LinearCreateCommentAction::perform);
78+
79+
private LinearCreateCommentAction() {
80+
}
81+
82+
public static Object perform(
83+
Parameters inputParameters, Parameters connectionParameters, Context context) {
84+
85+
String query =
86+
"mutation{commentCreate(input: {issueId: \"%s\", body: \"%s\"}){success comment{id issue{id} body}}}"
87+
.formatted(
88+
inputParameters.getRequiredString(ISSUE_ID),
89+
inputParameters.getRequiredString(BODY));
90+
91+
Map<String, Object> body = executeGraphQLQuery(query, context);
92+
93+
if (body.get("data") instanceof Map<?, ?> data
94+
&& data.get("commentCreate") instanceof Map<?, ?> commentCreate) {
95+
return commentCreate;
96+
}
97+
98+
return null;
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2025 ByteChef
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.bytechef.component.linear.action;
18+
19+
import static com.bytechef.component.definition.ComponentDsl.action;
20+
import static com.bytechef.component.definition.ComponentDsl.bool;
21+
import static com.bytechef.component.definition.ComponentDsl.integer;
22+
import static com.bytechef.component.definition.ComponentDsl.object;
23+
import static com.bytechef.component.definition.ComponentDsl.option;
24+
import static com.bytechef.component.definition.ComponentDsl.outputSchema;
25+
import static com.bytechef.component.definition.ComponentDsl.string;
26+
import static com.bytechef.component.linear.constant.LinearConstants.ASSIGNEE_ID;
27+
import static com.bytechef.component.linear.constant.LinearConstants.DESCRIPTION;
28+
import static com.bytechef.component.linear.constant.LinearConstants.PRIORITY;
29+
import static com.bytechef.component.linear.constant.LinearConstants.STATE_ID;
30+
import static com.bytechef.component.linear.constant.LinearConstants.TEAM_ID;
31+
import static com.bytechef.component.linear.constant.LinearConstants.TITLE;
32+
import static com.bytechef.component.linear.util.LinearUtils.executeGraphQLQuery;
33+
34+
import com.bytechef.component.definition.ComponentDsl.ModifiableActionDefinition;
35+
import com.bytechef.component.definition.Context;
36+
import com.bytechef.component.definition.OptionsDataSource.ActionOptionsFunction;
37+
import com.bytechef.component.definition.Parameters;
38+
import com.bytechef.component.linear.util.LinearUtils;
39+
import java.util.Map;
40+
41+
/**
42+
* @author Marija Horvat
43+
*/
44+
public class LinearCreateIssueAction {
45+
46+
public static final ModifiableActionDefinition ACTION_DEFINITION = action("createIssue")
47+
.title("Create Issue")
48+
.description("Creates a new issue.")
49+
.properties(
50+
string(TITLE)
51+
.label("Issue Title")
52+
.description("The title of the new issue.")
53+
.required(true),
54+
string(TEAM_ID)
55+
.label("Team ID")
56+
.description("The ID of the team where this issue should be created.")
57+
.options((ActionOptionsFunction<String>) LinearUtils::getTeamOptions)
58+
.required(true),
59+
string(STATE_ID)
60+
.label("Status")
61+
.description("The status of the issue.")
62+
.options((ActionOptionsFunction<String>) LinearUtils::getIssueStateOptions)
63+
.required(true),
64+
integer(PRIORITY)
65+
.label("Priority")
66+
.description("The priority of the issue.")
67+
.options(
68+
option("No priority", 0),
69+
option("Urgent", 1),
70+
option("High", 2),
71+
option("Normal", 3),
72+
option("Low", 4))
73+
.defaultValue(0)
74+
.required(false),
75+
string(ASSIGNEE_ID)
76+
.label("Assignee ID")
77+
.description("The identifier of the user to assign the issue to.")
78+
.options((ActionOptionsFunction<String>) LinearUtils::getAssigneeOptions)
79+
.required(false),
80+
string(DESCRIPTION)
81+
.label("Description")
82+
.description("The detailed description of the issue.")
83+
.required(false))
84+
.output(
85+
outputSchema(
86+
object()
87+
.properties(
88+
bool("success")
89+
.description(
90+
"Whether the operation was successful."),
91+
object("issue")
92+
.description("The issue that was created or updated.")
93+
.properties(
94+
string("id")
95+
.description("The ID of the issue."),
96+
string("title")
97+
.description("The title of the issue.")))))
98+
.perform(LinearCreateIssueAction::perform);
99+
100+
private LinearCreateIssueAction() {
101+
}
102+
103+
public static Object perform(
104+
Parameters inputParameters, Parameters connectionParameters, Context context) {
105+
106+
String query =
107+
"mutation{issueCreate(input: {title: \"%s\", teamId: \"%s\", stateId: \"%s\", %s%s%s}){success issue{id title}}}"
108+
.formatted(
109+
inputParameters.getRequiredString(TITLE),
110+
inputParameters.getRequiredString(TEAM_ID),
111+
inputParameters.getRequiredString(STATE_ID),
112+
inputParameters.getInteger(PRIORITY) != null
113+
? "priority: %d, ".formatted(inputParameters.getInteger(PRIORITY)) : "",
114+
inputParameters.getString(ASSIGNEE_ID) != null
115+
? "assigneeId: \"%s\", ".formatted(inputParameters.getString(ASSIGNEE_ID)) : "",
116+
inputParameters.getString(DESCRIPTION) != null
117+
? "description: \"%s\"".formatted(inputParameters.getString(DESCRIPTION)) : "");
118+
119+
Map<String, Object> body = executeGraphQLQuery(query, context);
120+
121+
if (body.get("data") instanceof Map<?, ?> data && data.get("issueCreate") instanceof Map<?, ?> issueCreate) {
122+
return issueCreate;
123+
}
124+
125+
return null;
126+
}
127+
}

0 commit comments

Comments
 (0)