or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

administration.mdagile-boards.mdclient-setup.mdcomments-attachments.mdfilters-dashboards.mdindex.mdissue-management.mdproject-management.mdremote-links.mdservice-desk.mdsystem-operations.mduser-management.mdworklogs.md

comments-attachments.mddocs/

0

# Comments & Attachments

1

2

Comment management and file attachment operations for issues. This includes adding, updating, and retrieving comments, as well as managing file attachments with support for various file types.

3

4

## Capabilities

5

6

### Comment Management

7

8

Operations for managing issue comments including creation, retrieval, and updates.

9

10

```python { .api }

11

def comments(self, issue: Issue) -> list[Comment]:

12

"""

13

Get all comments for an issue.

14

15

Parameters:

16

- issue: Issue object or issue key

17

18

Returns:

19

List of Comment objects

20

"""

21

22

def comment(self, issue: Issue, comment: str) -> Comment:

23

"""

24

Get a specific comment by ID.

25

26

Parameters:

27

- issue: Issue object or issue key

28

- comment: Comment ID

29

30

Returns:

31

Comment object

32

"""

33

34

def add_comment(

35

self,

36

issue: Issue,

37

body: str,

38

visibility: dict = None,

39

is_internal: bool = False

40

) -> Comment:

41

"""

42

Add a comment to an issue.

43

44

Parameters:

45

- issue: Issue object or issue key

46

- body: Comment text content

47

- visibility: Visibility restrictions (role or group based)

48

- is_internal: Mark comment as internal (Service Desk)

49

50

Returns:

51

Created Comment object

52

"""

53

```

54

55

Usage examples:

56

```python

57

# Get all comments for an issue

58

issue = jira.issue('PROJ-123')

59

comments = jira.comments(issue)

60

for comment in comments:

61

print(f"Author: {comment.author.displayName}")

62

print(f"Created: {comment.created}")

63

print(f"Body: {comment.body}")

64

print("---")

65

66

# Add a simple comment

67

new_comment = jira.add_comment(

68

issue='PROJ-123',

69

body='This is a comment added via the API'

70

)

71

print(f"Added comment: {new_comment.id}")

72

73

# Add comment with visibility restrictions

74

restricted_comment = jira.add_comment(

75

issue='PROJ-123',

76

body='This comment is only visible to developers',

77

visibility={

78

'type': 'group',

79

'value': 'developers'

80

}

81

)

82

83

# Add internal comment (Service Desk)

84

internal_comment = jira.add_comment(

85

issue='SD-456',

86

body='Internal note for agents only',

87

is_internal=True

88

)

89

90

# Get specific comment

91

comment = jira.comment('PROJ-123', new_comment.id)

92

print(f"Comment body: {comment.body}")

93

```

94

95

### Comment Updates

96

97

Comments can be updated through the Comment resource object:

98

99

```python

100

# Update a comment

101

comment = jira.comment('PROJ-123', '12345')

102

comment.update(body='Updated comment text')

103

104

# Update comment with visibility

105

comment.update(

106

body='Updated restricted comment',

107

visibility={

108

'type': 'role',

109

'value': 'Administrators'

110

}

111

)

112

```

113

114

### Attachment Management

115

116

Operations for managing file attachments on issues.

117

118

```python { .api }

119

def attachment(self, id: str) -> Attachment:

120

"""

121

Get attachment by ID.

122

123

Parameters:

124

- id: Attachment ID

125

126

Returns:

127

Attachment object

128

"""

129

130

def attachment_meta(self) -> dict:

131

"""

132

Get attachment metadata including size limits and enabled status.

133

134

Returns:

135

Attachment metadata dictionary

136

"""

137

138

def add_attachment(self, issue: Issue, attachment: str, filename: str = None) -> Attachment:

139

"""

140

Add an attachment to an issue.

141

142

Parameters:

143

- issue: Issue object or issue key

144

- attachment: File path or file-like object

145

- filename: Custom filename (optional)

146

147

Returns:

148

Created Attachment object

149

"""

150

151

def delete_attachment(self, id: str) -> None:

152

"""

153

Delete an attachment.

154

155

Parameters:

156

- id: Attachment ID

157

"""

158

```

