Skip to content

Commit 9d9135b

Browse files
d34dmanHenryHengZJ
andauthored
feat: add "Jira" document loader (#4247) (#4248)
* feat: add "Jira" document loader (#4247) * chore: Remove console.log * Update Jira.ts --------- Co-authored-by: Henry Heng <henryheng@flowiseai.com>
1 parent a7b4ae7 commit 9d9135b

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { INodeParams, INodeCredential } from '../src/Interface'
2+
3+
class JiraApi implements INodeCredential {
4+
label: string
5+
name: string
6+
version: number
7+
description: string
8+
inputs: INodeParams[]
9+
10+
constructor() {
11+
this.label = 'Jira API'
12+
this.name = 'jiraApi'
13+
this.version = 1.0
14+
this.description =
15+
'Refer to <a target="_blank" href="https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/">official guide</a> on how to get accessToken on Github'
16+
this.inputs = [
17+
{
18+
label: 'User Name',
19+
name: 'username',
20+
type: 'string',
21+
placeholder: 'username@example.com'
22+
},
23+
{
24+
label: 'Access Token',
25+
name: 'accessToken',
26+
type: 'password',
27+
placeholder: '<JIRA_ACCESS_TOKEN>'
28+
}
29+
]
30+
}
31+
}
32+
33+
module.exports = { credClass: JiraApi }
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { omit } from 'lodash'
2+
import { ICommonObject, IDocument, INode, INodeData, INodeParams } from '../../../src/Interface'
3+
import { TextSplitter } from 'langchain/text_splitter'
4+
import { JiraProjectLoaderParams, JiraProjectLoader } from '@langchain/community/document_loaders/web/jira'
5+
import { getCredentialData, getCredentialParam, handleEscapeCharacters, INodeOutputsValue } from '../../../src'
6+
7+
class Jira_DocumentLoaders implements INode {
8+
label: string
9+
name: string
10+
version: number
11+
description: string
12+
type: string
13+
icon: string
14+
category: string
15+
baseClasses: string[]
16+
credential: INodeParams
17+
inputs: INodeParams[]
18+
outputs: INodeOutputsValue[]
19+
20+
constructor() {
21+
this.label = 'Jira'
22+
this.name = 'jira'
23+
this.version = 1.0
24+
this.type = 'Document'
25+
this.icon = 'jira.svg'
26+
this.category = 'Document Loaders'
27+
this.description = `Load issues from Jira`
28+
this.baseClasses = [this.type]
29+
this.credential = {
30+
label: 'Connect Credential',
31+
name: 'credential',
32+
type: 'credential',
33+
description: 'Jira API Credential',
34+
credentialNames: ['jiraApi']
35+
}
36+
this.inputs = [
37+
{
38+
label: 'Host',
39+
name: 'host',
40+
type: 'string',
41+
placeholder: 'https://jira.example.com'
42+
},
43+
{
44+
label: 'Project Key',
45+
name: 'projectKey',
46+
type: 'string',
47+
default: 'main'
48+
},
49+
{
50+
label: 'Limit per request',
51+
name: 'limitPerRequest',
52+
type: 'number',
53+
step: 1,
54+
optional: true,
55+
placeholder: '100'
56+
},
57+
{
58+
label: 'Created after',
59+
name: 'createdAfter',
60+
type: 'string',
61+
optional: true,
62+
placeholder: '2024-01-01'
63+
},
64+
{
65+
label: 'Text Splitter',
66+
name: 'textSplitter',
67+
type: 'TextSplitter',
68+
optional: true
69+
},
70+
{
71+
label: 'Additional Metadata',
72+
name: 'metadata',
73+
type: 'json',
74+
description: 'Additional metadata to be added to the extracted documents',
75+
optional: true,
76+
additionalParams: true
77+
},
78+
{
79+
label: 'Omit Metadata Keys',
80+
name: 'omitMetadataKeys',
81+
type: 'string',
82+
rows: 4,
83+
description:
84+
'Each document loader comes with a default set of metadata keys that are extracted from the document. You can use this field to omit some of the default metadata keys. The value should be a list of keys, seperated by comma. Use * to omit all metadata keys execept the ones you specify in the Additional Metadata field',
85+
placeholder: 'key1, key2, key3.nestedKey1',
86+
optional: true,
87+
additionalParams: true
88+
}
89+
]
90+
this.outputs = [
91+
{
92+
label: 'Document',
93+
name: 'document',
94+
description: 'Array of document objects containing metadata and pageContent',
95+
baseClasses: [...this.baseClasses, 'json']
96+
},
97+
{
98+
label: 'Text',
99+
name: 'text',
100+
description: 'Concatenated string from pageContent of documents',
101+
baseClasses: ['string', 'json']
102+
}
103+
]
104+
}
105+
106+
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
107+
const host = nodeData.inputs?.host as string
108+
const projectKey = nodeData.inputs?.projectKey as string
109+
const limitPerRequest = nodeData.inputs?.limitPerRequest as string
110+
const createdAfter = nodeData.inputs?.createdAfter as string
111+
const textSplitter = nodeData.inputs?.textSplitter as TextSplitter
112+
const metadata = nodeData.inputs?.metadata
113+
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
114+
const output = nodeData.outputs?.output as string
115+
116+
let omitMetadataKeys: string[] = []
117+
if (_omitMetadataKeys) {
118+
omitMetadataKeys = _omitMetadataKeys.split(',').map((key) => key.trim())
119+
}
120+
121+
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
122+
const username = getCredentialParam('username', credentialData, nodeData)
123+
const accessToken = getCredentialParam('accessToken', credentialData, nodeData)
124+
125+
const jiraOptions: JiraProjectLoaderParams = {
126+
projectKey,
127+
host,
128+
username,
129+
accessToken
130+
}
131+
132+
if (limitPerRequest) {
133+
jiraOptions.limitPerRequest = parseInt(limitPerRequest)
134+
}
135+
136+
if (createdAfter) {
137+
jiraOptions.createdAfter = new Date(createdAfter)
138+
}
139+
140+
const loader = new JiraProjectLoader(jiraOptions)
141+
let docs: IDocument[] = []
142+
143+
if (textSplitter) {
144+
docs = await loader.load()
145+
docs = await textSplitter.splitDocuments(docs)
146+
} else {
147+
docs = await loader.load()
148+
}
149+
150+
if (metadata) {
151+
const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata)
152+
docs = docs.map((doc) => ({
153+
...doc,
154+
metadata:
155+
_omitMetadataKeys === '*'
156+
? {
157+
...parsedMetadata
158+
}
159+
: omit(
160+
{
161+
...doc.metadata,
162+
...parsedMetadata
163+
},
164+
omitMetadataKeys
165+
)
166+
}))
167+
} else {
168+
docs = docs.map((doc) => ({
169+
...doc,
170+
metadata:
171+
_omitMetadataKeys === '*'
172+
? {}
173+
: omit(
174+
{
175+
...doc.metadata
176+
},
177+
omitMetadataKeys
178+
)
179+
}))
180+
}
181+
182+
if (output === 'document') {
183+
return docs
184+
} else {
185+
let finaltext = ''
186+
for (const doc of docs) {
187+
finaltext += `${doc.pageContent}\n`
188+
}
189+
return handleEscapeCharacters(finaltext, false)
190+
}
191+
}
192+
}
193+
194+
module.exports = { nodeClass: Jira_DocumentLoaders }
Lines changed: 2 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)