@useairfoil/producer-linear 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +201 -0
- package/README.md +71 -0
- package/dist/index.d.mts +4027 -0
- package/dist/index.mjs +3 -0
- package/dist/main.d.mts +1 -0
- package/dist/main.mjs +1305 -0
- package/dist/main.mjs.map +1 -0
- package/dist/manifest.d.mts +114 -0
- package/dist/manifest.mjs +161 -0
- package/dist/manifest.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/src-BbzySPR7.mjs +1688 -0
- package/dist/src-BbzySPR7.mjs.map +1 -0
- package/package.json +46 -8
- package/src/client/client.ts +200 -0
- package/src/connector.ts +151 -0
- package/src/create-table.ts +10 -0
- package/src/demo/index.ts +137 -0
- package/src/demo/initiatives.ts +116 -0
- package/src/demo/issues.ts +320 -0
- package/src/demo/projects.ts +215 -0
- package/src/demo/setup.ts +151 -0
- package/src/demo/shared.ts +122 -0
- package/src/demo/teams.ts +173 -0
- package/src/index.ts +83 -0
- package/src/main.ts +28 -0
- package/src/manifest.ts +58 -0
- package/src/resources/comments.ts +32 -0
- package/src/resources/customers.ts +105 -0
- package/src/resources/documents.ts +31 -0
- package/src/resources/initiatives.ts +136 -0
- package/src/resources/issue-history.ts +119 -0
- package/src/resources/issues.ts +131 -0
- package/src/resources/pages.ts +264 -0
- package/src/resources/projects.ts +161 -0
- package/src/resources/releases.ts +114 -0
- package/src/resources/teams.ts +140 -0
- package/src/sandbox.ts +35 -0
- package/src/schemas/comments.ts +52 -0
- package/src/schemas/customers.ts +96 -0
- package/src/schemas/documents.ts +40 -0
- package/src/schemas/initiatives.ts +155 -0
- package/src/schemas/issues.ts +262 -0
- package/src/schemas/projects.ts +210 -0
- package/src/schemas/releases.ts +132 -0
- package/src/schemas/shared.ts +68 -0
- package/src/schemas/teams.ts +177 -0
- package/src/start.ts +47 -0
- package/src/tables.ts +83 -0
- package/src/webhook/route.ts +50 -0
- package/src/webhook/signature.ts +28 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
|
|
3
|
+
import type { LinearClientService } from "../client/client";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ProjectLabelObjectSchema,
|
|
7
|
+
ProjectLabelSchema,
|
|
8
|
+
ProjectMilestoneObjectSchema,
|
|
9
|
+
ProjectMilestoneSchema,
|
|
10
|
+
ProjectObjectSchema,
|
|
11
|
+
ProjectRelationObjectSchema,
|
|
12
|
+
ProjectRelationSchema,
|
|
13
|
+
ProjectSchema,
|
|
14
|
+
ProjectStatusObjectSchema,
|
|
15
|
+
ProjectStatusSchema,
|
|
16
|
+
ProjectUpdateObjectSchema,
|
|
17
|
+
ProjectUpdateSchema,
|
|
18
|
+
} from "../schemas/projects";
|
|
19
|
+
import {
|
|
20
|
+
backfillList,
|
|
21
|
+
changesList,
|
|
22
|
+
checkList,
|
|
23
|
+
listSources,
|
|
24
|
+
listSpec,
|
|
25
|
+
rowsOf,
|
|
26
|
+
webhookList,
|
|
27
|
+
} from "./pages";
|
|
28
|
+
|
|
29
|
+
const projectSpec = listSpec({
|
|
30
|
+
connection: "projects",
|
|
31
|
+
single: "project",
|
|
32
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
33
|
+
name description content url icon color status_id: status { id } priority
|
|
34
|
+
priority_label: priorityLabel health health_updated_at: healthUpdatedAt progress scope
|
|
35
|
+
lead_id: lead { id } creator_id: creator { id }
|
|
36
|
+
converted_from_issue_id: convertedFromIssue { id } start_date: startDate
|
|
37
|
+
target_date: targetDate started_at: startedAt completed_at: completedAt
|
|
38
|
+
canceled_at: canceledAt auto_archived_at: autoArchivedAt label_ids: labelIds
|
|
39
|
+
team_ids: teams(first: 50) { nodes { id } } member_ids: members(first: 100) { nodes { id } }
|
|
40
|
+
trashed`,
|
|
41
|
+
item: ProjectObjectSchema,
|
|
42
|
+
// Each project reads up to 150 teams and members, so pages stay under the cost limit.
|
|
43
|
+
first: 50,
|
|
44
|
+
updatedAt: (project) => project.updated_at,
|
|
45
|
+
rows: rowsOf((project) => ({ ...project, version: project.updated_at, _deleted: false })),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const makeProjects = (client: LinearClientService) =>
|
|
49
|
+
Resource.entity({
|
|
50
|
+
name: "projects",
|
|
51
|
+
rowSchema: ProjectSchema,
|
|
52
|
+
version: "version",
|
|
53
|
+
|
|
54
|
+
check: checkList(client, projectSpec),
|
|
55
|
+
backfill: backfillList(client, projectSpec),
|
|
56
|
+
changes: changesList(client, projectSpec),
|
|
57
|
+
webhook: webhookList(client, projectSpec),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const projectMilestoneSpec = listSpec({
|
|
61
|
+
connection: "projectMilestones",
|
|
62
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
63
|
+
name description project_id: project { id } target_date: targetDate status progress
|
|
64
|
+
sort_order: sortOrder`,
|
|
65
|
+
item: ProjectMilestoneObjectSchema,
|
|
66
|
+
updatedAt: (milestone) => milestone.updated_at,
|
|
67
|
+
rows: rowsOf((milestone) => ({ ...milestone, version: milestone.updated_at })),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const makeProjectMilestones = (client: LinearClientService) =>
|
|
71
|
+
Resource.entity({
|
|
72
|
+
name: "project_milestones",
|
|
73
|
+
rowSchema: ProjectMilestoneSchema,
|
|
74
|
+
version: "version",
|
|
75
|
+
|
|
76
|
+
check: checkList(client, projectMilestoneSpec),
|
|
77
|
+
backfill: backfillList(client, projectMilestoneSpec),
|
|
78
|
+
changes: changesList(client, projectMilestoneSpec),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const projectUpdateSpec = listSpec({
|
|
82
|
+
connection: "projectUpdates",
|
|
83
|
+
single: "projectUpdate",
|
|
84
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
85
|
+
project_id: project { id } user_id: user { id } body health url edited_at: editedAt
|
|
86
|
+
comment_count: commentCount`,
|
|
87
|
+
item: ProjectUpdateObjectSchema,
|
|
88
|
+
updatedAt: (update) => update.updated_at,
|
|
89
|
+
rows: rowsOf((update) => ({ ...update, version: update.updated_at, _deleted: false })),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const makeProjectUpdates = (client: LinearClientService) =>
|
|
93
|
+
Resource.entity({
|
|
94
|
+
name: "project_updates",
|
|
95
|
+
rowSchema: ProjectUpdateSchema,
|
|
96
|
+
version: "version",
|
|
97
|
+
|
|
98
|
+
check: checkList(client, projectUpdateSpec),
|
|
99
|
+
backfill: backfillList(client, projectUpdateSpec),
|
|
100
|
+
changes: changesList(client, projectUpdateSpec),
|
|
101
|
+
webhook: webhookList(client, projectUpdateSpec),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const projectStatusSpec = listSpec({
|
|
105
|
+
connection: "projectStatuses",
|
|
106
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name type color description position`,
|
|
107
|
+
item: ProjectStatusObjectSchema,
|
|
108
|
+
updatedAt: (status) => status.updated_at,
|
|
109
|
+
rows: rowsOf((status) => ({ ...status, version: status.updated_at })),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export const makeProjectStatuses = (client: LinearClientService) =>
|
|
113
|
+
Resource.entity({
|
|
114
|
+
name: "project_statuses",
|
|
115
|
+
rowSchema: ProjectStatusSchema,
|
|
116
|
+
version: "version",
|
|
117
|
+
...listSources(client, projectStatusSpec),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const projectLabelSpec = listSpec({
|
|
121
|
+
connection: "projectLabels",
|
|
122
|
+
single: "projectLabel",
|
|
123
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
124
|
+
name description color is_group: isGroup parent_id: parent { id }
|
|
125
|
+
creator_id: creator { id } retired_at: retiredAt`,
|
|
126
|
+
item: ProjectLabelObjectSchema,
|
|
127
|
+
updatedAt: (label) => label.updated_at,
|
|
128
|
+
rows: rowsOf((label) => ({ ...label, version: label.updated_at, _deleted: false })),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
export const makeProjectLabels = (client: LinearClientService) =>
|
|
132
|
+
Resource.entity({
|
|
133
|
+
name: "project_labels",
|
|
134
|
+
rowSchema: ProjectLabelSchema,
|
|
135
|
+
version: "version",
|
|
136
|
+
...listSources(client, projectLabelSpec),
|
|
137
|
+
webhook: webhookList(client, projectLabelSpec),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const projectRelationSpec = listSpec({
|
|
141
|
+
connection: "projectRelations",
|
|
142
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
143
|
+
type project_id: project { id } project_milestone_id: projectMilestone { id }
|
|
144
|
+
anchor_type: anchorType related_project_id: relatedProject { id }
|
|
145
|
+
related_project_milestone_id: relatedProjectMilestone { id }
|
|
146
|
+
related_anchor_type: relatedAnchorType user_id: user { id }`,
|
|
147
|
+
item: ProjectRelationObjectSchema,
|
|
148
|
+
updatedAt: (relation) => relation.updated_at,
|
|
149
|
+
rows: rowsOf((relation) => ({ ...relation, version: relation.updated_at })),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export const makeProjectRelations = (client: LinearClientService) =>
|
|
153
|
+
Resource.entity({
|
|
154
|
+
name: "project_relations",
|
|
155
|
+
rowSchema: ProjectRelationSchema,
|
|
156
|
+
version: "version",
|
|
157
|
+
|
|
158
|
+
check: checkList(client, projectRelationSpec),
|
|
159
|
+
backfill: backfillList(client, projectRelationSpec),
|
|
160
|
+
changes: changesList(client, projectRelationSpec),
|
|
161
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
|
|
3
|
+
import type { LinearClientService } from "../client/client";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
IssueToReleaseObjectSchema,
|
|
7
|
+
IssueToReleaseSchema,
|
|
8
|
+
ReleaseNoteObjectSchema,
|
|
9
|
+
ReleaseNoteSchema,
|
|
10
|
+
ReleaseObjectSchema,
|
|
11
|
+
ReleasePipelineObjectSchema,
|
|
12
|
+
ReleasePipelineSchema,
|
|
13
|
+
ReleaseSchema,
|
|
14
|
+
ReleaseStageObjectSchema,
|
|
15
|
+
ReleaseStageSchema,
|
|
16
|
+
} from "../schemas/releases";
|
|
17
|
+
import { backfillList, changesList, checkList, listSources, listSpec, rowsOf } from "./pages";
|
|
18
|
+
|
|
19
|
+
const releaseSpec = listSpec({
|
|
20
|
+
connection: "releases",
|
|
21
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
22
|
+
name description release_version: version url commit_sha: commitSha
|
|
23
|
+
pipeline_id: pipeline { id } stage_id: stage { id } creator_id: creator { id }
|
|
24
|
+
start_date: startDate target_date: targetDate started_at: startedAt
|
|
25
|
+
completed_at: completedAt canceled_at: canceledAt trashed`,
|
|
26
|
+
item: ReleaseObjectSchema,
|
|
27
|
+
updatedAt: (release) => release.updated_at,
|
|
28
|
+
rows: rowsOf((release) => ({ ...release, version: release.updated_at })),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const makeReleases = (client: LinearClientService) =>
|
|
32
|
+
Resource.entity({
|
|
33
|
+
name: "releases",
|
|
34
|
+
rowSchema: ReleaseSchema,
|
|
35
|
+
version: "version",
|
|
36
|
+
|
|
37
|
+
check: checkList(client, releaseSpec),
|
|
38
|
+
backfill: backfillList(client, releaseSpec),
|
|
39
|
+
changes: changesList(client, releaseSpec),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const releaseNoteSpec = listSpec({
|
|
43
|
+
connection: "releaseNotes",
|
|
44
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
45
|
+
title content: documentContent { content } url pipeline_id: pipeline { id }
|
|
46
|
+
release_ids: releases { id }`,
|
|
47
|
+
item: ReleaseNoteObjectSchema,
|
|
48
|
+
updatedAt: (note) => note.updated_at,
|
|
49
|
+
rows: rowsOf((note) => ({ ...note, version: note.updated_at })),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const makeReleaseNotes = (client: LinearClientService) =>
|
|
53
|
+
Resource.entity({
|
|
54
|
+
name: "release_notes",
|
|
55
|
+
rowSchema: ReleaseNoteSchema,
|
|
56
|
+
version: "version",
|
|
57
|
+
|
|
58
|
+
check: checkList(client, releaseNoteSpec),
|
|
59
|
+
backfill: backfillList(client, releaseNoteSpec),
|
|
60
|
+
changes: changesList(client, releaseNoteSpec),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const releasePipelineSpec = listSpec({
|
|
64
|
+
connection: "releasePipelines",
|
|
65
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
66
|
+
name type is_production: isProduction url include_path_patterns: includePathPatterns
|
|
67
|
+
approximate_release_count: approximateReleaseCount trashed`,
|
|
68
|
+
item: ReleasePipelineObjectSchema,
|
|
69
|
+
updatedAt: (pipeline) => pipeline.updated_at,
|
|
70
|
+
rows: rowsOf((pipeline) => ({ ...pipeline, version: pipeline.updated_at })),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const makeReleasePipelines = (client: LinearClientService) =>
|
|
74
|
+
Resource.entity({
|
|
75
|
+
name: "release_pipelines",
|
|
76
|
+
rowSchema: ReleasePipelineSchema,
|
|
77
|
+
version: "version",
|
|
78
|
+
...listSources(client, releasePipelineSpec),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const releaseStageSpec = listSpec({
|
|
82
|
+
connection: "releaseStages",
|
|
83
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name type color position frozen pipeline_id: pipeline { id }`,
|
|
84
|
+
item: ReleaseStageObjectSchema,
|
|
85
|
+
updatedAt: (stage) => stage.updated_at,
|
|
86
|
+
rows: rowsOf((stage) => ({ ...stage, version: stage.updated_at })),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const makeReleaseStages = (client: LinearClientService) =>
|
|
90
|
+
Resource.entity({
|
|
91
|
+
name: "release_stages",
|
|
92
|
+
rowSchema: ReleaseStageSchema,
|
|
93
|
+
version: "version",
|
|
94
|
+
...listSources(client, releaseStageSpec),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const issueToReleaseSpec = listSpec({
|
|
98
|
+
connection: "issueToReleases",
|
|
99
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt issue_id: issue { id } release_id: release { id }`,
|
|
100
|
+
item: IssueToReleaseObjectSchema,
|
|
101
|
+
updatedAt: (link) => link.updated_at,
|
|
102
|
+
rows: rowsOf((link) => ({ ...link, version: link.updated_at })),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const makeIssueToReleases = (client: LinearClientService) =>
|
|
106
|
+
Resource.entity({
|
|
107
|
+
name: "issue_to_releases",
|
|
108
|
+
rowSchema: IssueToReleaseSchema,
|
|
109
|
+
version: "version",
|
|
110
|
+
|
|
111
|
+
check: checkList(client, issueToReleaseSpec),
|
|
112
|
+
backfill: backfillList(client, issueToReleaseSpec),
|
|
113
|
+
changes: changesList(client, issueToReleaseSpec),
|
|
114
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
|
|
3
|
+
import type { LinearClientService } from "../client/client";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CycleObjectSchema,
|
|
7
|
+
CycleSchema,
|
|
8
|
+
ExternalUserObjectSchema,
|
|
9
|
+
ExternalUserSchema,
|
|
10
|
+
TeamMembershipObjectSchema,
|
|
11
|
+
TeamMembershipSchema,
|
|
12
|
+
TeamObjectSchema,
|
|
13
|
+
TeamSchema,
|
|
14
|
+
UserObjectSchema,
|
|
15
|
+
UserSchema,
|
|
16
|
+
WorkflowStateObjectSchema,
|
|
17
|
+
WorkflowStateSchema,
|
|
18
|
+
} from "../schemas/teams";
|
|
19
|
+
import {
|
|
20
|
+
backfillList,
|
|
21
|
+
changesList,
|
|
22
|
+
checkList,
|
|
23
|
+
listSources,
|
|
24
|
+
listSpec,
|
|
25
|
+
rowsOf,
|
|
26
|
+
webhookList,
|
|
27
|
+
} from "./pages";
|
|
28
|
+
|
|
29
|
+
const teamSpec = listSpec({
|
|
30
|
+
connection: "teams",
|
|
31
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
32
|
+
name key display_name: displayName description visibility parent_id: parent { id }
|
|
33
|
+
timezone cycles_enabled: cyclesEnabled cycle_duration: cycleDuration
|
|
34
|
+
cycle_cooldown_time: cycleCooldownTime cycle_start_day: cycleStartDay
|
|
35
|
+
triage_enabled: triageEnabled issue_estimation_type: issueEstimationType
|
|
36
|
+
default_issue_estimate: defaultIssueEstimate auto_archive_period: autoArchivePeriod
|
|
37
|
+
auto_close_period: autoClosePeriod retired_at: retiredAt`,
|
|
38
|
+
item: TeamObjectSchema,
|
|
39
|
+
updatedAt: (team) => team.updated_at,
|
|
40
|
+
rows: rowsOf((team) => ({ ...team, version: team.updated_at })),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const makeTeams = (client: LinearClientService) =>
|
|
44
|
+
Resource.entity({
|
|
45
|
+
name: "teams",
|
|
46
|
+
rowSchema: TeamSchema,
|
|
47
|
+
version: "version",
|
|
48
|
+
...listSources(client, teamSpec),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const teamMembershipSpec = listSpec({
|
|
52
|
+
connection: "teamMemberships",
|
|
53
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt team_id: team { id } user_id: user { id } owner`,
|
|
54
|
+
item: TeamMembershipObjectSchema,
|
|
55
|
+
updatedAt: (membership) => membership.updated_at,
|
|
56
|
+
rows: rowsOf((membership) => ({ ...membership, version: membership.updated_at })),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const makeTeamMemberships = (client: LinearClientService) =>
|
|
60
|
+
Resource.entity({
|
|
61
|
+
name: "team_memberships",
|
|
62
|
+
rowSchema: TeamMembershipSchema,
|
|
63
|
+
version: "version",
|
|
64
|
+
...listSources(client, teamMembershipSpec),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const workflowStateSpec = listSpec({
|
|
68
|
+
connection: "workflowStates",
|
|
69
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name type color description position team_id: team { id }`,
|
|
70
|
+
item: WorkflowStateObjectSchema,
|
|
71
|
+
updatedAt: (state) => state.updated_at,
|
|
72
|
+
rows: rowsOf((state) => ({ ...state, version: state.updated_at })),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const makeWorkflowStates = (client: LinearClientService) =>
|
|
76
|
+
Resource.entity({
|
|
77
|
+
name: "workflow_states",
|
|
78
|
+
rowSchema: WorkflowStateSchema,
|
|
79
|
+
version: "version",
|
|
80
|
+
...listSources(client, workflowStateSpec),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const userSpec = listSpec({
|
|
84
|
+
connection: "users",
|
|
85
|
+
single: "user",
|
|
86
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
87
|
+
name display_name: displayName email avatar_url: avatarUrl title timezone
|
|
88
|
+
active admin owner guest app last_seen: lastSeen url`,
|
|
89
|
+
item: UserObjectSchema,
|
|
90
|
+
updatedAt: (user) => user.updated_at,
|
|
91
|
+
rows: rowsOf((user) => ({ ...user, version: user.updated_at, _deleted: false })),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const makeUsers = (client: LinearClientService) =>
|
|
95
|
+
Resource.entity({
|
|
96
|
+
name: "users",
|
|
97
|
+
rowSchema: UserSchema,
|
|
98
|
+
version: "version",
|
|
99
|
+
...listSources(client, userSpec),
|
|
100
|
+
webhook: webhookList(client, userSpec),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const externalUserSpec = listSpec({
|
|
104
|
+
connection: "externalUsers",
|
|
105
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name display_name: displayName email avatar_url: avatarUrl last_seen: lastSeen`,
|
|
106
|
+
item: ExternalUserObjectSchema,
|
|
107
|
+
updatedAt: (user) => user.updated_at,
|
|
108
|
+
rows: rowsOf((user) => ({ ...user, version: user.updated_at })),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export const makeExternalUsers = (client: LinearClientService) =>
|
|
112
|
+
Resource.entity({
|
|
113
|
+
name: "external_users",
|
|
114
|
+
rowSchema: ExternalUserSchema,
|
|
115
|
+
version: "version",
|
|
116
|
+
...listSources(client, externalUserSpec),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const cycleSpec = listSpec({
|
|
120
|
+
connection: "cycles",
|
|
121
|
+
single: "cycle",
|
|
122
|
+
fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
|
|
123
|
+
number name description team_id: team { id } starts_at: startsAt ends_at: endsAt
|
|
124
|
+
completed_at: completedAt auto_archived_at: autoArchivedAt progress`,
|
|
125
|
+
item: CycleObjectSchema,
|
|
126
|
+
updatedAt: (cycle) => cycle.updated_at,
|
|
127
|
+
rows: rowsOf((cycle) => ({ ...cycle, version: cycle.updated_at, _deleted: false })),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
export const makeCycles = (client: LinearClientService) =>
|
|
131
|
+
Resource.entity({
|
|
132
|
+
name: "cycles",
|
|
133
|
+
rowSchema: CycleSchema,
|
|
134
|
+
version: "version",
|
|
135
|
+
|
|
136
|
+
check: checkList(client, cycleSpec),
|
|
137
|
+
backfill: backfillList(client, cycleSpec),
|
|
138
|
+
changes: changesList(client, cycleSpec),
|
|
139
|
+
webhook: webhookList(client, cycleSpec),
|
|
140
|
+
});
|
package/src/sandbox.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConnectorApp,
|
|
3
|
+
Ingestor,
|
|
4
|
+
RuntimeConfig,
|
|
5
|
+
StateStore,
|
|
6
|
+
Telemetry,
|
|
7
|
+
} from "@useairfoil/connector-kit";
|
|
8
|
+
import { Effect, Layer, Logger } from "effect";
|
|
9
|
+
import { Command } from "effect/cli";
|
|
10
|
+
|
|
11
|
+
import { LinearConnector } from "./index";
|
|
12
|
+
|
|
13
|
+
const ConnectorLayer = LinearConnector.layerConfig(LinearConnector.LinearConfigDef.config);
|
|
14
|
+
|
|
15
|
+
const TelemetryLayer = Layer.mergeAll(Telemetry.layerOtlp(), Telemetry.layerMetricsConsoleDump());
|
|
16
|
+
|
|
17
|
+
export const sandboxCommand = Command.make("sandbox", {}, () =>
|
|
18
|
+
Effect.gen(function* () {
|
|
19
|
+
const port = yield* RuntimeConfig.httpPort;
|
|
20
|
+
const entrypoint = yield* LinearConnector.LinearConnector;
|
|
21
|
+
|
|
22
|
+
return yield* ConnectorApp.start(entrypoint, { port });
|
|
23
|
+
}).pipe(
|
|
24
|
+
Effect.annotateLogs({ component: "linear" }),
|
|
25
|
+
Effect.provide(
|
|
26
|
+
Layer.mergeAll(
|
|
27
|
+
StateStore.layerMemory,
|
|
28
|
+
Ingestor.layerConsole,
|
|
29
|
+
ConnectorLayer,
|
|
30
|
+
Logger.layer([Logger.consolePretty()]),
|
|
31
|
+
TelemetryLayer,
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
).pipe(Command.withDescription("Run the connector locally and log ingested data"));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Iceberg } from "@useairfoil/connector-kit";
|
|
2
|
+
import { Schema, Struct } from "effect";
|
|
3
|
+
|
|
4
|
+
import { OptionalRef, OptionalString, OptionalTimestamp, common, deleted } from "./shared";
|
|
5
|
+
|
|
6
|
+
export const CommentSchema = Schema.Struct({
|
|
7
|
+
...common("Comment"),
|
|
8
|
+
_deleted: deleted("comment"),
|
|
9
|
+
body: Schema.String.pipe(Schema.annotateKey({ description: "Comment text in markdown." })),
|
|
10
|
+
user_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who wrote the comment." })),
|
|
11
|
+
external_user_id: OptionalRef.pipe(
|
|
12
|
+
Schema.annotateKey({ description: "External user who wrote the comment." }),
|
|
13
|
+
),
|
|
14
|
+
issue_id: OptionalString.pipe(Schema.annotateKey({ description: "Issue the comment is on." })),
|
|
15
|
+
project_id: OptionalString.pipe(
|
|
16
|
+
Schema.annotateKey({ description: "Project the comment is on." }),
|
|
17
|
+
),
|
|
18
|
+
project_update_id: OptionalString.pipe(
|
|
19
|
+
Schema.annotateKey({ description: "Project update the comment is on." }),
|
|
20
|
+
),
|
|
21
|
+
initiative_id: OptionalString.pipe(
|
|
22
|
+
Schema.annotateKey({ description: "Initiative the comment is on." }),
|
|
23
|
+
),
|
|
24
|
+
initiative_update_id: OptionalString.pipe(
|
|
25
|
+
Schema.annotateKey({ description: "Initiative update the comment is on." }),
|
|
26
|
+
),
|
|
27
|
+
document_content_id: OptionalString.pipe(
|
|
28
|
+
Schema.annotateKey({ description: "Document content the comment is on." }),
|
|
29
|
+
),
|
|
30
|
+
parent_id: OptionalString.pipe(
|
|
31
|
+
Schema.annotateKey({ description: "Comment this one replies to." }),
|
|
32
|
+
),
|
|
33
|
+
quoted_text: OptionalString.pipe(Schema.annotateKey({ description: "Text the comment quotes." })),
|
|
34
|
+
edited_at: OptionalTimestamp.pipe(
|
|
35
|
+
Schema.annotateKey({ description: "Time the comment was last edited." }),
|
|
36
|
+
),
|
|
37
|
+
resolved_at: OptionalTimestamp.pipe(
|
|
38
|
+
Schema.annotateKey({ description: "Time the thread was resolved." }),
|
|
39
|
+
),
|
|
40
|
+
resolving_user_id: OptionalRef.pipe(
|
|
41
|
+
Schema.annotateKey({ description: "User who resolved the thread." }),
|
|
42
|
+
),
|
|
43
|
+
url: Schema.String.pipe(Schema.annotateKey({ description: "Comment URL." })),
|
|
44
|
+
})
|
|
45
|
+
.annotate({ description: "Linear comments on issues, projects, initiatives, and updates." })
|
|
46
|
+
.pipe(Iceberg.identifier("id"));
|
|
47
|
+
|
|
48
|
+
export type Comment = Schema.Schema.Type<typeof CommentSchema>;
|
|
49
|
+
|
|
50
|
+
export const CommentObjectSchema = CommentSchema.mapFields((fields) =>
|
|
51
|
+
Struct.omit(fields, ["version", "_deleted"]),
|
|
52
|
+
);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Iceberg } from "@useairfoil/connector-kit";
|
|
2
|
+
import { Schema, Struct } from "effect";
|
|
3
|
+
|
|
4
|
+
import { Long, OptionalRef, OptionalString, Ref, common, deleted } from "./shared";
|
|
5
|
+
|
|
6
|
+
export const CustomerSchema = Schema.Struct({
|
|
7
|
+
...common("Customer"),
|
|
8
|
+
_deleted: deleted("customer"),
|
|
9
|
+
name: Schema.String.pipe(Schema.annotateKey({ description: "Customer name." })),
|
|
10
|
+
domains: Schema.Array(Schema.String).pipe(
|
|
11
|
+
Schema.annotateKey({ description: "Email domains of the customer." }),
|
|
12
|
+
),
|
|
13
|
+
external_ids: Schema.Array(Schema.String).pipe(
|
|
14
|
+
Schema.annotateKey({ description: "IDs in other systems." }),
|
|
15
|
+
),
|
|
16
|
+
logo_url: OptionalString.pipe(Schema.annotateKey({ description: "Logo URL." })),
|
|
17
|
+
owner_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who owns the customer." })),
|
|
18
|
+
status_id: Ref.pipe(Schema.annotateKey({ description: "Customer status." })),
|
|
19
|
+
tier_id: OptionalRef.pipe(Schema.annotateKey({ description: "Customer tier." })),
|
|
20
|
+
revenue: Schema.NullOr(Long).pipe(Schema.annotateKey({ description: "Annual revenue." })),
|
|
21
|
+
size: Schema.NullOr(Schema.Finite).pipe(Schema.annotateKey({ description: "Company size." })),
|
|
22
|
+
approximate_need_count: Schema.Finite.pipe(
|
|
23
|
+
Schema.annotateKey({ description: "About how many requests it has." }),
|
|
24
|
+
),
|
|
25
|
+
url: Schema.String.pipe(Schema.annotateKey({ description: "Customer URL." })),
|
|
26
|
+
})
|
|
27
|
+
.annotate({ description: "Linear customers." })
|
|
28
|
+
.pipe(Iceberg.identifier("id"));
|
|
29
|
+
|
|
30
|
+
export type Customer = Schema.Schema.Type<typeof CustomerSchema>;
|
|
31
|
+
|
|
32
|
+
export const CustomerObjectSchema = CustomerSchema.mapFields((fields) =>
|
|
33
|
+
Struct.omit(fields, ["version", "_deleted"]),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const CustomerNeedSchema = Schema.Struct({
|
|
37
|
+
...common("Request"),
|
|
38
|
+
_deleted: deleted("request"),
|
|
39
|
+
customer_id: OptionalRef.pipe(Schema.annotateKey({ description: "Customer." })),
|
|
40
|
+
issue_id: OptionalRef.pipe(
|
|
41
|
+
Schema.annotateKey({ description: "Issue the request is linked to." }),
|
|
42
|
+
),
|
|
43
|
+
project_id: OptionalRef.pipe(
|
|
44
|
+
Schema.annotateKey({ description: "Project the request is linked to." }),
|
|
45
|
+
),
|
|
46
|
+
original_issue_id: OptionalRef.pipe(
|
|
47
|
+
Schema.annotateKey({ description: "Issue the request was first linked to." }),
|
|
48
|
+
),
|
|
49
|
+
comment_id: OptionalRef.pipe(
|
|
50
|
+
Schema.annotateKey({ description: "Comment the request came from." }),
|
|
51
|
+
),
|
|
52
|
+
attachment_id: OptionalRef.pipe(
|
|
53
|
+
Schema.annotateKey({ description: "Attachment the request came from." }),
|
|
54
|
+
),
|
|
55
|
+
creator_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who added the request." })),
|
|
56
|
+
body: OptionalString.pipe(Schema.annotateKey({ description: "Request text in markdown." })),
|
|
57
|
+
priority: Schema.Finite.pipe(Schema.annotateKey({ description: "0 normal, 1 important." })),
|
|
58
|
+
url: OptionalString.pipe(Schema.annotateKey({ description: "Source URL." })),
|
|
59
|
+
})
|
|
60
|
+
.annotate({ description: "Linear customer requests, linked to issues or projects." })
|
|
61
|
+
.pipe(Iceberg.identifier("id"));
|
|
62
|
+
|
|
63
|
+
export type CustomerNeed = Schema.Schema.Type<typeof CustomerNeedSchema>;
|
|
64
|
+
|
|
65
|
+
export const CustomerNeedObjectSchema = CustomerNeedSchema.mapFields((fields) =>
|
|
66
|
+
Struct.omit(fields, ["version", "_deleted"]),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const levelFields = (object: string) => ({
|
|
70
|
+
...common(object),
|
|
71
|
+
name: Schema.String.pipe(Schema.annotateKey({ description: "Name." })),
|
|
72
|
+
display_name: Schema.String.pipe(Schema.annotateKey({ description: "Name shown in Linear." })),
|
|
73
|
+
description: OptionalString.pipe(Schema.annotateKey({ description: "Description." })),
|
|
74
|
+
color: Schema.String.pipe(Schema.annotateKey({ description: "Color, as hex." })),
|
|
75
|
+
position: Schema.Finite.pipe(Schema.annotateKey({ description: "Order in the list." })),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export const CustomerStatusSchema = Schema.Struct(levelFields("Status"))
|
|
79
|
+
.annotate({ description: "Linear customer statuses. Names the IDs in `customers.status_id`." })
|
|
80
|
+
.pipe(Iceberg.identifier("id"));
|
|
81
|
+
|
|
82
|
+
export type CustomerStatus = Schema.Schema.Type<typeof CustomerStatusSchema>;
|
|
83
|
+
|
|
84
|
+
export const CustomerStatusObjectSchema = CustomerStatusSchema.mapFields((fields) =>
|
|
85
|
+
Struct.omit(fields, ["version"]),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
export const CustomerTierSchema = Schema.Struct(levelFields("Tier"))
|
|
89
|
+
.annotate({ description: "Linear customer tiers. Names the IDs in `customers.tier_id`." })
|
|
90
|
+
.pipe(Iceberg.identifier("id"));
|
|
91
|
+
|
|
92
|
+
export type CustomerTier = Schema.Schema.Type<typeof CustomerTierSchema>;
|
|
93
|
+
|
|
94
|
+
export const CustomerTierObjectSchema = CustomerTierSchema.mapFields((fields) =>
|
|
95
|
+
Struct.omit(fields, ["version"]),
|
|
96
|
+
);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Iceberg } from "@useairfoil/connector-kit";
|
|
2
|
+
import { Schema, Struct } from "effect";
|
|
3
|
+
|
|
4
|
+
import { OptionalRef, OptionalString, OptionalTimestamp, common, deleted } from "./shared";
|
|
5
|
+
|
|
6
|
+
export const DocumentSchema = Schema.Struct({
|
|
7
|
+
...common("Document"),
|
|
8
|
+
_deleted: deleted("document"),
|
|
9
|
+
title: Schema.String.pipe(Schema.annotateKey({ description: "Title." })),
|
|
10
|
+
content: OptionalString.pipe(Schema.annotateKey({ description: "Content in markdown." })),
|
|
11
|
+
url: Schema.String.pipe(Schema.annotateKey({ description: "Document URL." })),
|
|
12
|
+
icon: OptionalString.pipe(Schema.annotateKey({ description: "Icon." })),
|
|
13
|
+
color: OptionalString.pipe(Schema.annotateKey({ description: "Icon color." })),
|
|
14
|
+
creator_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who made the document." })),
|
|
15
|
+
updated_by_id: OptionalRef.pipe(
|
|
16
|
+
Schema.annotateKey({ description: "User who last changed the document." }),
|
|
17
|
+
),
|
|
18
|
+
project_id: OptionalRef.pipe(Schema.annotateKey({ description: "Project the document is in." })),
|
|
19
|
+
initiative_id: OptionalRef.pipe(
|
|
20
|
+
Schema.annotateKey({ description: "Initiative the document is in." }),
|
|
21
|
+
),
|
|
22
|
+
issue_id: OptionalRef.pipe(Schema.annotateKey({ description: "Issue the document is in." })),
|
|
23
|
+
cycle_id: OptionalRef.pipe(Schema.annotateKey({ description: "Cycle the document is in." })),
|
|
24
|
+
team_id: OptionalRef.pipe(Schema.annotateKey({ description: "Team the document is in." })),
|
|
25
|
+
parent_document_id: OptionalRef.pipe(Schema.annotateKey({ description: "Parent document." })),
|
|
26
|
+
hidden_at: OptionalTimestamp.pipe(
|
|
27
|
+
Schema.annotateKey({ description: "Time the document was hidden." }),
|
|
28
|
+
),
|
|
29
|
+
trashed: Schema.NullOr(Schema.Boolean).pipe(
|
|
30
|
+
Schema.annotateKey({ description: "Whether the document is in the trash." }),
|
|
31
|
+
),
|
|
32
|
+
})
|
|
33
|
+
.annotate({ description: "Linear documents, including archived and trashed ones." })
|
|
34
|
+
.pipe(Iceberg.identifier("id"));
|
|
35
|
+
|
|
36
|
+
export type Document = Schema.Schema.Type<typeof DocumentSchema>;
|
|
37
|
+
|
|
38
|
+
export const DocumentObjectSchema = DocumentSchema.mapFields((fields) =>
|
|
39
|
+
Struct.omit(fields, ["version", "_deleted"]),
|
|
40
|
+
);
|