159

160

Usage examples:

161

```python

162

# Get attachment metadata

163

meta = jira.attachment_meta()

164

print(f"Attachments enabled: {meta['enabled']}")

165

print(f"Upload limit: {meta['uploadLimit']} bytes")

166

167

# Add file attachment

168

with open('document.pdf', 'rb') as f:

169

attachment = jira.add_attachment(

170

issue='PROJ-123',

171

attachment=f,

172

filename='requirements_document.pdf'

173

)

174

print(f"Uploaded attachment: {attachment.filename}")

175

176

# Add multiple attachments

177

files_to_upload = ['screenshot.png', 'log.txt', 'report.xlsx']

178

for file_path in files_to_upload:

179

with open(file_path, 'rb') as f:

180

attachment = jira.add_attachment('PROJ-123', f)

181

print(f"Uploaded: {attachment.filename}")

182

183

# Get attachment details

184

attachment = jira.attachment(attachment.id)

185

print(f"Filename: {attachment.filename}")

186

print(f"Size: {attachment.size} bytes")

187

print(f"MIME Type: {attachment.mimeType}")

188

print(f"Author: {attachment.author.displayName}")

189

print(f"Created: {attachment.created}")

190

191

# Delete attachment

192

jira.delete_attachment(attachment.id)

193

```

194

195

### Attachment Content Access

196

197

Retrieve attachment file content for processing or download.

198

199

```python

200

# Get attachment content as string (for text files)

201

attachment = jira.attachment('12345')

202

content = attachment.get()

203

print(content)

204

205

# Stream attachment content (for large files)

206

attachment = jira.attachment('12345')

207

with open(f'downloaded_{attachment.filename}', 'wb') as f:

208

for chunk in attachment.iter_content(chunk_size=8192):

209

f.write(chunk)

210

print(f"Downloaded: {attachment.filename}")

211

212

# Process image attachment

213

import io

214

from PIL import Image

215

216

attachment = jira.attachment('image_attachment_id')

217

if attachment.mimeType.startswith('image/'):

218

image_data = io.BytesIO()

219

for chunk in attachment.iter_content():

220

image_data.write(chunk)

221

image_data.seek(0)

222

223

image = Image.open(image_data)

224

print(f"Image size: {image.size}")

225

print(f"Image mode: {image.mode}")

226

```

227

228

### Remote Links

229

230

Manage remote links that connect issues to external resources.

231

232

```python { .api }

233

def remote_links(self, issue: Issue) -> list[dict]:

234

"""Get all remote links for an issue."""

235

236

def remote_link(self, issue: Issue, id: str) -> dict:

237

"""Get a specific remote link by ID."""

238

239

def add_remote_link(

240

self,

241

issue: Issue,

242

destination: str,

243

globalId: str = None,

244

application: dict = None,

245

relationship: str = None

246

) -> dict:

247

"""

248

Add a remote link to an issue.

249

250

Parameters:

251

- issue: Issue object or issue key

252

- destination: URL or destination object

253

- globalId: Global ID for the link

254

- application: Application information dictionary

255

- relationship: Relationship type

256

257

Returns:

258

Created remote link dictionary

259

"""

260

261

def add_simple_link(self, issue: Issue, object: dict) -> dict:

262

"""

263

Add a simple remote link to an issue.

264

265

Parameters:

266

- issue: Issue object or issue key

267

- object: Link object with URL and title

268

269

Returns:

270

Created remote link dictionary

271

"""

272

```

273

274

Usage examples:

275

