1 module pastemyst.paste; 2 3 import std.typecons; 4 import std.uri; 5 import vibe.d; 6 import pastemyst.info; 7 import pastemyst.expires; 8 9 /++ 10 + holds information about a single paste edit 11 +/ 12 public struct Edit 13 { 14 /++ 15 + unique id of the edit 16 +/ 17 @name("_id") 18 public string uniqueId; 19 20 /++ 21 + edit id, multiple edits can share the same id to show that multiple properties were edited at the same time 22 +/ 23 public ulong editId; 24 25 /++ 26 + type of edit 27 +/ 28 public EditType editType; 29 30 /++ 31 + various metadata, most used case is for storing which pasty was edited 32 +/ 33 public string[] metadata; 34 35 /++ 36 + actual edit, usually stores the old data 37 +/ 38 public string edit; 39 40 /++ 41 + unix time of when the edit was done 42 +/ 43 public ulong editedAt; 44 } 45 46 /++ 47 + type of paste edit 48 +/ 49 public enum EditType 50 { 51 title, 52 pastyTitle, 53 pastyLanguage, 54 pastyContent, 55 pastyAdded, 56 pastyRemoved, 57 } 58 59 /++ 60 + struct for a single pasty. a pasty is a part of a paste and represents a single "file", contains a title, language and code. 61 +/ 62 public struct Pasty 63 { 64 /++ 65 + id of the pasty 66 +/ 67 @name("_id") 68 @optional 69 public string id; 70 71 /++ 72 + title of the pasty. 73 +/ 74 public string title; 75 76 /++ 77 + language of the pasty. this stores the name of the language, not the mode or MIME type. 78 +/ 79 public string language; 80 81 /++ 82 + code of the pasty. 83 +/ 84 public string code; 85 } 86 87 /++ 88 + struct representing a paste. 89 +/ 90 public struct Paste 91 { 92 /++ 93 + paste id 94 +/ 95 @name("_id") 96 public string id; 97 98 /++ 99 + when the paste is created, using unix time. 100 +/ 101 public ulong createdAt; 102 103 /++ 104 + when the paste expires. 105 +/ 106 public ExpiresIn expiresIn; 107 108 /++ 109 + when the paste will get deleted, if `expiresIn` is set to never, this value is set to 0; 110 +/ 111 public ulong deletesAt; 112 113 /++ 114 + owner of the paste. if no owner then this value should be an empty string. 115 +/ 116 public string ownerId; 117 118 /++ 119 + if the paste is private. 120 +/ 121 public bool isPrivate; 122 123 /++ 124 + does the paste show up on the user's public profile? 125 +/ 126 public bool isPublic; 127 128 /++ 129 + array of all tags for this paste 130 +/ 131 public string[] tags; 132 133 /++ 134 + number of stars 135 +/ 136 public ulong stars; 137 138 /++ 139 + is the paste encrytped? 140 +/ 141 public bool encrypted; 142 143 /++ 144 + title of the paste. 145 +/ 146 public string title; 147 148 /++ 149 + pasties of the paste. a paste can have multiple pasties which are sort of like "files". 150 +/ 151 public Pasty[] pasties; 152 153 /++ 154 + array of paste edits 155 +/ 156 public Edit[] edits; 157 } 158 159 /++ 160 + struct containing info needed to create a pasty 161 +/ 162 public struct PastyCreateInfo 163 { 164 /++ 165 + title of the pasty. 166 +/ 167 public string title; 168 169 /++ 170 + language of the pasty. this stores the name of the language, not the mode or MIME type. 171 +/ 172 public string language; 173 174 /++ 175 + code of the pasty. 176 +/ 177 public string code; 178 } 179 180 /++ 181 + struct containing info needed to create a paste 182 +/ 183 public struct PasteCreateInfo 184 { 185 /++ 186 + title -- optional 187 +/ 188 public string title; 189 190 /++ 191 + expires in -- optional 192 +/ 193 public ExpiresIn expiresIn; 194 195 /++ 196 + is it only accessible by the owner -- optional 197 +/ 198 public bool isPrivate; 199 200 /++ 201 + is it displayed on the owners public profile -- optional 202 +/ 203 public bool isPublic; 204 205 /++ 206 + tags, comma separated -- optional 207 +/ 208 public string tags; 209 210 /++ 211 + list of pasties -- mandatory 212 +/ 213 public PastyCreateInfo[] pasties; 214 } 215 216 /++ 217 + returns a paste if it can find it by its id 218 + 219 + if the paste is private you need to provide the token (found on your profile's settings page) 220 +/ 221 public Nullable!Paste getPaste(string id, string token = "") 222 { 223 Nullable!Paste paste = Nullable!Paste.init; 224 225 requestHTTP(PASTE_ENDPOINT ~ id, 226 (scope req) 227 { 228 req.method = HTTPMethod.GET; 229 230 if (token != "") 231 { 232 req.headers.addField("Authorization", token); 233 } 234 }, 235 (scope res) 236 { 237 if (res.statusCode != HTTPStatus.notFound) 238 { 239 try 240 { 241 paste = nullable(deserializeJson!Paste(res.bodyReader.readAllUTF8())); 242 } catch (Exception) {} 243 } 244 } 245 ); 246 247 return paste; 248 } 249 250 /++ 251 + creates a paste and returns the full paste info 252 + 253 + if you want the paste to be tied to your account or to create a private/public paste, or use other account specific features you have to provide the token. 254 +/ 255 public Paste createPaste(const PasteCreateInfo createInfo, string token = "") 256 { 257 Paste result = Paste.init; 258 259 requestHTTP(BASE_ENDPOINT ~ "paste", 260 (scope req) 261 { 262 req.method = HTTPMethod.POST; 263 req.headers.addField("Content-Type", "application/json"); 264 265 if ((createInfo.isPrivate || createInfo.isPublic || createInfo.tags != "") && token == "") 266 { 267 throw new Exception("using account features but the token isnt provided"); 268 } 269 270 if (token != "") 271 { 272 req.headers.addField("Authorization", token); 273 } 274 275 req.writeJsonBody(createInfo); 276 }, 277 (scope res) 278 { 279 try 280 { 281 result = nullable(deserializeJson!Paste(res.bodyReader.readAllUTF8())); 282 } catch (Exception) {} 283 } 284 ); 285 286 return result; 287 } 288 289 /++ 290 + deletes a paste 291 + 292 + you can only delete pastes on your account so you must provide the token 293 + 294 + this action is irreversible 295 +/ 296 public void deletePaste(string id, string token) 297 { 298 requestHTTP(PASTE_ENDPOINT ~ id, 299 (scope req) 300 { 301 req.method = HTTPMethod.DELETE; 302 req.headers.addField("Content-Type", "application/json"); 303 304 req.headers.addField("Authorization", token); 305 }, (scope res) {} 306 ); 307 } 308 309 /++ 310 + edits a paste 311 + 312 + you can only edit pastes on your account so you must provide the token 313 + 314 + returns the new edited paste 315 + 316 + to edit values you need to send the exact same paste, except values you want edited should be changed 317 + 318 + you cant edit the expires in value, changing it will have no effect 319 +/ 320 public Paste editPaste(Paste paste, string token) 321 { 322 Paste result = Paste.init; 323 324 requestHTTP(PASTE_ENDPOINT ~ paste.id, 325 (scope req) 326 { 327 req.method = HTTPMethod.PATCH; 328 req.headers.addField("Content-Type", "application/json"); 329 330 req.headers.addField("Authorization", token); 331 332 req.writeJsonBody(paste); 333 }, 334 (scope res) 335 { 336 try 337 { 338 result = nullable(deserializeJson!Paste(res.bodyReader.readAllUTF8())); 339 } catch (Exception) {} 340 } 341 ); 342 343 return result; 344 } 345 346 @("getting a paste") 347 unittest 348 { 349 const paste = getPaste("cwy615yg").get(); 350 351 assert(paste.title == "DONT DELETE - api example"); 352 } 353 354 @("creating a paste") 355 unittest 356 { 357 const pastyCreateInfo = PastyCreateInfo("pasty1", "plain text", "asd asd asd"); 358 359 const createInfo = PasteCreateInfo("api test paste", 360 ExpiresIn.never, 361 false, 362 false, 363 "", 364 [pastyCreateInfo]); 365 366 const paste = createPaste(createInfo); 367 368 assert(paste.title == createInfo.title); 369 } 370 371 @("creating a private paste") 372 unittest 373 { 374 import std.process : environment; 375 376 const token = environment.get("TOKEN"); 377 378 const pastyCreateInfo = PastyCreateInfo("pasty1", "plain text", "asd asd asd"); 379 380 const createInfo = PasteCreateInfo("api test paste", 381 ExpiresIn.never, 382 true, 383 false, 384 "", 385 [pastyCreateInfo]); 386 387 const paste = createPaste(createInfo, token); 388 389 assert(paste.isPrivate); 390 } 391 392 @("deleting a paste") 393 unittest 394 { 395 import std.process : environment; 396 397 const token = environment.get("TOKEN"); 398 399 const pastyCreateInfo = PastyCreateInfo("pasty1", "plain text", "asd asd asd"); 400 401 const createInfo = PasteCreateInfo("api test paste", 402 ExpiresIn.never, 403 false, 404 false, 405 "", 406 [pastyCreateInfo]); 407 408 const paste = createPaste(createInfo, token); 409 410 deletePaste(paste.id, token); 411 412 assert(getPaste(paste.id, token).isNull()); 413 } 414 415 @("editing a paste") 416 unittest 417 { 418 import std.process : environment; 419 420 const token = environment.get("TOKEN"); 421 422 const pastyCreateInfo = PastyCreateInfo("pasty1", "plain text", "asd asd asd"); 423 424 const createInfo = PasteCreateInfo("api test paste", 425 ExpiresIn.never, 426 false, 427 false, 428 "", 429 [pastyCreateInfo]); 430 431 auto paste = createPaste(createInfo, token); 432 433 paste.title = "edited title"; 434 435 editPaste(paste, token); 436 }