```python

276

# Get all remote links for an issue

277

links = jira.remote_links('PROJ-123')

278

for link in links:

279

print(f"Title: {link['object']['title']}")

280

print(f"URL: {link['object']['url']}")

281

282

# Add simple remote link

283

simple_link = jira.add_simple_link(

284

issue='PROJ-123',

285

object={

286

'url': 'https://example.com/related-doc',

287

'title': 'Related Documentation'

288

}

289

)

290

291

# Add remote link with application info

292

app_link = jira.add_remote_link(

293

issue='PROJ-123',

294

destination='https://github.com/company/repo/pull/123',

295

application={

296

'type': 'com.atlassian.jira.plugins.github',

297

'name': 'GitHub'

298

},

299

relationship='implements'

300

)

301

302

# Get specific remote link

303

link = jira.remote_link('PROJ-123', simple_link['id'])

304

print(f"Link: {link['object']['title']} -> {link['object']['url']}")

305

```

306

307

## Comment Visibility Options

308

309

Control who can see comments using visibility restrictions:

310

311

### Group-based Visibility

312

```python

313

# Restrict to specific group

314

jira.add_comment(

315

issue='PROJ-123',

316

body='Visible only to developers',

317

visibility={

318

'type': 'group',

319

'value': 'developers'

320

}

321

)

322

```

323

324

### Role-based Visibility

325

```python

326

# Restrict to project role

327

jira.add_comment(

328

issue='PROJ-123',

329

body='Visible only to administrators',

330

visibility={

331

'type': 'role',

332

'value': 'Administrators'

333

}

334

)

335

```

336

337

## Attachment File Types

338

339

The library supports all file types that JIRA allows. Common examples:

340

341

```python

342

# Document files

343

jira.add_attachment('PROJ-123', open('document.pdf', 'rb'))

344

jira.add_attachment('PROJ-123', open('spreadsheet.xlsx', 'rb'))

345

jira.add_attachment('PROJ-123', open('presentation.pptx', 'rb'))

346

347

# Image files

348

jira.add_attachment('PROJ-123', open('screenshot.png', 'rb'))

349

jira.add_attachment('PROJ-123', open('diagram.jpg', 'rb'))

350

351

# Code files

352

jira.add_attachment('PROJ-123', open('script.py', 'rb'))

353

jira.add_attachment('PROJ-123', open('config.json', 'rb'))

354

355

# Archive files

356

jira.add_attachment('PROJ-123', open('logs.zip', 'rb'))

357

jira.add_attachment('PROJ-123', open('backup.tar.gz', 'rb'))

358

```

359

360

## Bulk Operations

361

362

Handle multiple comments or attachments efficiently:

363

364

```python

365

# Add multiple comments

366

comments_to_add = [

367

'First comment',

368

'Second comment with visibility',

369

'Third comment'

370

]

371

372

for i, comment_text in enumerate(comments_to_add):

373

visibility = None

374

if i == 1: # Second comment restricted

375

visibility = {'type': 'group', 'value': 'developers'}

376

377

jira.add_comment('PROJ-123', comment_text, visibility=visibility)

378

379

# Upload multiple related files

380

import os

381

log_dir = '/path/to/logs'

382

for filename in os.listdir(log_dir):

383

if filename.endswith('.log'):

384

with open(os.path.join(log_dir, filename), 'rb') as f:

385

jira.add_attachment('PROJ-123', f, filename=filename)

386

```

387

388

## Error Handling

389

390

Handle common attachment and comment errors:

391

392

```python

393

from jira import JIRAError

394

395

try:

396

# Attempt to add large attachment

397

with open('very_large_file.zip', 'rb') as f:

398

attachment = jira.add_attachment('PROJ-123', f)

399

except JIRAError as e:

400

if e.status_code == 413:

401

print("File too large for upload")

402

elif e.status_code == 403:

403

print("No permission to add attachments")

404

else:

405

print(f"Upload failed: {e.text}")

406

407

try:

408

# Attempt to add comment with invalid visibility

409

jira.add_comment(

410

'PROJ-123',

411

'Test comment',

412

visibility={'type': 'group', 'value': 'nonexistent-group'}

413

)

414

except JIRAError as e:

415

print(f"Comment creation failed: {e.text}")

416

```