Compare commits
425 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fd409c6dc | |||
| f4e7379768 | |||
| ee99191fb4 | |||
| 18124ae32a | |||
| de3373b8f9 | |||
| bdb4e6e2bd | |||
| 0a790de2c1 | |||
| ffb55bfde6 | |||
| c0fbe56f79 | |||
| 342ab0d471 | |||
| aa9fe7d408 | |||
| f499c10f09 | |||
| 45ed7cdf38 | |||
| fa271aaed2 | |||
| 8883476dcb | |||
| c1755ff85b | |||
| 6d04f6c62a | |||
| 53363711bb | |||
| 442ee45e97 | |||
| 8405b337f9 | |||
| facae926ce | |||
| 9544624e00 | |||
| a489fd465f | |||
| 9ff4c0dbd5 | |||
| 36ec16babe | |||
| 92233f2e90 | |||
| efd590976c | |||
| 463cbed7df | |||
| 9138976dad | |||
| ace4d5f6ce | |||
| b09ecb0b06 | |||
| 10fef0f667 | |||
| 3e5945fb9e | |||
| fdf1b2148c | |||
| 7e4d72f959 | |||
| 3e203ea967 | |||
| af56598a58 | |||
| 1fef1fa5f8 | |||
| bd2f68a51d | |||
| 64477f6b3a | |||
| 8b09dfd697 | |||
| f330673143 | |||
| 9351c05126 | |||
| af722e54d8 | |||
| 1eb89844f4 | |||
| 004423aea3 | |||
| 369c6245ab | |||
| b8ae937966 | |||
| 3b510a4f3d | |||
| b3e609d53d | |||
| 2f07a792e9 | |||
| 078bf2bc9c | |||
| a7c8f0b90e | |||
| 495acf4319 | |||
| dce86119d4 | |||
| 2c70b7a360 | |||
| c518185947 | |||
| b314178de2 | |||
| e0a2cec978 | |||
| d3608be1a0 | |||
| 4c79129242 | |||
| 8ac83f1d59 | |||
| 17e6d42989 | |||
| 0a3bbc1caf | |||
| 89e1331e49 | |||
| d934a53e6d | |||
| 2398d60b1a | |||
| 1bd755b628 | |||
| 861d5fa1b9 | |||
| 0239fc6773 | |||
| de57592d79 | |||
| f70f3a0a5d | |||
| 6c96b331dd | |||
| 767e09ee18 | |||
| b2f3ade7c7 | |||
| 8f050b40c1 | |||
| 3cf01ff14c | |||
| 73e0084125 | |||
| 093c9ec015 | |||
| 475dddfbe4 | |||
| 188e4f2740 | |||
| 4dc2b3184b | |||
| 139d541d86 | |||
| 10798c0a05 | |||
| 9c6ad11479 | |||
| 5527ef17f4 | |||
| 981f4010a9 | |||
| db7e8923c0 | |||
| 0eef5c2f01 | |||
| 08901f2e8a | |||
| 51b365226d | |||
| 0b89ee7eb5 | |||
| d7c4b24203 | |||
| d7352867ec | |||
| a2b97bd918 | |||
| 5991a767f7 | |||
| 643eba263e | |||
| 4db8a3992b | |||
| 03f7156749 | |||
| d9912e5548 | |||
| 3c85c985d9 | |||
| 0cf848b9b9 | |||
| ef4056abde | |||
| 3a6c25d42d | |||
| 642c95027c | |||
| 8a66a5c6c7 | |||
| 7dcc55a083 | |||
| 6989fec9d0 | |||
| bba190e4e9 | |||
| 6f670c8cc6 | |||
| d8626c624f | |||
| 454ad25b1d | |||
| 4dce703b20 | |||
| ce1e911c90 | |||
| f84f4dcee6 | |||
| bfa3757ee0 | |||
| 1fea5d7af3 | |||
| dd33bbf6a8 | |||
| 74640b1a82 | |||
| 51e6a74eda | |||
| 27a9812194 | |||
| ba03a9ed20 | |||
| f5c1c62a09 | |||
| 420e719db2 | |||
| c448f53964 | |||
| 72230f67d8 | |||
| 4bdc28d7fc | |||
| eb5195768b | |||
| 5b6539d799 | |||
| b1b471cf63 | |||
| bcd80463f3 | |||
| e2781cb090 | |||
| 09f906f7a1 | |||
| 706ab20ced | |||
| 1dde615686 | |||
| dda8be1fd0 | |||
| 2888877463 | |||
| ac4253a187 | |||
| a6f79d77d1 | |||
| 4ca841a1d5 | |||
| 4d2a8728be | |||
| ae3afdfc7e | |||
| e135b06e2c | |||
| 106843be09 | |||
| 022021dc4b | |||
| 8815b1608e | |||
| 3dcd7743be | |||
| 66fd838085 | |||
| b04852c98c | |||
| ad19bfa283 | |||
| ee86910caa | |||
| 9a4daaf024 | |||
| fcf1202af3 | |||
| 43c402ccae | |||
| 24334a1e78 | |||
| 37e27510de | |||
| fe35e8ea39 | |||
| db49879584 | |||
| df68305f28 | |||
| 078b10ccea | |||
| 1f8a36efb4 | |||
| b0c386ff87 | |||
| 8b69e06f79 | |||
| 24aa4c7406 | |||
| 621c45aaf1 | |||
| 9f67b83bc5 | |||
| da7dfd4bca | |||
| 07250f4ace | |||
| 8b5055d9d3 | |||
| ec36918061 | |||
| c895722ffc | |||
| 7df34db36b | |||
| 3008909f02 | |||
| 8af381729c | |||
| 8ca0aeb1bd | |||
| 99f8ec935b | |||
| d07cfe8fdb | |||
| 2f1b2a32c3 | |||
| b6fd62eb54 | |||
| 1dfe7e0ab5 | |||
| 17c7e9765e | |||
| 0ae03477df | |||
| 2348e95b6c | |||
| 439ecf4a7f | |||
| 68750da731 | |||
| 61de39712a | |||
| d32e6f9839 | |||
| 0cda4621c7 | |||
| 4bcd654e49 | |||
| bf0a0a17a0 | |||
| ca59b6b236 | |||
| 2a6cd98d4e | |||
| d4da2f322b | |||
| 04ce02bb5d | |||
| 62e3bb93c1 | |||
| 7da6b94daf | |||
| e95bd3b6fe | |||
| 8e4c70468d | |||
| 1ea5082314 | |||
| 267cbc751c | |||
| 5e522e1f3e | |||
| ba7ef25bcb | |||
| e132ea33f2 | |||
| d0b19d50e4 | |||
| 226ded6156 | |||
| 8f5abfd0a6 | |||
| 1bf2802b67 | |||
| b95826840a | |||
| 584e2b3ffb | |||
| 69af845ad1 | |||
| fba8eec319 | |||
| 01c0f9b324 | |||
| 86e8ccbbd6 | |||
| 0b45338300 | |||
| eafe6a0d7b | |||
| 3fb41f68fb | |||
| f5c819a489 | |||
| 5336317dbb | |||
| 6099e06a71 | |||
| 0066971b77 | |||
| c417a7f9ab | |||
| 1961b994d7 | |||
| 5288ca3840 | |||
| f985dc93aa | |||
| d6e43c40c9 | |||
| 184c24cace | |||
| 9861cb9aa7 | |||
| 2d722b94bd | |||
| 2db67e3b4d | |||
| 6290232858 | |||
| 7f6bf992a2 | |||
| 1f5bd8d6bb | |||
| 46383e57d2 | |||
| 2611b2c171 | |||
| 229207c8f8 | |||
| 2eb0b3abce | |||
| 8b6909b486 | |||
| dbb9fe44d8 | |||
| bb11001fb7 | |||
| b35457bb53 | |||
| 1ea0d39fa2 | |||
| 9193b3c90b | |||
| c349b50164 | |||
| fe3bc6efe4 | |||
| df138ee96e | |||
| d0b8e560c9 | |||
| 537409cec1 | |||
| bac8ad1040 | |||
| cc26cbeb7b | |||
| 3b6c2bd0e8 | |||
| 0b004fbcaf | |||
| c72c21e2a9 | |||
| d6310a79fa | |||
| 953354f367 | |||
| f8ddb21510 | |||
| 761e89108b | |||
| ec34a84d6d | |||
| 9cb5e6847b | |||
| 43f0c6f7f2 | |||
| ffcad81498 | |||
| f16a337c50 | |||
| 30e5303c36 | |||
| 19d5cb596f | |||
| ff58925606 | |||
| 12b16a0030 | |||
| b08c10a716 | |||
| b878561f65 | |||
| 5e36ad961f | |||
| 3dc2f0cfff | |||
| 073c4fb76e | |||
| cef0bd172d | |||
| 23eed197ee | |||
| 3f8f76df76 | |||
| b047023d9c | |||
| 420655f231 | |||
| 520ab88a97 | |||
| f96da50e6a | |||
| 1dc42daed4 | |||
| 0a80e2f5ec | |||
| 4661d2019c | |||
| 4e4e7b705d | |||
| bae1b0be72 | |||
| e890ee01a3 | |||
| af4b0757a9 | |||
| 6ef62d82c4 | |||
| 44df509b62 | |||
| 5d3fe2a5ea | |||
| 076e6dce73 | |||
| f745320456 | |||
| 6e64aaa1ee | |||
| 8b68edfc05 | |||
| 0a7bae4dac | |||
| b925a573f3 | |||
| 0012bb21ce | |||
| 3b3ec9fc4a | |||
| 396663d65c | |||
| f275b47755 | |||
| cfb7481d7e | |||
| 57fcce90d9 | |||
| 2c0ea0c108 | |||
| e81317b72c | |||
| 686cf9dc7e | |||
| a9e7cda37d | |||
| eae0b8a95e | |||
| 1b65b1d596 | |||
| d85b1fe2fd | |||
| f00006af1b | |||
| 75a07fe4ef | |||
| 946e429906 | |||
| 2e5a401ff7 | |||
| b8b84d8d16 | |||
| 4373538476 | |||
| af79ea2f72 | |||
| aab859bc20 | |||
| c692c16183 | |||
| cb59808be2 | |||
| 039777bf63 | |||
| 3547ec13c3 | |||
| 3763577451 | |||
| c6618049f2 | |||
| 28d6aa9885 | |||
| 568786e4f6 | |||
| 2aad2d29de | |||
| 400354ee9e | |||
| ea3ba78d1a | |||
| 38614ee526 | |||
| 15ad6bb5f8 | |||
| 15f431ada6 | |||
| 5163acc6ef | |||
| f9297d783a | |||
| c34a02c06f | |||
| 61ed1fb202 | |||
| 87638ba6c5 | |||
| 26b4100bb0 | |||
| 1157c2adf6 | |||
| 8613d3288b | |||
| ab008fbdff | |||
| 0b30197280 | |||
| 89a121f21e | |||
| 045a8c56af | |||
| d83c11db8c | |||
| 7efe14b835 | |||
| 37116aecf9 | |||
| 9a606e8f2a | |||
| b1e65ed500 | |||
| 26f3dbff06 | |||
| 5ee0b188bd | |||
| 4692cd6a55 | |||
| 7ad8c01853 | |||
| bc27b45fd7 | |||
| 94cc28d377 | |||
| 19e80cef3b | |||
| 5397cd3194 | |||
| f01af9c0ec | |||
| a288010d02 | |||
| b9a7a1c32b | |||
| 65b9a0dce5 | |||
| 7464006e19 | |||
| 590d56b7e7 | |||
| c75cb1e87e | |||
| 597b2f4a6f | |||
| 2e84c6bbb2 | |||
| 06c0cd5297 | |||
| 79eab3d62f | |||
| 6527723a0e | |||
| 3d6652f132 | |||
| da5d9779d4 | |||
| 865399e753 | |||
| 960ea6c8dd | |||
| 39ab48f0cb | |||
| 7341b4cf61 | |||
| 28c5acc6af | |||
| 4366eefc66 | |||
| cb95e761c1 | |||
| 7136748463 | |||
| b0cacfd75c | |||
| 81f03dac79 | |||
| e37d1eadcd | |||
| 23fb310374 | |||
| 797a1732d2 | |||
| c405a417e9 | |||
| 808deb1d77 | |||
| 6ac1473ec3 | |||
| 9c183abf4b | |||
| 33c33faae9 | |||
| 4cccbc2039 | |||
| cc061dedf8 | |||
| 402ce2210f | |||
| 329c5c4070 | |||
| 878aeb7a8c | |||
| 3ee396d788 | |||
| 090037602c | |||
| bee77051e7 | |||
| be6741a4a8 | |||
| 3e9b440322 | |||
| dfea5bc77c | |||
| 1eef30e298 | |||
| e5b9a9de3f | |||
| dfedd3a6e8 | |||
| a19845b4ca | |||
| 094f6f5a7a | |||
| fa60edce2b | |||
| ba44652457 | |||
| 931be39912 | |||
| 5048a27acb | |||
| 71fc615b31 | |||
| 20ed4e22ef | |||
| fc2d337a39 | |||
| 31d96f3862 | |||
| 7b43853ec6 | |||
| 40fa913272 | |||
| cdd5482aad | |||
| 4a3fa20ee7 | |||
| d49ed4cd47 | |||
| db3cf77f5b | |||
| c4a08961d6 | |||
| 5ba1643d46 | |||
| d189f425e8 | |||
| 0fe6fb83ac | |||
| f15ae41f48 | |||
| 46a07fc72e | |||
| 83af317739 | |||
| f4db1b2735 | |||
| 6d4922f419 | |||
| 6af098ee8a |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
run context jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Better docs for io.try
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Added retrying and better messaging
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
IO Logging now respects the job and client logLevel, and only outputs locally when ioLogLocalEnabled is true
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Adding support for trigger source in the run context, and make sure dynamic trigger runs are preprocessed so they have a chance of populating run properties
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
A number of improvements to the initialization experience:
|
||||
|
||||
- Detect middleware usage and warn about how it could conflict with Trigger.dev and with a link to the docs
|
||||
- Lookup package versions and use the latest versions
|
||||
- Better error messages when registering an endpoint doesn't work
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/github": patch
|
||||
---
|
||||
|
||||
Fixed the broken export issue with two of the new tasks
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Fix for Next.js projects using the src dir
|
||||
@@ -2,13 +2,10 @@
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"fixed": [["@trigger.dev/*"]],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": [
|
||||
"webapp",
|
||||
"emails"
|
||||
]
|
||||
}
|
||||
"ignore": ["webapp", "emails", "@trigger.dev/database"]
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/integration-kit": patch
|
||||
"@trigger.dev/sdk": patch
|
||||
"@trigger.dev/github": patch
|
||||
"@trigger.dev/openai": patch
|
||||
"@trigger.dev/slack": patch
|
||||
"@trigger.dev/nextjs": patch
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Creating the init CLI package
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": major
|
||||
"@trigger.dev/github": major
|
||||
"@trigger.dev/slack": major
|
||||
"@trigger.dev/nextjs": major
|
||||
---
|
||||
|
||||
Preparing packages for V2
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Remove the localhost default for endpoint URL
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Fixed the version of the @trigger.dev/nextjs package
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
registerCron and unregisterCron jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Endpoint registration and indexing now is only initiated outside of clients
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Split the CLI into two commands, init and a new dev command
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"@trigger.dev/github": patch
|
||||
"@trigger.dev/nextjs": patch
|
||||
---
|
||||
|
||||
Add support for task errors and task retrying
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
io.runTask jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
[dev] Better handle errors when registering endpoint
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Improved the CLI prompts
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
io.sendEvent jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
deliverAt and timestamp event properties are now dates
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/openai": patch
|
||||
---
|
||||
|
||||
listModels returns the response.data instead of response.data.data
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Fixed the eventTrigger name
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Added JSDocs related to logging
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Removed --tunnel option, currently we just support ngrok
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Fixed responses from the PING action to match expected schema
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Adding more granular error messages around unauthorized requests
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"@trigger.dev/github": patch
|
||||
"@trigger.dev/openai": patch
|
||||
"@trigger.dev/slack": patch
|
||||
---
|
||||
|
||||
Added support for backgroundFetch
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Export the client from the generated code and added code comments
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/github": patch
|
||||
---
|
||||
|
||||
Fixed some properties that had API urls instead of HTML urls
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/nextjs": patch
|
||||
---
|
||||
|
||||
Adaptors no longer take an options object
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"@trigger.dev/internal": patch
|
||||
---
|
||||
|
||||
backgroundFetch jsdocs
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"webapp": "1.0.0",
|
||||
"@trigger.dev/github": "0.2.0",
|
||||
"@trigger.dev/slack": "0.2.0",
|
||||
"create-trigger": "0.2.0",
|
||||
"emails": "1.0.0",
|
||||
"@trigger.dev/internal": "0.0.1",
|
||||
"@trigger.dev/nextjs": "0.1.0",
|
||||
"@trigger.dev/sdk": "1.0.0",
|
||||
"@trigger.dev/openai": "1.0.0-next.0",
|
||||
"@trigger.dev/database": "0.0.0",
|
||||
"@trigger.dev/init": "0.2.0",
|
||||
"@trigger.dev/integration-kit": "2.0.0-next.1",
|
||||
"@trigger.dev/resend": "1.0.0-next.1",
|
||||
"@trigger.dev/cli": "0.2.1-next.8"
|
||||
},
|
||||
"changesets": [
|
||||
"afraid-hornets-cheer",
|
||||
"beige-maps-hug",
|
||||
"big-tables-search",
|
||||
"bright-buses-join",
|
||||
"bright-horses-bathe",
|
||||
"chatty-cars-agree",
|
||||
"chilly-meals-warn",
|
||||
"clever-bats-learn",
|
||||
"curly-hornets-sort",
|
||||
"dirty-needles-kneel",
|
||||
"eight-buckets-agree",
|
||||
"eighty-queens-prove",
|
||||
"fast-ligers-wink",
|
||||
"four-ligers-perform",
|
||||
"friendly-parents-clean",
|
||||
"great-meals-listen",
|
||||
"healthy-rockets-cry",
|
||||
"heavy-eyes-own",
|
||||
"heavy-mails-explain",
|
||||
"hot-eggs-protect",
|
||||
"hot-seals-press",
|
||||
"itchy-cars-count",
|
||||
"itchy-rockets-provide",
|
||||
"long-clocks-hide",
|
||||
"lovely-hounds-accept",
|
||||
"lucky-crews-matter",
|
||||
"metal-donkeys-relate",
|
||||
"modern-emus-suffer",
|
||||
"moody-islands-melt",
|
||||
"odd-colts-smash",
|
||||
"pink-islands-pay",
|
||||
"popular-cooks-boil",
|
||||
"quick-stingrays-own",
|
||||
"quiet-dryers-collect",
|
||||
"rich-clocks-attack",
|
||||
"shy-bobcats-hear",
|
||||
"silent-mirrors-lay",
|
||||
"silver-insects-sneeze",
|
||||
"six-humans-happen",
|
||||
"sixty-squids-wink",
|
||||
"strange-jobs-shout",
|
||||
"strong-cars-visit",
|
||||
"stupid-files-cough",
|
||||
"sweet-cups-rescue",
|
||||
"sweet-snails-refuse",
|
||||
"tame-flowers-invite",
|
||||
"tasty-bats-flow",
|
||||
"ten-birds-confess",
|
||||
"tidy-planes-compete",
|
||||
"tough-walls-share",
|
||||
"wild-rats-mix",
|
||||
"young-birds-unite"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Fixed the help defaults
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
registerTrigger jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
io.registerInterval and io.unregisterInterval jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Removed the url option for TriggerClient
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Added JSdocs for io.wait and io.logger
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
isTriggerError jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
eventTrigger() jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
io.try jsdocs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Added better Next Steps in the init cli command
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Fixed an issue that cause environment variables to be incorrectly added to .env.local
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/nextjs": patch
|
||||
---
|
||||
|
||||
Loosen the peer dep requirement on next
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Added human readable cron expression property to cron triggers
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Fixed CLI showing undefined when not specifying a trigger-url flag
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/resend": patch
|
||||
---
|
||||
|
||||
Adding the resend integration package
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Added jsdocs for TriggerClient() and sendEvent()
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Better handle input for the --trigger-url option
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/slack": patch
|
||||
---
|
||||
|
||||
Added joinConversation task, and automatically try and join a public conversation if the bot is not in it when posting a message
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/nextjs": patch
|
||||
---
|
||||
|
||||
Make sure nextjs app routes have the correct types
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/nextjs": patch
|
||||
---
|
||||
|
||||
Update the devDependency on nextjs to be more permissive
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Added JSdocs for Job
|
||||
+9
-1
@@ -3,6 +3,7 @@
|
||||
.github
|
||||
# editor
|
||||
.idea
|
||||
.vscode
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
@@ -34,4 +35,11 @@ cypress/screenshots
|
||||
cypress/videos
|
||||
|
||||
apps/**/styles/tailwind.css
|
||||
packages/**/styles/tailwind.css
|
||||
packages/**/styles/tailwind.css
|
||||
|
||||
.changeset
|
||||
examples
|
||||
CHANGESETS.md
|
||||
CONTRIBUTING.md
|
||||
README.md
|
||||
LICENSE
|
||||
|
||||
+11
-14
@@ -1,13 +1,7 @@
|
||||
# YOU MUST SET THESE VARIABLES
|
||||
# Resend is an email service used for signing in to Trigger.dev via a Magic Link.
|
||||
### Visit https://resend.com, create an account and get your API key. Then insert it below along with your From and Reply To email addresses. Visit https://resend.com/docs for more information.
|
||||
RESEND_API_KEY=<api_key>
|
||||
FROM_EMAIL=
|
||||
REPLY_TO_EMAIL=
|
||||
|
||||
# YOU MIGHT LIKE TO MODIFY THESE VARIABLES
|
||||
SESSION_SECRET=abcdef1234
|
||||
MAGIC_LINK_SECRET=abcdef1234
|
||||
ENCRYPTION_KEY=ae13021afef0819c3a307ad487071c06 # Must be a random 16 byte hex string. You can generate an encryption key by running `openssl rand -hex 16` in your terminal
|
||||
LOGIN_ORIGIN=http://localhost:3030
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres?schema=public
|
||||
REMIX_APP_PORT=3030
|
||||
@@ -16,17 +10,20 @@ APP_ORIGIN=http://localhost:3030
|
||||
NODE_ENV=development
|
||||
|
||||
# OPTIONAL VARIABLES
|
||||
AUTH_GITHUB_CLIENT_ID=
|
||||
AUTH_GITHUB_CLIENT_SECRET=
|
||||
# This is used for logging in via GitHub. You can leave these commented out if you don't want to use GitHub for authentication.
|
||||
# AUTH_GITHUB_CLIENT_ID=
|
||||
# AUTH_GITHUB_CLIENT_SECRET=
|
||||
|
||||
# Resend is an email service used for signing in to Trigger.dev via a Magic Link.
|
||||
# Emails will print to the console if you leave these commented out
|
||||
### Visit https://resend.com, create an account and get your API key. Then insert it below along with your From and Reply To email addresses. Visit https://resend.com/docs for more information.
|
||||
# RESEND_API_KEY=<api_key>
|
||||
# FROM_EMAIL=
|
||||
# REPLY_TO_EMAIL=
|
||||
|
||||
# CLOUD VARIABLES
|
||||
POSTHOG_PROJECT_KEY=
|
||||
PLAIN_API_KEY=
|
||||
SEED_CLOUD=
|
||||
SEED_CLOUD_EMAIL=
|
||||
SEED_CLOUD_GITHUB_ACCESS_TOKEN=
|
||||
SEED_CLOUD_SLACK_ACCESS_TOKEN=
|
||||
SEED_CLOUD_USER_GITHUB_ACCESS_TOKEN=
|
||||
CLOUD_AIRTABLE_CLIENT_ID=
|
||||
CLOUD_AIRTABLE_CLIENT_SECRET=
|
||||
CLOUD_GITHUB_CLIENT_ID=
|
||||
|
||||
@@ -5,7 +5,23 @@ on:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "v*"
|
||||
- "v.docker.*"
|
||||
paths:
|
||||
- ".github/workflows/publish.yml"
|
||||
- "packages/**"
|
||||
- "!packages/**/*.md"
|
||||
- "!packages/**/*.eslintrc"
|
||||
- "apps/**"
|
||||
- "!apps/**/*.md"
|
||||
- "!apps/**/*.eslintrc"
|
||||
- "integrations/**"
|
||||
- "!integrations/**/*.md"
|
||||
- "!integrations/**/*.eslintrc"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "turbo.json"
|
||||
- "docker/Dockerfile"
|
||||
- "docker/scripts/**"
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -63,6 +79,10 @@ jobs:
|
||||
run: |
|
||||
IMAGE_TAG="${GITHUB_REF#refs/tags/}"
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
if [[ $IMAGE_TAG == v.docker.* ]]; then
|
||||
ORIGINAL_VERSION="${IMAGE_TAG#v.docker.}"
|
||||
IMAGE_TAG="v${ORIGINAL_VERSION}"
|
||||
fi
|
||||
echo "IMAGE_TAG=${IMAGE_TAG}"
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
IMAGE_TAG="${GITHUB_REF#refs/heads/}"
|
||||
@@ -71,8 +91,7 @@ jobs:
|
||||
echo "Invalid reference: ${GITHUB_REF}"
|
||||
exit 1
|
||||
fi
|
||||
echo ::set-output name=version::${IMAGE_TAG}
|
||||
|
||||
echo "::set-output name=version::${IMAGE_TAG}"
|
||||
- name: 🔢 Get the commit hash
|
||||
id: get_commit
|
||||
run: |
|
||||
|
||||
@@ -4,6 +4,16 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- ".github/workflows/release.yml"
|
||||
- "packages/**"
|
||||
- "!packages/**/*.md"
|
||||
- "changesets/**"
|
||||
- "integrations/**"
|
||||
- "!integrations/**/*.md"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "turbo.json"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"workspaceId":"63e5e42daf9a537ba8d9503c"}
|
||||
{ "workspaceId": "63e5e42daf9a537ba8d9503c" }
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
node_modules
|
||||
.env
|
||||
.env.local
|
||||
pnpm-lock.yaml
|
||||
tailwind.css
|
||||
.babelrc.json
|
||||
**/.react-email/
|
||||
**/storybook-static/
|
||||
**/.changeset/
|
||||
**/build/
|
||||
**/dist/
|
||||
Vendored
+1
-8
@@ -5,19 +5,12 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "pnpm run idev --filter webapp",
|
||||
"command": "pnpm run dev --filter webapp",
|
||||
"name": "Run webapp",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev --filter integrations",
|
||||
"name": "Run integrations",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
|
||||
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "trigger.dev",
|
||||
"path": "../"
|
||||
},
|
||||
{
|
||||
"name": "Webapp",
|
||||
"path": "../apps/webapp"
|
||||
},
|
||||
{
|
||||
"name": "Integrations",
|
||||
"path": "../apps/integrations"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"vitest.commandLine": "pnpm exec vitest"
|
||||
}
|
||||
}
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
# Contributing to Trigger.dev
|
||||
|
||||
Thank you for taking the time to contribute to Trigger.dev. Your involvement is not just welcomed, but we encourage it! 🚀
|
||||
|
||||
Please take some time to read this guide to understand contributing best practices for Trigger.dev.
|
||||
|
||||
Thank you for helping us make Trigger.dev even better! 🤩
|
||||
|
||||
## Developing
|
||||
|
||||
The development branch is `main`. This is the branch that all pull
|
||||
requests should be made against. The changes on the `main`
|
||||
branch are tagged into a release monthly.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/en) version >=16.x
|
||||
- [pnpm package manager](https://pnpm.io/installation) version 7
|
||||
- [Docker](https://www.docker.com/get-started/)
|
||||
|
||||
### Setup
|
||||
|
||||
1. Clone the repo into a public GitHub repository or [fork the repo](https://github.com/triggerdotdev/trigger.dev/fork). If you plan to distribute the code, keep the source code public to comply with the [Apache Licence 2.0](https://github.com/triggerdotdev/trigger.dev/blob/main/LICENSE).
|
||||
|
||||
```
|
||||
git clone https://github.com/triggerdotdev/trigger.dev.git
|
||||
```
|
||||
|
||||
> If you are on windows, run the following command on gitbash with admin privileges:
|
||||
> `git clone -c core.symlinks=true https://triggerdotdev/trigger.dev.git`
|
||||
|
||||
2. Navigate to the project folder
|
||||
```
|
||||
cd trigger.dev
|
||||
```
|
||||
3. Install the required packages using pnpm.
|
||||
```
|
||||
pnpm i
|
||||
```
|
||||
4. Create your `.env` files
|
||||
```
|
||||
cp .env.example .env && cp packages/database/.env.example packages/database/.env
|
||||
```
|
||||
5. Open the root `.env` file and generate a new value for `ENCRYPTION_KEY`:
|
||||
|
||||
`ENCRYPTION_KEY` is used to two-way encrypt OAuth access tokens and so you'll probably want to actually generate a unique value, and it must be a random 16 byte hex string. You can generate one with the following command:
|
||||
|
||||
```sh
|
||||
openssl rand -hex 16
|
||||
```
|
||||
|
||||
Feel free to update `SESSION_SECRET` and `MAGIC_LINK_SECRET` as well using the same method.
|
||||
|
||||
6. Start Docker. This starts the required services like Postgres. If this is your first time using Docker, consider going through this [guide](DOCKER_INSTALLATION.md)
|
||||
```
|
||||
pnpm run docker
|
||||
```
|
||||
7. Migrate the database
|
||||
```
|
||||
pnpm run db:migrate
|
||||
```
|
||||
8. Build the app
|
||||
```
|
||||
pnpm run build --filter webapp
|
||||
```
|
||||
9. Run the seed script
|
||||
```
|
||||
pnpm run db:seed
|
||||
```
|
||||
10. Run the app. See the section below.
|
||||
|
||||
## Running
|
||||
|
||||
1. You can run the app with:
|
||||
|
||||
```
|
||||
pnpm run dev --filter webapp
|
||||
```
|
||||
|
||||
It should run on port `3030`: [http://localhost:3030](http://localhost:3030/)
|
||||
|
||||
2. Once the app is running click the magic link button and enter your email.
|
||||
3. Check your terminal, the magic link email should have printed out as following:
|
||||
|
||||
```sh
|
||||
webapp:dev: Log in to Trigger.dev
|
||||
webapp:dev:
|
||||
webapp:dev: Click here to log in with this magic link
|
||||
webapp:dev: [http://localhost:3030/magic?token=U2FsdGVkX18OvB0JxgaswTLCSbaRz%2FY82TN0EZWhSzFyZYwgG%2BIzKVTkeiaOtWfotPw7F8RwFzCHh53aBpMEu%2B%2B%2FItb%2FcJYh89MSjc3Pz92bevoEjqxSQ%2Ff%2BZbks09JOpqlBbYC3FzGWC8vuSVFBlxqLXxteSDLthZSUaC%2BS2LaA%2BJgp%2BLO7hgjAaC2lXbCHrM7MTgTdXOFt7i0Dvvuwz6%2BWY25RnfomZOPqDsyH0xz8Q2rzPTz0Xu53WSXrZ1hd]
|
||||
webapp:dev:
|
||||
webapp:dev: If you didn't try to log in, you can safely ignore this email.
|
||||
```
|
||||
|
||||
Paste the magic link shown in your terminal into your browser to login.
|
||||
|
||||
## Adding and running migrations
|
||||
|
||||
1. Modify packages/database/prisma/schema.prisma file
|
||||
2. Change directory to the packages/database folder
|
||||
```sh
|
||||
cd packages/database
|
||||
```
|
||||
3. Generate the Prisma client
|
||||
|
||||
```sh
|
||||
pnpm run generate
|
||||
```
|
||||
|
||||
The above updates the prisma client generated into node_modules/.prisma/client folder. This helps with typing of relevant prisma models. It ensures typescript
|
||||
recognizes fields added or removed from a model and type-checks appropriately.
|
||||
|
||||
4. Create and apply the migrations
|
||||
|
||||
```
|
||||
pnpm run db:migrate:dev
|
||||
```
|
||||
|
||||
This creates a migration file and executes the migrations against your database and applies changes to the database schema(s)
|
||||
|
||||
5. Commit generated migrations as well as changes to the schema.prisma file
|
||||
6. If you're using VSCode you may need to restart the Typescript server in the webapp to get updated type inference. Open a TypeScript file, then open the Command Palette (View > Command Palette) and run `TypeScript: Restart TS server`.
|
||||
|
||||
## Testing CLI changes
|
||||
|
||||
To test CLI changes, follow the steps below:
|
||||
|
||||
1. Build the CLI and watch for changes
|
||||
|
||||
```sh
|
||||
cd packages/cli
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
2. Open a new Terminal window and run the webapp locally and then create a new project in the dashboard. Copy out the dev API key.
|
||||
|
||||
3. Create a new temporary Next.js app in examples directory
|
||||
|
||||
```sh
|
||||
pnpm create next-app@latest
|
||||
```
|
||||
|
||||
Follow the prompts to create a TypeScript project using the App Directory.
|
||||
|
||||
4. Then once that's finished, add the `@trigger.dev/cli` to the `devDependencies` of the newly created Next.js app's `package.json` file, like so:
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": { "@trigger.dev/cli": "workspace:*" }
|
||||
}
|
||||
```
|
||||
|
||||
5. Open a new terminal window, navigate into the example, and initialize the CLI:
|
||||
|
||||
```sh
|
||||
cd examples/your-newly-created-nextjs-project
|
||||
pnpm i
|
||||
pnpm exec trigger-cli init
|
||||
```
|
||||
|
||||
6. When prompted, select `self-hosted` and enter `localhost:3030` for your local version of the webapp. When asked for an API key, use the key you copied earlier.
|
||||
|
||||
7. Run the CLI
|
||||
|
||||
```sh
|
||||
pnpm exec trigger-cli dev
|
||||
```
|
||||
|
||||
8. After running the CLI, start your newly created Next.js project. You should now be able to see the changes.
|
||||
|
||||
9. Please remember to delete the temporary project you created after you've tested the changes, and before you raise a PR.
|
||||
|
||||
## Add sample jobs
|
||||
|
||||
The [examples/jobs-starter](./examples/jobs-starter/) project defines simple jobs you can get started with.
|
||||
|
||||
1. `cd` into `examples/jobs-starter`
|
||||
2. Create a `.env.local` file with the following content,
|
||||
replacing `[TRIGGER_DEV_API_KEY]` with an actual key:
|
||||
|
||||
```env
|
||||
TRIGGER_API_KEY=[TRIGGER_DEV_API_KEY]
|
||||
TRIGGER_API_URL=http://localhost:3030
|
||||
```
|
||||
|
||||
`TRIGGER_API_URL` is used to configure the URL for your Trigger.dev instance,
|
||||
where the jobs will be registered.
|
||||
|
||||
3. Run the `jobs-starter` app:
|
||||
|
||||
```sh
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
4. Navigate to your trigger.dev instance ([http://localhost:3030](http://localhost:3030/)), to see the jobs.
|
||||
You can use the test feature to trigger them.
|
||||
|
||||
## Making a pull request
|
||||
|
||||
**If you get errors, be sure to fix them before committing.**
|
||||
|
||||
- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) while creating you PR.
|
||||
- If your PR refers to or fixes an issue, be sure to add `refs #XXX` or `fixes #XXX` to the PR description. Replacing `XXX` with the respective issue number. See more about [Linking a pull request to an issue
|
||||
](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
|
||||
- Be sure to fill the PR Template accordingly.
|
||||
|
||||
## Adding changesets
|
||||
|
||||
We use [changesets](https://github.com/changesets/changesets) to manage our package versions and changelogs. If you've never used changesets before, first read [their guide here](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).
|
||||
|
||||
If you are contributing a change to any packages in this monorepo (anything in either the `/packages` or `/integrations` directories), then you will need to add a changeset to your Pull Requests before they can be merged.
|
||||
|
||||
To add a changeset, run the following command in the root of the repo
|
||||
|
||||
```sh
|
||||
pnpm run changeset:add
|
||||
```
|
||||
|
||||
Here's an example of creating a `patch` changeset for the `@trigger.dev/github` and `@trigger.dev/slack` packages (click to view):
|
||||
|
||||
[](https://asciinema.org/a/599228)
|
||||
|
||||
You will be prompted to select which packages to include in the changeset. Only select the packages that you have made changes for.
|
||||
|
||||
Most of the time the changes you'll make are likely to be categorized as patch releases. If you feel like there is the need for a minor or major release of the package based on the changes being made, add the changeset as such and it will be discussed during PR review.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### EADDRINUSE: address already in use :::3030
|
||||
|
||||
When receiving the following error message:
|
||||
|
||||
```sh
|
||||
webapp:dev: Error: listen EADDRINUSE: address already in use :::3030
|
||||
```
|
||||
|
||||
The process running on port `3030` should be destroyed.
|
||||
|
||||
1. Get the `PID` of the process running on PORT `3030`
|
||||
```sh
|
||||
lsof -i :3030
|
||||
```
|
||||
2. Kill the process
|
||||
```sh
|
||||
sudo kill -9 <PID>
|
||||
```
|
||||
@@ -1,32 +0,0 @@
|
||||
# Trigger.dev Deployment Guide
|
||||
|
||||
## StreamNative Cloud - Hosted Pulsar
|
||||
|
||||
### Generic client credentials
|
||||
|
||||
```sh
|
||||
snctl auth export-service-account webapp --key-file webapp-credentials.json
|
||||
snctl auth export-service-account websocketserver --key-file wss-credentials.json
|
||||
```
|
||||
|
||||
### Topics
|
||||
|
||||
#### Triggers (`persistent://triggerdotdev/workflows/triggers`)
|
||||
|
||||
Events that trigger workflows to run. These are sent by the "platform" and read by the Web Socket Servers, which then coordinate with the hosts for running the workflows
|
||||
|
||||
#### Run Commands (`persistent://triggerdotdev/workflows/run-commands`)
|
||||
|
||||
These are events that come from hosts and are published by the Web Socket Servers, e.g. Sending Integration Requests, Sending Logs, Initializing a Delay
|
||||
|
||||
#### Run Command Responses (`persistent://triggerdotdev/workflows/run-command-responses`)
|
||||
|
||||
These are events that come from the platform and are read by the Web Socket Servers, to resolve or reject a previous Run Command
|
||||
|
||||
#### Integration Requests (`persistent://triggerdotdev/queues/integration-requests`)
|
||||
|
||||
This is an internal queue used by the platform to perform integration requests, and retry them.
|
||||
|
||||
#### App Task Queue (`persistent://triggerdotdev/queues/background-tasks`)
|
||||
|
||||
This is an internal queue used by the platform to do tasks in a queue. Basically a background job system.
|
||||
-231
@@ -1,231 +0,0 @@
|
||||
# Initial setup
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Pulsar requirements
|
||||
|
||||
1. Ensure you have Homebrew installed by running `which brew` in terminal. If it's not found then you should install it: https://brew.sh/. Run `which brew` again to check it's found. If it's not you may need to [add it your path](https://stackoverflow.com/questions/36657321/after-installing-homebrew-i-get-zsh-command-not-found-brew)
|
||||
|
||||
2. Run `brew install libpulsar` to install the C++ libraries that the pulsar-client depends on
|
||||
|
||||
3. Make sure you have Python installed on your machine by running `which python3` in terminal.
|
||||
|
||||
4. If python isn't found then you should install it: https://www.python.org/downloads/. In a new terminal window run `which python3` again.
|
||||
|
||||
5. Run `npm config set python /the/path/from/the/which/python3/command` inserting the path from step 2 or 3
|
||||
|
||||
6. Install node-gyp: `npm install -g node-gyp`
|
||||
|
||||
7. Make sure you have the Xcode command line tools installed by running `xcode-select --install` from the terminal. If it says they're already installed then you're set.
|
||||
|
||||
8. Run this in the terminal:
|
||||
|
||||
```sh
|
||||
export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:$(brew --prefix)/include"
|
||||
export LIBRARY_PATH="$LIBRARY_PATH:$(brew --prefix)/lib"
|
||||
export PULSAR_CPP_DIR=/opt/homebrew/Cellar/libpulsar/3.1.0
|
||||
```
|
||||
|
||||
9. Run `pnpm install` in the same terminal window.
|
||||
|
||||
## Building and running the webapp
|
||||
|
||||
> **Warning**
|
||||
> All the following commands should be launched from the **monorepo root directory**
|
||||
|
||||
1. Install the dependencies.
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
2. Optionally, if you are testing auth (pizzly) or webhooks (webapp) then you'll need to use ngrok to proxy internet traffic to your local machine
|
||||
|
||||
Get access to ngrok and then follow the instructions here to get it setup: https://ngrok.com/download (use homebrew) and make sure to authenticate.
|
||||
|
||||
Then run the following scripts to start proxying:
|
||||
|
||||
```sh
|
||||
./scripts/proxy-pizzly.sh dan-pizzly-dev
|
||||
./scripts/proxy-webapp.sh dan-trigger-dev
|
||||
```
|
||||
|
||||
3. Environment variables. You will need to create copies of the `.env.example` files in `app/webapp`
|
||||
|
||||
```sh
|
||||
cp ./apps/webapp/.env.example ./apps/webapp/.env
|
||||
```
|
||||
|
||||
Then you will need to fill in the fields with real values.
|
||||
|
||||
You also need to create the `pizzly-server.env` files under the `.docker` directory:
|
||||
|
||||
```sh
|
||||
cp ./.docker/pizzly-server.env.example ./.docker/pizzly-server.env
|
||||
```
|
||||
|
||||
Next, update the `AUTH_CALLBACK_URL` env var in the `pizzly-server.env` env file with the value provided to the `./scripts/proxy-pizzly.sh` command. Using the example above the `AUTH_CALLBACK_URL` would be `AUTH_CALLBACK_URL=https://dan-pizzly-dev.eu.ngrok.io/oauth/callback`.
|
||||
|
||||
If you aren't proxying pizzly according to step 2, then leave the `pizzly-server.env` file empty.
|
||||
|
||||
If you are proxying the webapp according to step 2 then in `webapp/.env`, set the `APP_ORIGIN` to the `NGROK_SUBDOMAIN` provided to the `./scripts/proxy-webapp.sh` command, e.g. `APP_ORIGIN=https://dan-trigger-dev.eu.ngrok.io`
|
||||
|
||||
4. Start postgresql, redis, pizzly server, followed by pulsar standalone
|
||||
|
||||
```bash
|
||||
pnpm run docker:services
|
||||
pnpm run docker:pulsar
|
||||
```
|
||||
|
||||
> **Note:** The npm script will complete while Docker sets up the container in the background. Ensure that Docker has finished and your container is running before proceeding.
|
||||
|
||||
5. Generate prisma schema
|
||||
```bash
|
||||
pnpm run generate
|
||||
```
|
||||
6. Run the Prisma migration to the database
|
||||
|
||||
```bash
|
||||
pnpm run db:migrate:deploy
|
||||
```
|
||||
|
||||
7. Run the first build (with dependencies via the `...` option)
|
||||
|
||||
```bash
|
||||
pnpm run build --filter=webapp...
|
||||
```
|
||||
|
||||
**Running simply `pnpm run build` will build everything, including the Remix app.**
|
||||
|
||||
8. Run the Remix dev server
|
||||
|
||||
```bash
|
||||
pnpm run dev --filter=webapp
|
||||
```
|
||||
|
||||
## Attaching an API integration using Pizzly
|
||||
|
||||
```bash
|
||||
PIZZLY_HOSTPORT=http://localhost:3004 npx pizzly config:create github github <client-id> <client-secret> "repo,user"
|
||||
```
|
||||
|
||||
## Tests, Typechecks, Lint, Install packages...
|
||||
|
||||
Check the `turbo.json` file to see the available pipelines.
|
||||
|
||||
- Run the Cypress tests and Dev
|
||||
```bash
|
||||
pnpm run test:e2e:dev --filter=webapp
|
||||
```
|
||||
- Lint everything
|
||||
```bash
|
||||
pnpm run lint
|
||||
```
|
||||
- Typecheck the whole monorepo
|
||||
```bash
|
||||
pnpm run typecheck
|
||||
```
|
||||
- Test the whole monorepo
|
||||
```bash
|
||||
pnpm run test
|
||||
or
|
||||
pnpm run test:dev
|
||||
```
|
||||
- How to install an npm package in the Remix app ?
|
||||
```bash
|
||||
pnpm add dayjs --filter webapp
|
||||
```
|
||||
- Tweak the tsconfigs, eslint configs in the `config-package` folder. Any package or app will then extend from these configs.
|
||||
|
||||
# Running a workflow locally
|
||||
|
||||
## After pulling a change
|
||||
|
||||
1. Ensure there are no database migrations to run
|
||||
|
||||
```bash
|
||||
pnpm run db:migrate:dev
|
||||
```
|
||||
|
||||
2. Generate the Prisma database client
|
||||
|
||||
```bash
|
||||
pnpm run generate
|
||||
```
|
||||
|
||||
3. Install packages
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
4. Build everything
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
5. Install packages again, this makes sure the local packages are linked
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Running the servers
|
||||
|
||||
1. Ensure the docker containers are running
|
||||
|
||||
```bash
|
||||
pnpm run docker:services
|
||||
pnpm run docker:pulsar
|
||||
```
|
||||
|
||||
2. Run the webapp
|
||||
|
||||
```bash
|
||||
pnpm run dev --filter=webapp
|
||||
```
|
||||
|
||||
3. Run the Web Socket Server
|
||||
|
||||
```bash
|
||||
pnpm run dev --filter=wss
|
||||
```
|
||||
|
||||
4. Build all the @trigger.dev/\* packages
|
||||
|
||||
```bash
|
||||
pnpm run dev --filter="@trigger.dev/*"
|
||||
```
|
||||
|
||||
## Running the smoke test
|
||||
|
||||
1. Run the smoke test workflow
|
||||
|
||||
```bash
|
||||
cd ./examples/smoke-test
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
2. Running the workflow requires you to send data to the local API.
|
||||
|
||||
You can use this cURL command to send a `user.created` event. This will run the workflow and generate the corresponding logs.
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url http://localhost:3000/api/v1/events \
|
||||
--header 'Authorization: Bearer trigger_dev_zC25mKNn6c0q' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "user.created",
|
||||
"payload": {
|
||||
"id": "123"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Dependency & Package graph
|
||||
|
||||

|
||||
|
||||
## Warp Launch Configuration
|
||||
|
||||
Setup a custom launch configuration for the Warp terminal ([docs here](https://docs.warp.dev/features/sessions/launch-configurations)) by copying the `.warp/triggerdotdev.yaml.example` file to `~/.warp/launch_configurations/triggerdotdev.yaml`. Make sure you edit the file and replace `<your-trigger-dev>` and `<your-pizzly-dev>` with your custom ngrok subdomains.
|
||||
@@ -0,0 +1,126 @@
|
||||
This guide covers installing Docker and Docker Compose. If you're looking for instructions for running Trigger.dev in docker, [see here](https://github.com/triggerdotdev/docker).
|
||||
|
||||
## Setting up Docker for the first time.
|
||||
|
||||
In the contributing guide of Trigger.dev, there's a section that requires you to start Docker.
|
||||
|
||||
If you don't have Docker installed on your machine, you'll run into some complications (errors).
|
||||
|
||||
Below are the steps on how you can avoid that.
|
||||
|
||||
First you need to setup docker-compose as it is an underlying tool that this command: `pnpm run docker` fires behind the scene.
|
||||
|
||||
## Linux
|
||||
|
||||
To install Docker Compose on Linux Ubuntu via the terminal, you can follow these steps:
|
||||
|
||||
1. Update the package index on your system by running the following command:
|
||||
|
||||
```shell
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
2. Install the required dependencies by running the following command:
|
||||
|
||||
```shell
|
||||
sudo apt install curl
|
||||
```
|
||||
|
||||
3. Download the Docker Compose binary into the `/usr/local/bin` directory using the `curl` command:
|
||||
|
||||
```shell
|
||||
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
4. Set the appropriate permissions to make the `docker-compose` binary executable:
|
||||
|
||||
```shell
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
5. Verify that Docker Compose has been successfully installed by running the following command:
|
||||
|
||||
```shell
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
This command should display the version information of Docker Compose without any errors.
|
||||
|
||||
After following these steps, you should have Docker Compose installed on your Ubuntu system, and you can use it by running `docker-compose` commands in the terminal.
|
||||
|
||||
When you've verified that the `docker-compose` package is installed and you proceed to start Docker with `pnpm run docker`.
|
||||
|
||||
You'll probably get an error similar to the one below:
|
||||
|
||||
```shell
|
||||
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
|
||||
ELIFECYCLE Command failed with exit code 1.
|
||||
```
|
||||
|
||||
The error message suggests that the Docker daemon is not running on your system. The Docker daemon is responsible for managing and running Docker containers.
|
||||
|
||||
To resolve this issue, you may need to install Docker properly on your Ubuntu system. Here are the steps to install Docker on Ubuntu:
|
||||
|
||||
1. Update the package index on your system by running the following command:
|
||||
|
||||
```shell
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
2. Install the necessary packages to allow apt to use repositories over HTTPS:
|
||||
|
||||
```shell
|
||||
sudo apt install apt-transport-https ca-certificates curl software-properties-common
|
||||
```
|
||||
|
||||
3. Add the official Docker GPG key to your system by running the following command:
|
||||
|
||||
```shell
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
```
|
||||
|
||||
4. Add the Docker repository to the APT sources list:
|
||||
|
||||
```shell
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
```
|
||||
|
||||
5. Update the package index again:
|
||||
|
||||
```shell
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
6. Install Docker by running the following command:
|
||||
|
||||
```shell
|
||||
sudo apt install docker-ce docker-ce-cli containerd.io
|
||||
```
|
||||
|
||||
7. After the installation is complete, verify that Docker is installed correctly by running the following command:
|
||||
|
||||
```shell
|
||||
docker --version
|
||||
```
|
||||
|
||||
This command should display the version information of Docker without any errors.
|
||||
|
||||
Once Docker is installed and verified, you should be able to start the Docker daemon and run the `pnpm run docker` command without encountering any issues.
|
||||
|
||||
## Windows
|
||||
|
||||
1. Download the Docker Desktop installer from the Docker website: [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop)
|
||||
|
||||
2. Run the installer and follow the instructions to install Docker Desktop.
|
||||
|
||||
3. After installation, Docker Desktop should be running automatically.
|
||||
|
||||
## macOS
|
||||
|
||||
1. Download the Docker Desktop installer from the Docker website: [Docker Desktop for Mac](https://www.docker.com/products/docker-desktop)
|
||||
|
||||
2. Run the installer and follow the instructions to install Docker Desktop.
|
||||
|
||||
3. After installation, Docker Desktop should be running automatically.
|
||||
|
||||
Please note that the instructions provided above are for the most common scenarios. For specific versions or different distributions, it's always a good idea to consult the official Docker documentation for the respective operating systems.
|
||||
@@ -7,13 +7,15 @@
|
||||
|
||||
### The background jobs framework for Next.js
|
||||
|
||||
[Discord](https://discord.gg/bpVDSjcG) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
|
||||
[Discord](https://discord.gg/JtBAxBr2m3) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
|
||||
|
||||
[](https://twitter.com/triggerdotdev)
|
||||
[](https://github.com/triggerdotdev/trigger.dev)
|
||||
|
||||
</div>
|
||||
|
||||
# About Trigger.dev
|
||||
|
||||
Create long-running jobs directly in your codebase with features like API integrations, webhooks, scheduling and delays.
|
||||
|
||||
### Long running Jobs on serverless
|
||||
@@ -60,46 +62,4 @@ We provide an official trigger.dev docker image you can use to easily self-host
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
- [Node.js](https://nodejs.org/en) version >=18.x
|
||||
- [pnpm package manager](https://pnpm.io/installation) version 7
|
||||
- [Docker](https://www.docker.com/get-started/)
|
||||
|
||||
### Setup
|
||||
|
||||
1. Clone the repo into a public GitHub repository or [fork the repo](https://github.com/triggerdotdev/trigger.dev/fork). If you plan to distribute the code, keep the source code public to comply with the [Apache Licence 2.0](https://github.com/triggerdotdev/trigger.dev/blob/main/LICENSE).
|
||||
|
||||
```
|
||||
git clone https://github.com/triggerdotdev/trigger.dev.git
|
||||
```
|
||||
> If you are on windows, run the following command on gitbash with admin privileges:
|
||||
> `git clone -c core.symlinks=true https://triggerdotdev/trigger.dev.git`
|
||||
2. Navigate to the project folder
|
||||
```
|
||||
cd trigger.dev
|
||||
```
|
||||
3. Install the required packages using pnpm.
|
||||
```
|
||||
pnpm i
|
||||
```
|
||||
4. Create your `.env` files
|
||||
```
|
||||
cp .env.example .env && cp packages/database/.env.example packages/database/.env
|
||||
```
|
||||
Open the root `.env` file and fill in the required values.
|
||||
6. Start Docker. This starts the required services like Postgres.
|
||||
```
|
||||
pnpm run docker
|
||||
```
|
||||
7. Migrate the database
|
||||
```
|
||||
pnpm run db:migrate
|
||||
```
|
||||
8. Run the seed script
|
||||
```
|
||||
pnpm run db:seed
|
||||
```
|
||||
5. Run the project. It should run on `port:3030`
|
||||
```
|
||||
pnpm run dev --filter webapp
|
||||
```
|
||||
To setup and develop locally or contribute to the open source project, follow our [developement guide](./CONTRIBUTING.md).
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"@remix-run/eslint-config",
|
||||
"@remix-run/eslint-config/node",
|
||||
"prettier"
|
||||
],
|
||||
"extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node", "prettier"],
|
||||
"rules": {
|
||||
"@typescript-eslint/strict-boolean-expressions": [
|
||||
"error",
|
||||
|
||||
@@ -15,4 +15,6 @@ node_modules
|
||||
build-storybook.log
|
||||
.out
|
||||
.storybook-out
|
||||
storybook-static
|
||||
storybook-static
|
||||
|
||||
/prisma/seed.js
|
||||
@@ -20,10 +20,7 @@ const config: StorybookConfig = {
|
||||
},
|
||||
};
|
||||
},
|
||||
stories: [
|
||||
"../app/**/stories/*.mdx",
|
||||
"../app/**/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
],
|
||||
stories: ["../app/**/stories/*.mdx", "../app/**/stories/*.stories.@(js|jsx|ts|tsx)"],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
|
||||
@@ -35,9 +35,7 @@ const preview: Preview = {
|
||||
|
||||
return (
|
||||
<OperatingSystemContextProvider platform="mac">
|
||||
<LocaleContextProvider
|
||||
locales={window.navigator.languages as string[]}
|
||||
>
|
||||
<LocaleContextProvider locales={window.navigator.languages as string[]}>
|
||||
<RemixStub initialEntries={["/"]} />
|
||||
</LocaleContextProvider>
|
||||
</OperatingSystemContextProvider>
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
export function DynamicTriggerIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 21 21"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<svg viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
export function ErrorIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="10" cy="10" r="7" fill="white" />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M18 10C18 14.4183 14.4183 18 10 18C5.58172 18 2 14.4183 2 10C2 5.58172 5.58172 2 10 2C14.4183 2 18 5.58172 18 10ZM10 5C10.4142 5 10.75 5.33579 10.75 5.75V10.25C10.75 10.6642 10.4142 11 10 11C9.58579 11 9.25 10.6642 9.25 10.25V5.75C9.25 5.33579 9.58579 5 10 5ZM10 15C10.5523 15 11 14.5523 11 14C11 13.4477 10.5523 13 10 13C9.44771 13 9 13.4477 9 14C9 14.5523 9.44771 15 10 15Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
export function WebhookIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
viewBox="0 0 190 180"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg className={className} viewBox="0 0 190 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
|
||||
@@ -16,10 +16,7 @@ export function ComingSoon({ title, description, icon }: ComingSoonProps) {
|
||||
<div className="flex flex-col justify-center rounded border border-slate-800 bg-slate-850">
|
||||
<div className="flex items-center gap-2 border-b border-slate-750 px-4 py-4">
|
||||
{typeof icon === "string" ? (
|
||||
<NamedIconInBox
|
||||
name={icon}
|
||||
className={"h-10 w-10 bg-midnight-800"}
|
||||
/>
|
||||
<NamedIconInBox name={icon} className={"h-10 w-10 bg-midnight-800"} />
|
||||
) : (
|
||||
icon
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
|
||||
import { LinkButton } from "./primitives/Buttons";
|
||||
import { Header1, Header3 } from "./primitives/Headers";
|
||||
import { friendlyErrorDisplay } from "~/utils/httpErrors";
|
||||
|
||||
type ErrorDisplayOptions = {
|
||||
button?: {
|
||||
title: string;
|
||||
to: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function RouteErrorDisplay(options?: ErrorDisplayOptions) {
|
||||
const error = useRouteError();
|
||||
|
||||
return (
|
||||
<>
|
||||
{isRouteErrorResponse(error) ? (
|
||||
<ErrorDisplay
|
||||
title={friendlyErrorDisplay(error.status, error.statusText).title}
|
||||
message={
|
||||
error.data.message ?? friendlyErrorDisplay(error.status, error.statusText).message
|
||||
}
|
||||
{...options}
|
||||
/>
|
||||
) : error instanceof Error ? (
|
||||
<ErrorDisplay title={error.name} message={error.message} {...options} />
|
||||
) : (
|
||||
<ErrorDisplay title="Oops" message={JSON.stringify(error)} {...options} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type DisplayOptionsProps = {
|
||||
title: string;
|
||||
message?: string;
|
||||
} & ErrorDisplayOptions;
|
||||
|
||||
export function ErrorDisplay({ title, message, button }: DisplayOptionsProps) {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<Header1 className="mb-4 border-b border-slate-800 pb-4">{title}</Header1>
|
||||
{message && <Header3>{message}</Header3>}
|
||||
<LinkButton to={button ? button.to : "/"} variant="primary/medium" className="mt-8">
|
||||
{button ? button.title : "Home"}
|
||||
</LinkButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { ChatBubbleLeftRightIcon } from "@heroicons/react/24/solid";
|
||||
import {
|
||||
Form,
|
||||
useActionData,
|
||||
useLocation,
|
||||
useNavigation,
|
||||
} from "@remix-run/react";
|
||||
import { useState } from "react";
|
||||
import { feedbackTypeLabel, schema } from "~/routes/resources.feedback";
|
||||
import { Form, useActionData, useLocation, useNavigation } from "@remix-run/react";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { FeedbackType, feedbackTypeLabel, schema } from "~/routes/resources.feedback";
|
||||
import { Button } from "./primitives/Buttons";
|
||||
import { Fieldset } from "./primitives/Fieldset";
|
||||
import { FormButtons } from "./primitives/FormButtons";
|
||||
@@ -24,16 +18,15 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "./primitives/Select";
|
||||
import {
|
||||
Sheet,
|
||||
SheetBody,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTrigger,
|
||||
} from "./primitives/Sheet";
|
||||
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "./primitives/Sheet";
|
||||
import { TextArea } from "./primitives/TextArea";
|
||||
|
||||
export function Feedback() {
|
||||
type FeedbackProps = {
|
||||
button: ReactNode;
|
||||
defaultValue?: FeedbackType;
|
||||
};
|
||||
|
||||
export function Feedback({ button, defaultValue = "bug" }: FeedbackProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
const lastSubmission = useActionData();
|
||||
@@ -58,31 +51,21 @@ export function Feedback() {
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild={true}>
|
||||
<Button
|
||||
variant="secondary/small"
|
||||
LeadingIcon={ChatBubbleLeftRightIcon}
|
||||
shortcut={{ key: "f" }}
|
||||
>
|
||||
Send us feedback
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetTrigger asChild={true}>{button}</SheetTrigger>
|
||||
<SheetContent size="sm">
|
||||
<SheetHeader className="justify-between">Give us feedback</SheetHeader>
|
||||
<SheetHeader className="justify-between">Help & feedback</SheetHeader>
|
||||
<SheetBody>
|
||||
<Paragraph variant="small" className="mb-4">
|
||||
We'd love to hear your feedback, good, bad or ugly.
|
||||
Use this form to ask for help or give us feedback. We read every message and will get
|
||||
back to you as soon as we can.
|
||||
</Paragraph>
|
||||
<Form method="post" action="/resources/feedback" {...form.props}>
|
||||
<Fieldset>
|
||||
<input
|
||||
value={location.pathname}
|
||||
{...conform.input(path, { type: "hidden" })}
|
||||
/>
|
||||
<input value={location.pathname} {...conform.input(path, { type: "hidden" })} />
|
||||
<InputGroup>
|
||||
<Label>What kind of feedback do you have?</Label>
|
||||
<Label>How can we help?</Label>
|
||||
<SelectGroup>
|
||||
<Select {...conform.input(feedbackType)} defaultValue={"bug"}>
|
||||
<Select {...conform.input(feedbackType)} defaultValue={defaultValue}>
|
||||
<SelectTrigger size="medium" width="full">
|
||||
<SelectValue placeholder="Type" />
|
||||
</SelectTrigger>
|
||||
@@ -95,9 +78,7 @@ export function Feedback() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</SelectGroup>
|
||||
<FormError id={feedbackType.errorId}>
|
||||
{feedbackType.error}
|
||||
</FormError>
|
||||
<FormError id={feedbackType.errorId}>{feedbackType.error}</FormError>
|
||||
</InputGroup>
|
||||
<InputGroup>
|
||||
<Label>Message</Label>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { H, HighlightOptions } from "highlight.run";
|
||||
import { useEffect } from "react";
|
||||
|
||||
interface Props extends HighlightOptions {
|
||||
projectId?: string;
|
||||
}
|
||||
|
||||
export function HighlightInit({ projectId, ...highlightOptions }: Props) {
|
||||
useEffect(() => {
|
||||
projectId && H.init(projectId, highlightOptions);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -2,17 +2,12 @@ import { Form } from "@remix-run/react";
|
||||
import { Paragraph } from "./primitives/Paragraph";
|
||||
import { Button } from "./primitives/Buttons";
|
||||
|
||||
export function ImpersonationBanner({
|
||||
impersonationId,
|
||||
}: {
|
||||
impersonationId: string;
|
||||
}) {
|
||||
export function ImpersonationBanner({ impersonationId }: { impersonationId: string }) {
|
||||
return (
|
||||
<div className="grid h-8 grid-cols-3 items-center bg-gradient-to-r from-amber-500 via-amber-400 to-amber-500 px-4 text-center">
|
||||
<span></span>
|
||||
<Paragraph variant="small">
|
||||
You are impersonating{" "}
|
||||
<span className="font-semibold">{impersonationId}</span>
|
||||
You are impersonating <span className="font-semibold">{impersonationId}</span>
|
||||
</Paragraph>
|
||||
<Form
|
||||
action="/resources/impersonation"
|
||||
|
||||
@@ -10,9 +10,7 @@ export function NoMobileOverlay() {
|
||||
<div className="absolute mx-8 flex flex-col items-center gap-6 rounded-lg bg-slate-850 px-8 py-10 text-center">
|
||||
<XMarkIcon className="absolute top-14 h-8 w-8 text-rose-600" />
|
||||
<DevicePhoneMobileIcon className="h-16 w-16 text-slate-500" />
|
||||
<Paragraph>
|
||||
Trigger.dev is currently only available on desktop.
|
||||
</Paragraph>
|
||||
<Paragraph>Trigger.dev is currently only available on desktop.</Paragraph>
|
||||
<LinkButton to="https://trigger.dev" variant="primary/medium">
|
||||
Back Home
|
||||
</LinkButton>
|
||||
|
||||
@@ -4,13 +4,7 @@ import { cn } from "~/utils/cn";
|
||||
|
||||
export function UserProfilePhoto({ className }: { className?: string }) {
|
||||
const user = useOptionalUser();
|
||||
return (
|
||||
<UserAvatar
|
||||
avatarUrl={user?.avatarUrl}
|
||||
name={user?.name}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
return <UserAvatar avatarUrl={user?.avatarUrl} name={user?.name} className={className} />;
|
||||
}
|
||||
|
||||
export function UserAvatar({
|
||||
@@ -23,9 +17,9 @@ export function UserAvatar({
|
||||
className?: string;
|
||||
}) {
|
||||
return avatarUrl ? (
|
||||
<div>
|
||||
<div className={cn("grid aspect-square place-items-center", className)}>
|
||||
<img
|
||||
className={cn("aspect-square rounded-full p-[7%]", className)}
|
||||
className={cn("aspect-square rounded-full p-[7%]")}
|
||||
src={avatarUrl}
|
||||
alt={name ?? "User"}
|
||||
/>
|
||||
|
||||
@@ -2,16 +2,8 @@ import type { Language, PrismTheme } from "prism-react-renderer";
|
||||
import Highlight, { defaultProps } from "prism-react-renderer";
|
||||
import { forwardRef, useCallback, useState } from "react";
|
||||
import { cn } from "~/utils/cn";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "../primitives/Tooltip";
|
||||
import {
|
||||
ClipboardDocumentCheckIcon,
|
||||
ClipboardIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
|
||||
import { ClipboardDocumentCheckIcon, ClipboardIcon } from "@heroicons/react/24/solid";
|
||||
import { Clipboard, ClipboardCheck, ClipboardCheckIcon } from "lucide-react";
|
||||
|
||||
//This is a fork of https://github.com/mantinedev/mantine/blob/master/src/mantine-prism/src/Prism/Prism.tsx
|
||||
@@ -193,9 +185,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
const maxLineWidth = lineCount.toString().length;
|
||||
let maxHeight: string | undefined = undefined;
|
||||
if (maxLines && lineCount > maxLines) {
|
||||
maxHeight = `calc(${
|
||||
(maxLines + extraLinesWhenClipping) * 0.75 * 1.625
|
||||
}rem + 1.5rem )`;
|
||||
maxHeight = `calc(${(maxLines + extraLinesWhenClipping) * 0.75 * 1.625}rem + 1.5rem )`;
|
||||
}
|
||||
|
||||
const highlightLines = highlightedRanges?.flatMap(([start, end]) =>
|
||||
@@ -204,10 +194,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative overflow-hidden rounded-md border border-slate-800",
|
||||
className
|
||||
)}
|
||||
className={cn("relative overflow-hidden rounded-md border border-slate-800", className)}
|
||||
style={{
|
||||
backgroundColor: theme.plain.backgroundColor,
|
||||
}}
|
||||
@@ -226,9 +213,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
className={cn(
|
||||
"absolute right-3 z-50 transition-colors duration-100 hover:cursor-pointer",
|
||||
showChrome ? "top-10" : "top-3",
|
||||
copied
|
||||
? "text-emerald-500"
|
||||
: "text-slate-500 hover:text-slate-300"
|
||||
copied ? "text-emerald-500" : "text-slate-500 hover:text-slate-300"
|
||||
)}
|
||||
>
|
||||
{copied ? (
|
||||
@@ -244,12 +229,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<Highlight
|
||||
{...defaultProps}
|
||||
theme={theme}
|
||||
code={code}
|
||||
language={language}
|
||||
>
|
||||
<Highlight {...defaultProps} theme={theme} code={code} language={language}>
|
||||
{({
|
||||
className: inheritedClassName,
|
||||
style: inheritedStyle,
|
||||
@@ -285,15 +265,10 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
const lineNumber = index + 1;
|
||||
const lineProps = getLineProps({ line, key: index });
|
||||
|
||||
let hasAnyHighlights = highlightLines
|
||||
? highlightLines.length > 0
|
||||
: false;
|
||||
let hasAnyHighlights = highlightLines ? highlightLines.length > 0 : false;
|
||||
|
||||
let shouldDim = hasAnyHighlights;
|
||||
if (
|
||||
hasAnyHighlights &&
|
||||
highlightLines?.includes(lineNumber)
|
||||
) {
|
||||
if (hasAnyHighlights && highlightLines?.includes(lineNumber)) {
|
||||
shouldDim = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { json as jsonLang } from "@codemirror/lang-json";
|
||||
import type { ViewUpdate } from "@codemirror/view";
|
||||
import type {
|
||||
ReactCodeMirrorProps,
|
||||
UseCodeMirror,
|
||||
} from "@uiw/react-codemirror";
|
||||
import type { ReactCodeMirrorProps, UseCodeMirror } from "@uiw/react-codemirror";
|
||||
import { useCodeMirror } from "@uiw/react-codemirror";
|
||||
import { useRef, useEffect } from "react";
|
||||
import { getEditorSetup } from "./codeMirrorSetup";
|
||||
|
||||
@@ -30,10 +30,7 @@ export function getViewerSetup(): Array<Extension> {
|
||||
return [drawSelection(), dropCursor(), bracketMatching(), lineNumbers()];
|
||||
}
|
||||
|
||||
export function getEditorSetup(
|
||||
showLineNumbers = true,
|
||||
showHighlights = true
|
||||
): Array<Extension> {
|
||||
export function getEditorSetup(showLineNumbers = true, showHighlights = true): Array<Extension> {
|
||||
const options = [
|
||||
drawSelection(),
|
||||
dropCursor(),
|
||||
|
||||
@@ -36,8 +36,9 @@ export function darkTheme(): Extension {
|
||||
},
|
||||
|
||||
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
|
||||
{ backgroundColor: selection },
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
|
||||
backgroundColor: selection,
|
||||
},
|
||||
|
||||
".cm-panels": { backgroundColor: darkBackground, color: ivory },
|
||||
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
|
||||
@@ -101,13 +102,7 @@ export function darkTheme(): Extension {
|
||||
const jsonHeroHighlightStyle = HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: violet },
|
||||
{
|
||||
tag: [
|
||||
tags.name,
|
||||
tags.deleted,
|
||||
tags.character,
|
||||
tags.propertyName,
|
||||
tags.macroName,
|
||||
],
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
color: coral,
|
||||
},
|
||||
{ tag: [tags.function(tags.variableName), tags.labelName], color: malibu },
|
||||
@@ -194,8 +189,9 @@ export function lightTheme(): Extension[] {
|
||||
},
|
||||
|
||||
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
|
||||
{ backgroundColor: selection },
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
|
||||
backgroundColor: selection,
|
||||
},
|
||||
|
||||
".cm-panels": { backgroundColor: darkBackground, color: ivory },
|
||||
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
|
||||
@@ -259,13 +255,7 @@ export function lightTheme(): Extension[] {
|
||||
const jsonHeroHighlightStyle = tagHighlighter([
|
||||
{ tag: tags.keyword, class: violet },
|
||||
{
|
||||
tag: [
|
||||
tags.name,
|
||||
tags.deleted,
|
||||
tags.character,
|
||||
tags.propertyName,
|
||||
tags.macroName,
|
||||
],
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
class: variableColor,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
import { Paragraph, TextLink } from "~/components/primitives/Paragraph";
|
||||
import { Link } from "@remix-run/react";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { StepNumber } from "~/components/primitives/StepNumber";
|
||||
import { useAppOrigin } from "~/hooks/useAppOrigin";
|
||||
import { useDevEnvironment } from "~/hooks/useEnvironments";
|
||||
import { useJob } from "~/hooks/useJob";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { IntegrationIcon } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route";
|
||||
import { Button } from "../primitives/Buttons";
|
||||
import { Callout } from "../primitives/Callout";
|
||||
import { jobTestPath } from "~/utils/pathBuilder";
|
||||
import { CodeBlock } from "../code/CodeBlock";
|
||||
import { InlineCode } from "../code/InlineCode";
|
||||
import { EnvironmentLabel } from "../environments/EnvironmentLabel";
|
||||
import { HelpPanelProps } from "../integrations/ApiKeyHelp";
|
||||
import { HelpInstall } from "../integrations/HelpInstall";
|
||||
import { HelpSamples } from "../integrations/HelpSamples";
|
||||
import { Button, LinkButton } from "../primitives/Buttons";
|
||||
import { Callout, variantClasses } from "../primitives/Callout";
|
||||
import {
|
||||
ClientTabs,
|
||||
ClientTabsContent,
|
||||
@@ -12,25 +23,19 @@ import {
|
||||
ClientTabsTrigger,
|
||||
} from "../primitives/ClientTabs";
|
||||
import { ClipboardField } from "../primitives/ClipboardField";
|
||||
import { Header2 } from "../primitives/Headers";
|
||||
import { TextLink } from "../primitives/TextLink";
|
||||
import integrationButton from "./integration-button.png";
|
||||
import selectEnvironment from "./select-environment.png";
|
||||
import selectExample from "./select-example.png";
|
||||
import { InlineCode } from "../code/InlineCode";
|
||||
import { ApiKeyHelp, HelpPanelProps } from "../integrations/ApiKeyHelp";
|
||||
import { CodeBlock } from "../code/CodeBlock";
|
||||
import { HelpInstall } from "../integrations/HelpInstall";
|
||||
import { HelpSamples } from "../integrations/HelpSamples";
|
||||
|
||||
export function HowToSetupYourProject() {
|
||||
const devEnvironment = useDevEnvironment();
|
||||
const appOrigin = useAppOrigin();
|
||||
return (
|
||||
<>
|
||||
<StepNumber stepNumber="1" title="Run the CLI init command" />
|
||||
<StepNumber stepNumber="1" title="Run the CLI 'init' command in a Next.js project" />
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
Run this CLI command in a terminal window from your Next.js project.
|
||||
</Paragraph>
|
||||
<ClientTabs defaultValue="npm">
|
||||
<ClientTabsList>
|
||||
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
|
||||
@@ -57,40 +62,41 @@ export function HowToSetupYourProject() {
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
className="mb-4"
|
||||
secure={`yarn @trigger.dev/cli@latest init -k ••••••••• -t ${appOrigin}`}
|
||||
value={`yarn @trigger.dev/cli@latest init -k ${devEnvironment?.apiKey} -t ${appOrigin}`}
|
||||
secure={`yarn dlx @trigger.dev/cli@latest init -k ••••••••• -t ${appOrigin}`}
|
||||
value={`yarn dlx @trigger.dev/cli@latest init -k ${devEnvironment?.apiKey} -t ${appOrigin}`}
|
||||
/>
|
||||
</ClientTabsContent>
|
||||
</ClientTabs>
|
||||
<Paragraph spacing variant="small">
|
||||
It will ask you for a "unique ID for your endpoint". You can use the
|
||||
default by hitting enter.
|
||||
|
||||
<Paragraph spacing>
|
||||
You’ll notice a new folder in your project called 'jobs'. We’ve added a very simple
|
||||
example Job in <InlineCode>examples.ts</InlineCode> to help you get started.
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="2" title="Run your Next.js app" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
Ensure your Next.js app is running locally on port 3000.
|
||||
</Paragraph>
|
||||
<Paragraph>Ensure your app is running locally.</Paragraph>
|
||||
<ClientTabs defaultValue="npm">
|
||||
<ClientTabsList>
|
||||
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
|
||||
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
|
||||
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
|
||||
</ClientTabsList>
|
||||
<ClientTabsContent value={"npm"}>
|
||||
<ClipboardField variant="primary/medium" className="mb-4" value={`npm run dev`} />
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"pnpm"}>
|
||||
<ClipboardField variant="primary/medium" className="mb-4" value={`pnpm run dev`} />
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"yarn"}>
|
||||
<ClipboardField variant="primary/medium" className="mb-4" value={`yarn run dev`} />
|
||||
</ClientTabsContent>
|
||||
</ClientTabs>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="3" title="Run the CLI dev command" />
|
||||
<StepNumber stepNumber="3" title="Run the CLI 'dev' command" />
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
The CLI <InlineCode>dev</InlineCode> command allows the Trigger.dev
|
||||
service to send messages to your Next.js site. This is required for
|
||||
registering Jobs, triggering them and running Tasks. To achieve this
|
||||
it creates a tunnel (using{" "}
|
||||
<TextLink href="https://ngrok.com/">ngrok</TextLink>) so Trigger.dev
|
||||
can send messages to your machine.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
You should leave the <InlineCode>dev</InlineCode> command running when
|
||||
you're developing.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
In a{" "}
|
||||
<strong className="text-bright">new terminal window or tab</strong>{" "}
|
||||
run:
|
||||
In a <strong className="text-bright">separate terminal window or tab</strong> run:
|
||||
</Paragraph>
|
||||
<ClientTabs defaultValue="npm">
|
||||
<ClientTabsList>
|
||||
@@ -116,16 +122,22 @@ export function HowToSetupYourProject() {
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
className="mb-4"
|
||||
value={`yarn @trigger.dev/cli@latest dev`}
|
||||
value={`yarn dlx @trigger.dev/cli@latest dev`}
|
||||
/>
|
||||
</ClientTabsContent>
|
||||
</ClientTabs>
|
||||
<Paragraph spacing variant="small">
|
||||
If you’re not running on port 3000 you can specify the port by adding{" "}
|
||||
<InlineCode>--port 3001</InlineCode> to the end.
|
||||
</Paragraph>
|
||||
<Paragraph spacing variant="small">
|
||||
You should leave the <InlineCode>dev</InlineCode> command running when you're developing.
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="4" title="Check for Jobs" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
Once you've run the CLI command, click Refresh to view your example
|
||||
Job in the list.
|
||||
Once you've run the CLI command, click Refresh to view your example Job in the list.
|
||||
</Paragraph>
|
||||
<Button
|
||||
variant="primary/medium"
|
||||
@@ -140,6 +152,64 @@ export function HowToSetupYourProject() {
|
||||
);
|
||||
}
|
||||
|
||||
export function HowToRunYourJob() {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
const job = useJob();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Callout variant="info" className="mb-6">
|
||||
<Paragraph variant={"small"} className={variantClasses.info.textColor}>
|
||||
Scheduled Triggers <strong>do not</strong> trigger Jobs in the DEV Environment. When
|
||||
developing locally you should use the{" "}
|
||||
<Link
|
||||
to={jobTestPath(organization, project, job)}
|
||||
className="underline underline-offset-2 transition hover:text-blue-100"
|
||||
>
|
||||
Test feature
|
||||
</Link>{" "}
|
||||
to trigger any scheduled Jobs.
|
||||
</Paragraph>
|
||||
</Callout>
|
||||
|
||||
<Paragraph spacing>There are two ways to run your Job:</Paragraph>
|
||||
|
||||
<StepNumber stepNumber="1" title="Trigger a test Run" />
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
You can perform a Run with any payload you want, or use one of our examples, on the test
|
||||
page.
|
||||
</Paragraph>
|
||||
<LinkButton
|
||||
to={jobTestPath(organization, project, job)}
|
||||
variant={"primary/small"}
|
||||
LeadingIcon={"beaker"}
|
||||
leadingIconClassName="text-bright"
|
||||
>
|
||||
Test
|
||||
</LinkButton>
|
||||
</StepContentContainer>
|
||||
|
||||
<StepNumber stepNumber="2" title="Trigger your Job for real" />
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
Performing a real run depends on the type of Trigger your Job is using.
|
||||
</Paragraph>
|
||||
|
||||
<LinkButton
|
||||
to="https://trigger.dev/docs/documentation/guides/running-jobs"
|
||||
variant={"primary/small"}
|
||||
LeadingIcon={"docs"}
|
||||
leadingIconClassName="text-bright"
|
||||
>
|
||||
How to run a Job
|
||||
</LinkButton>
|
||||
</StepContentContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function HowToRunATest() {
|
||||
return (
|
||||
<>
|
||||
@@ -149,30 +219,22 @@ export function HowToRunATest() {
|
||||
"
|
||||
/>
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
Select the environment you’d like the test to run against.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>Select the environment you’d like the test to run against.</Paragraph>
|
||||
<img src={selectEnvironment} className="mt-2 w-52" />
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="2" title="Write your test payload" />
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
Write your own payload specific to your Job. Some Triggers also
|
||||
provide example payloads that you can select from. This will populate
|
||||
the code editor below.
|
||||
Write your own payload specific to your Job. Some Triggers also provide example payloads
|
||||
that you can select from. This will populate the code editor below.
|
||||
</Paragraph>
|
||||
<img src={selectExample} className="mt-2 h-40" />
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="3" title="Run your test" />
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
When you’re happy with the payload, click Run test.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>When you’re happy with the payload, click Run test.</Paragraph>
|
||||
</StepContentContainer>
|
||||
<Callout
|
||||
variant="docs"
|
||||
href="https://trigger.dev/docs/documentation/guides/testing-jobs"
|
||||
>
|
||||
<Callout variant="docs" to="https://trigger.dev/docs/documentation/guides/testing-jobs">
|
||||
Learn more about running tests.
|
||||
</Callout>
|
||||
</>
|
||||
@@ -186,53 +248,38 @@ export function HowToConnectAnIntegration() {
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
APIs marked with a
|
||||
<span
|
||||
className="mx-2 -mt-1 inline-flex"
|
||||
aria-label="Trigger.dev Integration icon"
|
||||
>
|
||||
<span className="mx-2 -mt-1 inline-flex" aria-label="Trigger.dev Integration icon">
|
||||
<IntegrationIcon />
|
||||
</span>
|
||||
are Trigger.dev Integrations. These Integrations make connecting to
|
||||
the API easier by offering OAuth or API key authentication. All APIs
|
||||
can also be used with fetch or an SDK.
|
||||
are Trigger.dev Integrations. These Integrations make connecting to the API easier by
|
||||
offering OAuth or API key authentication. All APIs can also be used with fetch or an SDK.
|
||||
</Paragraph>
|
||||
<img src={integrationButton} className="mt-2 h-10" />
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="2" title="Choose how you want to connect" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
Follow the instructions for your chosen connection method in the
|
||||
popover form. If no Integration exists yet, you can request one by
|
||||
clicking the "I want an Integration" button.
|
||||
Follow the instructions for your chosen connection method in the popover form. If no
|
||||
Integration exists yet, you can request one by clicking the "I want an Integration"
|
||||
button.
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<StepNumber
|
||||
stepNumber="3"
|
||||
title="Your connection will appear in the list"
|
||||
/>
|
||||
<StepNumber stepNumber="3" title="Your connection will appear in the list" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
Once you've connected your API, it will appear in the list of
|
||||
Integrations below. You can view details and manage your connection by
|
||||
selecting it from the table.
|
||||
Once you've connected your API, it will appear in the list of Integrations below. You can
|
||||
view details and manage your connection by selecting it from the table.
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<Callout
|
||||
variant={"docs"}
|
||||
href="https://trigger.dev/docs/integrations/introduction"
|
||||
>
|
||||
View the Integration docs page for more information on connecting an API
|
||||
using an Integration or other method.
|
||||
<Callout variant={"docs"} to="https://trigger.dev/docs/integrations/introduction">
|
||||
View the Integration docs page for more information on connecting an API using an
|
||||
Integration or other method.
|
||||
</Callout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function HowToUseThisIntegration({
|
||||
integration,
|
||||
help,
|
||||
integrationClient,
|
||||
}: HelpPanelProps) {
|
||||
export function HowToUseThisIntegration({ integration, help, integrationClient }: HelpPanelProps) {
|
||||
return (
|
||||
<>
|
||||
<StepNumber stepNumber="1" title="Install the package" />
|
||||
@@ -255,6 +302,92 @@ export function HowToUseThisIntegration({
|
||||
);
|
||||
}
|
||||
|
||||
export function HowToUseApiKeysAndEndpoints() {
|
||||
return (
|
||||
<>
|
||||
<Paragraph spacing>
|
||||
Environments and Endpoints are used to connect your server to the Trigger.dev platform.
|
||||
</Paragraph>
|
||||
<Header2 spacing>Environments</Header2>
|
||||
<Paragraph spacing>
|
||||
Each environment has API Keys associated with it. The Server API Key is used to authenticate
|
||||
your Jobs with the Trigger.dev platform.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
The Server API Key you use for your{" "}
|
||||
<TextLink to="https://trigger.dev/docs/documentation/concepts/client-adaptors">
|
||||
Client
|
||||
</TextLink>{" "}
|
||||
is how we know which environment to run your code against:
|
||||
</Paragraph>
|
||||
<CodeBlock
|
||||
showLineNumbers={false}
|
||||
className="mb-4"
|
||||
code={`export const client = new TriggerClient({
|
||||
id: "nextjs-example",
|
||||
//this environment variable should be set to your Server DEV API Key locally,
|
||||
//and your Server PROD API Key in production
|
||||
apiKey: process.env.TRIGGER_API_KEY!,
|
||||
});`}
|
||||
/>
|
||||
<StepNumber
|
||||
stepNumber="→"
|
||||
title={
|
||||
<span className="flex items-center gap-x-2">
|
||||
<span>Development</span>
|
||||
<EnvironmentLabel environment={{ type: "DEVELOPMENT" }} />
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
The <InlineCode>DEV</InlineCode> environment should only be used for local development.
|
||||
It’s where you can test your Jobs before deploying them to servers.
|
||||
</Paragraph>
|
||||
<Callout variant="warning" className="my-2">
|
||||
Scheduled Triggers do not trigger Jobs in the DEV Environment. When you’re working locally
|
||||
you should use the Test feature to trigger any scheduled Jobs.
|
||||
</Callout>
|
||||
</StepContentContainer>
|
||||
<StepNumber
|
||||
stepNumber="→"
|
||||
title={
|
||||
<span className="flex items-center gap-x-2">
|
||||
<span>Production</span>
|
||||
<EnvironmentLabel environment={{ type: "PRODUCTION" }} />
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<StepContentContainer>
|
||||
<Paragraph spacing>
|
||||
The <InlineCode>PROD</InlineCode> environment is where your Jobs will run in production.
|
||||
It’s where you can run your Jobs against real data.
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<Header2 spacing>Endpoints</Header2>
|
||||
<Paragraph spacing>
|
||||
An Endpoint is a URL on your server that Trigger.dev can connect to. This URL is used to
|
||||
register Jobs, start them and orchestrate runs and retries.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
<InlineCode>DEV</InlineCode> has multiple endpoints associated with it – one for each team
|
||||
member. This allows each team member to run their own Jobs, without interfering with each
|
||||
other.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
All other environments have just a single endpoint (with a single URL) associated with them.
|
||||
</Paragraph>
|
||||
<Header2 spacing>Deployment</Header2>
|
||||
<Paragraph spacing>
|
||||
Deployment uses Environments and Endpoints to connect your Jobs to the Trigger.dev platform.
|
||||
</Paragraph>
|
||||
<Callout variant="docs" to="https://trigger.dev/docs/documentation/guides/deployment">
|
||||
Read the deployment guide to learn more.
|
||||
</Callout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function StepContentContainer({ children }: { children: React.ReactNode }) {
|
||||
return <div className="mb-6 ml-9 mt-1">{children}</div>;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ import { Paragraph } from "../primitives/Paragraph";
|
||||
import { HelpInstall } from "./HelpInstall";
|
||||
import { HelpSamples, ReplacementData } from "./HelpSamples";
|
||||
|
||||
export type HelpPanelIntegration = Pick<
|
||||
Integration,
|
||||
"name" | "packageName" | "identifier"
|
||||
>;
|
||||
export type HelpPanelIntegration = Pick<Integration, "name" | "packageName" | "identifier">;
|
||||
|
||||
export type HelpPanelProps = {
|
||||
integration: HelpPanelIntegration;
|
||||
@@ -16,32 +13,21 @@ export type HelpPanelProps = {
|
||||
integrationClient?: ReplacementData;
|
||||
};
|
||||
|
||||
export function ApiKeyHelp({
|
||||
integration,
|
||||
help,
|
||||
integrationClient,
|
||||
}: HelpPanelProps) {
|
||||
export function ApiKeyHelp({ integration, help, integrationClient }: HelpPanelProps) {
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<Header1 className="mb-2">
|
||||
How to use {integration.name} with API keys
|
||||
</Header1>
|
||||
<Header1 className="mb-2">How to use {integration.name} with API keys</Header1>
|
||||
<Paragraph spacing>
|
||||
You can use API keys to authenticate with {integration.name}. Your API
|
||||
keys won't leave your server, we'll never see them.
|
||||
You can use API keys to authenticate with {integration.name}. Your API keys won't leave your
|
||||
server, we'll never see them.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
First install the{" "}
|
||||
<InlineCode>{integration.packageName}@next</InlineCode> package using
|
||||
your preferred package manager. For example:
|
||||
First install the <InlineCode>{integration.packageName}</InlineCode> package using your
|
||||
preferred package manager. For example:
|
||||
</Paragraph>
|
||||
<HelpInstall packageName={integration.packageName} />
|
||||
{help && (
|
||||
<HelpSamples
|
||||
help={help}
|
||||
integration={integration}
|
||||
integrationClient={integrationClient}
|
||||
/>
|
||||
<HelpSamples help={help} integration={integration} integrationClient={integrationClient} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
ApiAuthenticationMethodApiKey,
|
||||
Integration,
|
||||
} from "~/services/externalApis/types";
|
||||
import { ApiAuthenticationMethodApiKey, Integration } from "~/services/externalApis/types";
|
||||
import { docsIntegrationPath } from "~/utils/pathBuilder";
|
||||
import { LinkButton } from "../primitives/Buttons";
|
||||
import { Header1, Header2 } from "../primitives/Headers";
|
||||
import { NamedIconInBox } from "../primitives/NamedIcon";
|
||||
import { Paragraph } from "../primitives/Paragraph";
|
||||
import { RadioGroup, RadioGroupItem } from "../primitives/RadioButton";
|
||||
import {
|
||||
Sheet,
|
||||
SheetBody,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTrigger,
|
||||
} from "../primitives/Sheet";
|
||||
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../primitives/Sheet";
|
||||
import { ApiKeyHelp } from "./ApiKeyHelp";
|
||||
import { CustomHelp } from "./CustomHelp";
|
||||
import { SelectOAuthMethod } from "./SelectOAuthMethod";
|
||||
@@ -28,16 +19,18 @@ export function ConnectToIntegrationSheet({
|
||||
button,
|
||||
className,
|
||||
callbackUrl,
|
||||
icon,
|
||||
}: {
|
||||
integration: Integration;
|
||||
organizationId: string;
|
||||
button: React.ReactNode;
|
||||
callbackUrl: string;
|
||||
className?: string;
|
||||
icon?: string;
|
||||
}) {
|
||||
const [integrationMethod, setIntegrationMethod] = useState<
|
||||
IntegrationMethod | undefined
|
||||
>(undefined);
|
||||
const [integrationMethod, setIntegrationMethod] = useState<IntegrationMethod | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const authMethods = Object.values(integration.authenticationMethods);
|
||||
const hasApiKeyOption = authMethods.some((s) => s.type === "apikey");
|
||||
@@ -48,7 +41,7 @@ export function ConnectToIntegrationSheet({
|
||||
<SheetTrigger className={className}>{button}</SheetTrigger>
|
||||
<SheetContent size="lg" className="relative">
|
||||
<SheetHeader>
|
||||
<NamedIconInBox name={integration.identifier} className="h-9 w-9" />
|
||||
<NamedIconInBox name={icon ?? integration.identifier} className="h-9 w-9" />
|
||||
<div className="grow">
|
||||
<Header1>{integration.name}</Header1>
|
||||
{integration.description && (
|
||||
@@ -59,6 +52,7 @@ export function ConnectToIntegrationSheet({
|
||||
to={docsIntegrationPath(integration.identifier)}
|
||||
variant="secondary/small"
|
||||
LeadingIcon="docs"
|
||||
target="_blank"
|
||||
>
|
||||
View docs
|
||||
</LinkButton>
|
||||
|
||||
@@ -8,11 +8,7 @@ import simplur from "simplur";
|
||||
import { useFeatures } from "~/hooks/useFeatures";
|
||||
import { useTextFilter } from "~/hooks/useTextFilter";
|
||||
import { createSchema } from "~/routes/resources.connection.$organizationId.oauth2";
|
||||
import {
|
||||
ApiAuthenticationMethodOAuth2,
|
||||
Integration,
|
||||
Scope,
|
||||
} from "~/services/externalApis/types";
|
||||
import { ApiAuthenticationMethodOAuth2, Integration, Scope } from "~/services/externalApis/types";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { CodeBlock } from "../code/CodeBlock";
|
||||
import { Button } from "../primitives/Buttons";
|
||||
@@ -48,32 +44,22 @@ export function ConnectToOAuthForm({
|
||||
const fetcher = useFetcher();
|
||||
const { isManagedCloud } = useFeatures();
|
||||
|
||||
const [
|
||||
form,
|
||||
{
|
||||
title,
|
||||
slug,
|
||||
scopes,
|
||||
hasCustomClient,
|
||||
customClientId,
|
||||
customClientSecret,
|
||||
},
|
||||
] = useForm({
|
||||
lastSubmission: fetcher.data,
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
// Create the schema without any constraint defined
|
||||
schema: createSchema(),
|
||||
});
|
||||
},
|
||||
});
|
||||
const [form, { title, slug, scopes, hasCustomClient, customClientId, customClientSecret }] =
|
||||
useForm({
|
||||
lastSubmission: fetcher.data,
|
||||
shouldRevalidate: "onSubmit",
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
// Create the schema without any constraint defined
|
||||
schema: createSchema(),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
const [selectedScopes, setSelectedScopes] = useState<Set<string>>(
|
||||
new Set(
|
||||
authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name)
|
||||
)
|
||||
new Set(authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name))
|
||||
);
|
||||
|
||||
const requiresCustomOAuthApp = clientType === "EXTERNAL" || !isManagedCloud;
|
||||
@@ -84,10 +70,7 @@ export function ConnectToOAuthForm({
|
||||
items: authMethod.scopes,
|
||||
filter: (scope, text) => {
|
||||
if (scope.name.toLowerCase().includes(text.toLowerCase())) return true;
|
||||
if (
|
||||
scope.description &&
|
||||
scope.description.toLowerCase().includes(text.toLowerCase())
|
||||
)
|
||||
if (scope.description && scope.description.toLowerCase().includes(text.toLowerCase()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -103,16 +86,8 @@ export function ConnectToOAuthForm({
|
||||
>
|
||||
<Fieldset>
|
||||
<input type="hidden" name="id" value={id} />
|
||||
<input
|
||||
type="hidden"
|
||||
name="integrationIdentifier"
|
||||
value={integration.identifier}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="integrationAuthMethod"
|
||||
value={authMethodKey}
|
||||
/>
|
||||
<input type="hidden" name="integrationIdentifier" value={integration.identifier} />
|
||||
<input type="hidden" name="integrationAuthMethod" value={authMethodKey} />
|
||||
<input type="hidden" name="redirectTo" value={location.pathname} />
|
||||
<InputGroup>
|
||||
<FormError>{form.error}</FormError>
|
||||
@@ -126,8 +101,8 @@ export function ConnectToOAuthForm({
|
||||
placeholder={`e.g. ${integration.identifier}`}
|
||||
/>
|
||||
<Hint>
|
||||
This is used in your code to reference this connection. It must be
|
||||
unique for this project.
|
||||
This is used in your code to reference this connection. It must be unique for this
|
||||
project.
|
||||
</Hint>
|
||||
<FormError>{slug.error}</FormError>
|
||||
</InputGroup>
|
||||
@@ -145,8 +120,7 @@ export function ConnectToOAuthForm({
|
||||
<div>
|
||||
<Header2>Use my OAuth App</Header2>
|
||||
<Paragraph variant="small" className="mb-2">
|
||||
To use your own OAuth app, check the option below and insert the
|
||||
details.
|
||||
To use your own OAuth app, check the option below and insert the details.
|
||||
</Paragraph>
|
||||
<Checkbox
|
||||
id="hasCustomClient"
|
||||
@@ -160,17 +134,13 @@ export function ConnectToOAuthForm({
|
||||
{useMyOAuthApp && (
|
||||
<div className="ml-6 mt-2">
|
||||
<Paragraph variant="small" className="mb-2">
|
||||
Set the callback url to{" "}
|
||||
<CodeBlock code={callbackUrl} showLineNumbers={false} />
|
||||
Set the callback url to <CodeBlock code={callbackUrl} showLineNumbers={false} />
|
||||
</Paragraph>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex gap-2">
|
||||
<InputGroup fullWidth>
|
||||
<Label variant="small">Client ID</Label>
|
||||
<Input
|
||||
fullWidth
|
||||
{...conform.input(customClientId, { type: "text" })}
|
||||
/>
|
||||
<Input fullWidth {...conform.input(customClientId, { type: "text" })} />
|
||||
</InputGroup>
|
||||
<InputGroup fullWidth>
|
||||
<Label variant="small">Client secret</Label>
|
||||
@@ -187,15 +157,15 @@ export function ConnectToOAuthForm({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Header2>Scopes</Header2>
|
||||
<Paragraph variant="small" className="mb-4">
|
||||
Select the scopes you want to grant to {integration.name} in order
|
||||
for it to access your data. Note: If you try and perform an action
|
||||
in a Job that requires a scope you haven’t granted, that task will
|
||||
fail.
|
||||
</Paragraph>
|
||||
{/* <Header3 className="mb-2">
|
||||
{authMethod.scopes.length > 0 && (
|
||||
<div>
|
||||
<Header2>Scopes</Header2>
|
||||
<Paragraph variant="small" className="mb-4">
|
||||
Select the scopes you want to grant to {integration.name} in order for it to access
|
||||
your data. Note: If you try and perform an action in a Job that requires a scope you
|
||||
haven’t granted, that task will fail.
|
||||
</Paragraph>
|
||||
{/* <Header3 className="mb-2">
|
||||
Select from popular scope collections
|
||||
</Header3>
|
||||
<fieldset>
|
||||
@@ -205,60 +175,59 @@ export function ConnectToOAuthForm({
|
||||
variant="button/small"
|
||||
/>
|
||||
</fieldset> */}
|
||||
<div className="mb-2 mt-4 flex items-center justify-between">
|
||||
<Header3>Select {integration.name} scopes</Header3>
|
||||
<Paragraph variant="small" className="text-slate-500">
|
||||
{simplur`${selectedScopes.size} scope[|s] selected`}
|
||||
</Paragraph>
|
||||
</div>
|
||||
<Input
|
||||
placeholder="Search scopes"
|
||||
className="mb-2"
|
||||
variant="medium"
|
||||
icon="search"
|
||||
fullWidth={true}
|
||||
value={filterText}
|
||||
onChange={(e) => setFilterText(e.target.value)}
|
||||
/>
|
||||
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
|
||||
{filteredItems.length === 0 && (
|
||||
<Paragraph variant="small" className="p-4">
|
||||
No scopes match {filterText}. Try a different search query.
|
||||
<div className="mb-2 mt-4 flex items-center justify-between">
|
||||
<Header3>Select {integration.name} scopes</Header3>
|
||||
<Paragraph variant="small" className="text-slate-500">
|
||||
{simplur`${selectedScopes.size} scope[|s] selected`}
|
||||
</Paragraph>
|
||||
)}
|
||||
{authMethod.scopes.map((s) => {
|
||||
return (
|
||||
<Checkbox
|
||||
key={s.name}
|
||||
id={s.name}
|
||||
value={s.name}
|
||||
name="scopes"
|
||||
label={s.name}
|
||||
defaultChecked={s.defaultChecked ?? false}
|
||||
badges={s.annotations?.map((a) => a.label)}
|
||||
description={s.description}
|
||||
variant="description"
|
||||
className={cn(
|
||||
filteredItems.find((f) => f.name === s.name) ? "" : "hidden"
|
||||
)}
|
||||
onChange={(isChecked) => {
|
||||
if (isChecked) {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.add(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
} else {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.delete(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Input
|
||||
placeholder="Search scopes"
|
||||
className="mb-2"
|
||||
variant="medium"
|
||||
icon="search"
|
||||
fullWidth={true}
|
||||
value={filterText}
|
||||
onChange={(e) => setFilterText(e.target.value)}
|
||||
/>
|
||||
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
|
||||
{filteredItems.length === 0 && (
|
||||
<Paragraph variant="small" className="p-4">
|
||||
No scopes match {filterText}. Try a different search query.
|
||||
</Paragraph>
|
||||
)}
|
||||
{authMethod.scopes.map((s) => {
|
||||
return (
|
||||
<Checkbox
|
||||
key={s.name}
|
||||
id={s.name}
|
||||
value={s.name}
|
||||
name="scopes"
|
||||
label={s.name}
|
||||
defaultChecked={s.defaultChecked ?? false}
|
||||
badges={s.annotations?.map((a) => a.label)}
|
||||
description={s.description}
|
||||
variant="description"
|
||||
className={cn(filteredItems.find((f) => f.name === s.name) ? "" : "hidden")}
|
||||
onChange={(isChecked) => {
|
||||
if (isChecked) {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.add(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
} else {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.delete(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Fieldset>
|
||||
|
||||
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
|
||||
@@ -268,7 +237,7 @@ export function ConnectToOAuthForm({
|
||||
className="flex gap-2"
|
||||
disabled={transition.state !== "idle"}
|
||||
variant="primary/medium"
|
||||
LeadingIcon={integration.identifier}
|
||||
LeadingIcon={integration.icon ?? integration.identifier}
|
||||
>
|
||||
Connect to {integration.name}
|
||||
</Button>
|
||||
|
||||
@@ -5,20 +5,16 @@ import { Paragraph } from "../primitives/Paragraph";
|
||||
export function CustomHelp({ name }: { name: string }) {
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<Header1 className="mb-2">
|
||||
You can use any API with requests or an SDK
|
||||
</Header1>
|
||||
<Header1 className="mb-2">You can use any API with requests or an SDK</Header1>
|
||||
<Header2 className="mb-2">How to use an SDK</Header2>
|
||||
<Paragraph spacing>
|
||||
You can call SDK methods from inside the run function, but you should
|
||||
wrap them in a Task to make sure they're resumable.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>
|
||||
Here's an example with the official GitHub SDK
|
||||
You can call SDK methods from inside the run function, but you should wrap them in a Task to
|
||||
make sure they're resumable.
|
||||
</Paragraph>
|
||||
<Paragraph spacing>Here's an example with the official GitHub SDK</Paragraph>
|
||||
<CodeBlock
|
||||
code={`
|
||||
new Job(client, {
|
||||
client.defineJob({
|
||||
id: "scheduled-job-1",
|
||||
name: "Scheduled Job 1",
|
||||
version: "0.1.1",
|
||||
@@ -48,13 +44,12 @@ new Job(client, {
|
||||
/>
|
||||
<Header2 className="mb-2">How to use fetch</Header2>
|
||||
<Paragraph spacing>
|
||||
You can use the fetch API to make requests to any API. Or a different
|
||||
request library like axios if you'd prefer. Again wrapping the request
|
||||
in a Task will make sure it's resumable.
|
||||
You can use the fetch API to make requests to any API. Or a different request library like
|
||||
axios if you'd prefer. Again wrapping the request in a Task will make sure it's resumable.
|
||||
</Paragraph>
|
||||
<CodeBlock
|
||||
code={`
|
||||
new Job(client, {
|
||||
client.defineJob({
|
||||
id: "scheduled-job-1",
|
||||
name: "Scheduled Job 1",
|
||||
version: "0.1.1",
|
||||
|
||||
@@ -21,21 +21,21 @@ export function HelpInstall({ packageName }: { packageName: string }) {
|
||||
<ClientTabsContent value={"npm"}>
|
||||
<ClipboardField
|
||||
variant="secondary/medium"
|
||||
value={`npm install ${packageName}@next`}
|
||||
value={`npm install ${packageName}`}
|
||||
className="mb-4"
|
||||
/>
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"pnpm"}>
|
||||
<ClipboardField
|
||||
variant="secondary/medium"
|
||||
value={`pnpm install ${packageName}@next`}
|
||||
value={`pnpm add ${packageName}`}
|
||||
className="mb-4"
|
||||
/>
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"yarn"}>
|
||||
<ClipboardField
|
||||
variant="secondary/medium"
|
||||
value={`yarn add ${packageName}@next`}
|
||||
value={`yarn add ${packageName}`}
|
||||
className="mb-4"
|
||||
/>
|
||||
</ClientTabsContent>
|
||||
|
||||
@@ -7,28 +7,16 @@ export type ReplacementData = {
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export function HelpSamples({
|
||||
help,
|
||||
integrationClient,
|
||||
integration,
|
||||
}: HelpPanelProps) {
|
||||
export function HelpSamples({ help, integrationClient, integration }: HelpPanelProps) {
|
||||
return (
|
||||
<>
|
||||
{help &&
|
||||
help.samples.map((sample, i) => {
|
||||
const code = runReplacers(
|
||||
sample.code,
|
||||
integrationClient,
|
||||
integration
|
||||
);
|
||||
const code = runReplacers(sample.code, integrationClient, integration);
|
||||
return (
|
||||
<div key={i}>
|
||||
<Paragraph spacing>{sample.title}</Paragraph>
|
||||
<CodeBlock
|
||||
code={code}
|
||||
className="mb-4"
|
||||
highlightedRanges={sample.highlight}
|
||||
/>
|
||||
<CodeBlock code={code} className="mb-4" highlightedRanges={sample.highlight} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -39,10 +27,7 @@ export function HelpSamples({
|
||||
const replacements = [
|
||||
{
|
||||
match: /__SLUG__/g,
|
||||
replacement: (
|
||||
data: ReplacementData | undefined,
|
||||
integration: HelpPanelIntegration
|
||||
) => {
|
||||
replacement: (data: ReplacementData | undefined, integration: HelpPanelIntegration) => {
|
||||
if (data) return data.slug;
|
||||
return integration.identifier;
|
||||
},
|
||||
|
||||
@@ -3,13 +3,7 @@ import { LinkButton } from "../primitives/Buttons";
|
||||
import { Header1 } from "../primitives/Headers";
|
||||
import { NamedIconInBox } from "../primitives/NamedIcon";
|
||||
import { Paragraph } from "../primitives/Paragraph";
|
||||
import {
|
||||
Sheet,
|
||||
SheetBody,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTrigger,
|
||||
} from "../primitives/Sheet";
|
||||
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../primitives/Sheet";
|
||||
import { SelectOAuthMethod } from "./SelectOAuthMethod";
|
||||
import { Integration } from "~/services/externalApis/types";
|
||||
import { Client } from "~/presenters/IntegrationsPresenter.server";
|
||||
@@ -45,6 +39,7 @@ export function IntegrationWithMissingFieldSheet({
|
||||
to={docsIntegrationPath(integration.identifier)}
|
||||
variant="secondary/small"
|
||||
LeadingIcon="docs"
|
||||
target="_blank"
|
||||
>
|
||||
View docs
|
||||
</LinkButton>
|
||||
|
||||
@@ -4,13 +4,7 @@ import { Button } from "../primitives/Buttons";
|
||||
import { Callout } from "../primitives/Callout";
|
||||
import { Header1 } from "../primitives/Headers";
|
||||
import { NamedIconInBox } from "../primitives/NamedIcon";
|
||||
import {
|
||||
Sheet,
|
||||
SheetBody,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTrigger,
|
||||
} from "../primitives/Sheet";
|
||||
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../primitives/Sheet";
|
||||
import { CustomHelp } from "./CustomHelp";
|
||||
import { CheckIcon } from "@heroicons/react/24/solid";
|
||||
import { useFetcher } from "@remix-run/react";
|
||||
@@ -45,27 +39,22 @@ export function NoIntegrationSheet({
|
||||
</Paragraph>
|
||||
</div>
|
||||
) : (
|
||||
<fetcher.Form
|
||||
method="post"
|
||||
action={`/resources/apivote/${api.identifier}`}
|
||||
>
|
||||
<fetcher.Form method="post" action={`/resources/apivote/${api.identifier}`}>
|
||||
<Button
|
||||
variant="primary/small"
|
||||
disabled={isLoading}
|
||||
LeadingIcon={isLoading ? "spinner-white" : undefined}
|
||||
>
|
||||
{isLoading
|
||||
? "Saving…"
|
||||
: `I want an Integration for ${api.name}`}
|
||||
{isLoading ? "Saving…" : `I want an Integration for ${api.name}`}
|
||||
</Button>
|
||||
</fetcher.Form>
|
||||
)}
|
||||
</SheetHeader>
|
||||
<SheetBody>
|
||||
<Callout variant="info">
|
||||
We don’t have an Integration for the {api.name} API yet but you can
|
||||
request one by clicking the button above. In the meantime, connect
|
||||
to {api.name} using one of the methods below.
|
||||
We don’t have an Integration for the {api.name} API yet but you can request one by
|
||||
clicking the button above. In the meantime, connect to {api.name} using one of the
|
||||
methods below.
|
||||
</Callout>
|
||||
<CustomHelp name={api.name} />
|
||||
</SheetBody>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
ApiAuthenticationMethodOAuth2,
|
||||
Integration,
|
||||
} from "~/services/externalApis/types";
|
||||
import { ApiAuthenticationMethodOAuth2, Integration } from "~/services/externalApis/types";
|
||||
import { RadioGroup, RadioGroupItem } from "../primitives/RadioButton";
|
||||
import type { ConnectionType } from "@trigger.dev/database";
|
||||
import { Header2 } from "../primitives/Headers";
|
||||
@@ -29,14 +26,10 @@ export function SelectOAuthMethod({
|
||||
const [oAuthKey, setOAuthKey] = useState<string | undefined>(
|
||||
oAuthMethods.length === 1 ? oAuthMethods[0][0] : undefined
|
||||
);
|
||||
const [connectionType, setConnectionType] = useState<
|
||||
ConnectionType | undefined
|
||||
>();
|
||||
const [connectionType, setConnectionType] = useState<ConnectionType | undefined>();
|
||||
|
||||
const selectedOAuthMethod = oAuthKey
|
||||
? (integration.authenticationMethods[
|
||||
oAuthKey
|
||||
] as ApiAuthenticationMethodOAuth2)
|
||||
? (integration.authenticationMethods[oAuthKey] as ApiAuthenticationMethodOAuth2)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
@@ -65,9 +58,7 @@ export function SelectOAuthMethod({
|
||||
)}
|
||||
{selectedOAuthMethod && (
|
||||
<>
|
||||
<Header2 className="mb-2 mt-4">
|
||||
Who is connecting to {integration.name}
|
||||
</Header2>
|
||||
<Header2 className="mb-2 mt-4">Who is connecting to {integration.name}</Header2>
|
||||
<RadioGroup
|
||||
name="connection-type"
|
||||
className="flex gap-2"
|
||||
@@ -119,12 +110,9 @@ export function SelectOAuthMethod({
|
||||
<>
|
||||
<Header2 className="mb-1 mt-4">User OAuth coming soon</Header2>
|
||||
<Paragraph spacing>
|
||||
End-user OAuth is going to be released soon. If you are interested
|
||||
in being an early beta tester then please{" "}
|
||||
<a
|
||||
href="mailto:founders@trigger.dev"
|
||||
className="text-indigo-500 underline"
|
||||
>
|
||||
End-user OAuth is going to be released soon. If you are interested in being an early
|
||||
beta tester then please{" "}
|
||||
<a href="mailto:founders@trigger.dev" className="text-indigo-500 underline">
|
||||
message us
|
||||
</a>
|
||||
.
|
||||
|
||||
@@ -6,11 +6,7 @@ import { useState } from "react";
|
||||
import simplur from "simplur";
|
||||
import { useFeatures } from "~/hooks/useFeatures";
|
||||
import { useTextFilter } from "~/hooks/useTextFilter";
|
||||
import {
|
||||
ApiAuthenticationMethodOAuth2,
|
||||
Integration,
|
||||
Scope,
|
||||
} from "~/services/externalApis/types";
|
||||
import { ApiAuthenticationMethodOAuth2, Integration, Scope } from "~/services/externalApis/types";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { CodeBlock } from "../code/CodeBlock";
|
||||
import { Button } from "../primitives/Buttons";
|
||||
@@ -48,10 +44,7 @@ export function UpdateOAuthForm({
|
||||
const fetcher = useFetcher();
|
||||
const { isManagedCloud } = useFeatures();
|
||||
|
||||
const [
|
||||
form,
|
||||
{ title, scopes, hasCustomClient, customClientId, customClientSecret },
|
||||
] = useForm({
|
||||
const [form, { title, scopes, hasCustomClient, customClientId, customClientSecret }] = useForm({
|
||||
lastSubmission: fetcher.data,
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
@@ -63,9 +56,7 @@ export function UpdateOAuthForm({
|
||||
const location = useLocation();
|
||||
|
||||
const [selectedScopes, setSelectedScopes] = useState<Set<string>>(
|
||||
new Set(
|
||||
authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name)
|
||||
)
|
||||
new Set(authMethod.scopes.filter((s) => s.defaultChecked).map((s) => s.name))
|
||||
);
|
||||
|
||||
const requiresCustomOAuthApp = clientType === "EXTERNAL" || !isManagedCloud;
|
||||
@@ -76,10 +67,7 @@ export function UpdateOAuthForm({
|
||||
items: authMethod.scopes,
|
||||
filter: (scope, text) => {
|
||||
if (scope.name.toLowerCase().includes(text.toLowerCase())) return true;
|
||||
if (
|
||||
scope.description &&
|
||||
scope.description.toLowerCase().includes(text.toLowerCase())
|
||||
)
|
||||
if (scope.description && scope.description.toLowerCase().includes(text.toLowerCase()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -95,16 +83,8 @@ export function UpdateOAuthForm({
|
||||
>
|
||||
<Fieldset>
|
||||
<input type="hidden" name="id" value={existingIntegration.id} />
|
||||
<input
|
||||
type="hidden"
|
||||
name="integrationIdentifier"
|
||||
value={integration.identifier}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="integrationAuthMethod"
|
||||
value={authMethodKey}
|
||||
/>
|
||||
<input type="hidden" name="integrationIdentifier" value={integration.identifier} />
|
||||
<input type="hidden" name="integrationAuthMethod" value={authMethodKey} />
|
||||
<input type="hidden" name="redirectTo" value={location.pathname} />
|
||||
<InputGroup>
|
||||
<FormError>{form.error}</FormError>
|
||||
@@ -130,8 +110,7 @@ export function UpdateOAuthForm({
|
||||
<div>
|
||||
<Header2>Use my OAuth App</Header2>
|
||||
<Paragraph variant="small" className="mb-2">
|
||||
To use your own OAuth app, check the option below and insert the
|
||||
details.
|
||||
To use your own OAuth app, check the option below and insert the details.
|
||||
</Paragraph>
|
||||
<Checkbox
|
||||
id="hasCustomClient"
|
||||
@@ -145,17 +124,13 @@ export function UpdateOAuthForm({
|
||||
{useMyOAuthApp && (
|
||||
<div className="ml-6 mt-2">
|
||||
<Paragraph variant="small" className="mb-2">
|
||||
Set the callback url to{" "}
|
||||
<CodeBlock code={callbackUrl} showLineNumbers={false} />
|
||||
Set the callback url to <CodeBlock code={callbackUrl} showLineNumbers={false} />
|
||||
</Paragraph>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex gap-2">
|
||||
<InputGroup fullWidth>
|
||||
<Label variant="small">Client ID</Label>
|
||||
<Input
|
||||
fullWidth
|
||||
{...conform.input(customClientId, { type: "text" })}
|
||||
/>
|
||||
<Input fullWidth {...conform.input(customClientId, { type: "text" })} />
|
||||
</InputGroup>
|
||||
<InputGroup fullWidth>
|
||||
<Label variant="small">Client secret</Label>
|
||||
@@ -172,15 +147,15 @@ export function UpdateOAuthForm({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Header2>Scopes</Header2>
|
||||
<Paragraph variant="small" className="mb-4">
|
||||
Select the scopes you want to grant to {integration.name} in order
|
||||
for it to access your data. Note: If you try and perform an action
|
||||
in a Job that requires a scope you haven’t granted, that task will
|
||||
fail.
|
||||
</Paragraph>
|
||||
{/* <Header3 className="mb-2">
|
||||
{authMethod.scopes.length > 0 && (
|
||||
<div>
|
||||
<Header2>Scopes</Header2>
|
||||
<Paragraph variant="small" className="mb-4">
|
||||
Select the scopes you want to grant to {integration.name} in order for it to access
|
||||
your data. Note: If you try and perform an action in a Job that requires a scope you
|
||||
haven’t granted, that task will fail.
|
||||
</Paragraph>
|
||||
{/* <Header3 className="mb-2">
|
||||
Select from popular scope collections
|
||||
</Header3>
|
||||
<fieldset>
|
||||
@@ -190,60 +165,59 @@ export function UpdateOAuthForm({
|
||||
variant="button/small"
|
||||
/>
|
||||
</fieldset> */}
|
||||
<div className="mb-2 mt-4 flex items-center justify-between">
|
||||
<Header3>Select {integration.name} scopes</Header3>
|
||||
<Paragraph variant="small" className="text-slate-500">
|
||||
{simplur`${selectedScopes.size} scope[|s] selected`}
|
||||
</Paragraph>
|
||||
</div>
|
||||
<Input
|
||||
placeholder="Search scopes"
|
||||
className="mb-2"
|
||||
variant="medium"
|
||||
icon="search"
|
||||
fullWidth={true}
|
||||
value={filterText}
|
||||
onChange={(e) => setFilterText(e.target.value)}
|
||||
/>
|
||||
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
|
||||
{filteredItems.length === 0 && (
|
||||
<Paragraph variant="small" className="p-4">
|
||||
No scopes match {filterText}. Try a different search query.
|
||||
<div className="mb-2 mt-4 flex items-center justify-between">
|
||||
<Header3>Select {integration.name} scopes</Header3>
|
||||
<Paragraph variant="small" className="text-slate-500">
|
||||
{simplur`${selectedScopes.size} scope[|s] selected`}
|
||||
</Paragraph>
|
||||
)}
|
||||
{authMethod.scopes.map((s) => {
|
||||
return (
|
||||
<Checkbox
|
||||
key={s.name}
|
||||
id={s.name}
|
||||
value={s.name}
|
||||
name="scopes"
|
||||
label={s.name}
|
||||
defaultChecked={s.defaultChecked ?? false}
|
||||
badges={s.annotations?.map((a) => a.label)}
|
||||
description={s.description}
|
||||
variant="description"
|
||||
className={cn(
|
||||
filteredItems.find((f) => f.name === s.name) ? "" : "hidden"
|
||||
)}
|
||||
onChange={(isChecked) => {
|
||||
if (isChecked) {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.add(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
} else {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.delete(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Input
|
||||
placeholder="Search scopes"
|
||||
className="mb-2"
|
||||
variant="medium"
|
||||
icon="search"
|
||||
fullWidth={true}
|
||||
value={filterText}
|
||||
onChange={(e) => setFilterText(e.target.value)}
|
||||
/>
|
||||
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
|
||||
{filteredItems.length === 0 && (
|
||||
<Paragraph variant="small" className="p-4">
|
||||
No scopes match {filterText}. Try a different search query.
|
||||
</Paragraph>
|
||||
)}
|
||||
{authMethod.scopes.map((s) => {
|
||||
return (
|
||||
<Checkbox
|
||||
key={s.name}
|
||||
id={s.name}
|
||||
value={s.name}
|
||||
name="scopes"
|
||||
label={s.name}
|
||||
defaultChecked={s.defaultChecked ?? false}
|
||||
badges={s.annotations?.map((a) => a.label)}
|
||||
description={s.description}
|
||||
variant="description"
|
||||
className={cn(filteredItems.find((f) => f.name === s.name) ? "" : "hidden")}
|
||||
onChange={(isChecked) => {
|
||||
if (isChecked) {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.add(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
} else {
|
||||
setSelectedScopes((selected) => {
|
||||
selected.delete(s.name);
|
||||
return new Set(selected);
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Fieldset>
|
||||
|
||||
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
|
||||
|
||||
@@ -15,17 +15,13 @@ import {
|
||||
} from "../primitives/Table";
|
||||
import { SimpleTooltip } from "../primitives/Tooltip";
|
||||
import { runStatusTitle } from "../runs/RunStatuses";
|
||||
import { ProjectJob, useProject } from "~/hooks/useProject";
|
||||
import { ProjectJob } from "~/hooks/useJobs";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { JobRunStatus } from "~/models/job.server";
|
||||
import { cn } from "~/utils/cn";
|
||||
|
||||
export function JobsTable({
|
||||
jobs,
|
||||
noResultsText,
|
||||
}: {
|
||||
jobs: ProjectJob[];
|
||||
noResultsText: string;
|
||||
}) {
|
||||
export function JobsTable({ jobs, noResultsText }: { jobs: ProjectJob[]; noResultsText: string }) {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
|
||||
@@ -46,7 +42,10 @@ export function JobsTable({
|
||||
jobs.map((job) => {
|
||||
const path = jobPath(organization, project, job);
|
||||
return (
|
||||
<TableRow key={job.id}>
|
||||
<TableRow
|
||||
key={job.id}
|
||||
className={cn(job.hasIntegrationsRequiringAction && "bg-rose-500/30")}
|
||||
>
|
||||
<TableCell to={path}>
|
||||
<span className="flex items-center gap-2">
|
||||
<NamedIcon name={job.event.icon} className="h-8 w-8" />
|
||||
@@ -56,8 +55,7 @@ export function JobsTable({
|
||||
job.dynamic ? (
|
||||
<span className="flex items-center gap-0.5">
|
||||
<NamedIcon name="dynamic" className="h-4 w-4" />{" "}
|
||||
<span className="uppercase">Dynamic:</span>{" "}
|
||||
{job.event.title}
|
||||
<span className="uppercase">Dynamic:</span> {job.event.title}
|
||||
</span>
|
||||
) : (
|
||||
job.event.title
|
||||
@@ -68,23 +66,31 @@ export function JobsTable({
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
<LabelValueStack
|
||||
label={job.slug}
|
||||
value={`v${job.version}`}
|
||||
variant="primary"
|
||||
/>
|
||||
<LabelValueStack label={job.slug} value={`v${job.version}`} variant="primary" />
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
{job.integrations.map((integration) => (
|
||||
<SimpleTooltip
|
||||
key={integration.key}
|
||||
button={
|
||||
<NamedIcon
|
||||
name={integration.icon}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
<div className="relative">
|
||||
<NamedIcon name={integration.icon} className="h-6 w-6" />
|
||||
{integration.setupStatus === "MISSING_FIELDS" && (
|
||||
<NamedIcon name="error" className="absolute -left-1 -top-1 h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
content={
|
||||
<div>
|
||||
<p className="mb-1 text-rose-400">
|
||||
{integration.setupStatus === "MISSING_FIELDS" &&
|
||||
"This integration requires configuration"}
|
||||
</p>
|
||||
<p>
|
||||
{integration.title}: {integration.key}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
content={`${integration.title}: ${integration.key}`}
|
||||
/>
|
||||
))}
|
||||
</TableCell>
|
||||
@@ -141,10 +147,7 @@ export function JobsTable({
|
||||
})
|
||||
) : (
|
||||
<TableBlankRow colSpan={6}>
|
||||
<Paragraph
|
||||
variant="small"
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<Paragraph variant="small" className="flex items-center justify-center">
|
||||
{noResultsText}
|
||||
</Paragraph>
|
||||
</TableBlankRow>
|
||||
|
||||
@@ -11,9 +11,7 @@ export function AppContainer({
|
||||
}) {
|
||||
return (
|
||||
<BackgroundGradient showBackgroundGradient={showBackgroundGradient}>
|
||||
<div className={cn("grid h-full w-full grid-rows-[2.75rem_auto]")}>
|
||||
{children}
|
||||
</div>
|
||||
<div className={cn("grid h-full w-full grid-rows-[2.75rem_auto]")}>{children}</div>
|
||||
</BackgroundGradient>
|
||||
);
|
||||
}
|
||||
@@ -43,9 +41,7 @@ export function BackgroundGradient({
|
||||
|
||||
/** This container should be placed around the content on a page */
|
||||
export function PageContainer({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className={cn("grid h-full grid-rows-[auto_1fr]")}>{children}</div>
|
||||
);
|
||||
return <div className={cn("grid h-full grid-rows-[auto_1fr]")}>{children}</div>;
|
||||
}
|
||||
|
||||
export function PageBody({
|
||||
@@ -56,11 +52,7 @@ export function PageBody({
|
||||
scrollable?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(scrollable ? "overflow-y-auto p-4 " : "overflow-hidden")}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div className={cn(scrollable ? "overflow-y-auto p-4 " : "overflow-hidden")}>{children}</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,11 +60,7 @@ export function PageBodyPadding({ children }: { children: React.ReactNode }) {
|
||||
return <div className="p-4">{children}</div>;
|
||||
}
|
||||
|
||||
export function MainCenteredContainer({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export function MainCenteredContainer({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<div className="mx-auto mt-[30vh] max-w-xs">{children}</div>
|
||||
|
||||
@@ -1,211 +1,26 @@
|
||||
import { useMatches } from "@remix-run/react";
|
||||
import { Fragment } from "react";
|
||||
import {
|
||||
useIntegrationClient,
|
||||
useOptionalIntegrationClient,
|
||||
} from "~/hooks/useIntegrationClient";
|
||||
import { useJob, useOptionalJob } from "~/hooks/useJob";
|
||||
import {
|
||||
useOptionalOrganization,
|
||||
useOrganization,
|
||||
} from "~/hooks/useOrganizations";
|
||||
import { useOptionalProject, useProject } from "~/hooks/useProject";
|
||||
import { useOptionalRun, useRun } from "~/hooks/useRun";
|
||||
import {
|
||||
integrationClientConnectionsPath,
|
||||
integrationClientPath,
|
||||
integrationClientScopesPath,
|
||||
jobPath,
|
||||
jobTestPath,
|
||||
projectEnvironmentsPath,
|
||||
projectIntegrationsPath,
|
||||
projectPath,
|
||||
runDashboardPath,
|
||||
} from "~/utils/pathBuilder";
|
||||
import { RouteMatch, useMatches } from "@remix-run/react";
|
||||
import { Fragment, ReactNode } from "react";
|
||||
import { BreadcrumbIcon } from "../primitives/BreadcrumbIcon";
|
||||
import { JobsMenu } from "./JobsMenu";
|
||||
import { BreadcrumbLink } from "./NavBar";
|
||||
import { ProjectsMenu } from "./ProjectsMenu";
|
||||
|
||||
export type Breadcrumb = {
|
||||
slug:
|
||||
| "projects"
|
||||
| "jobs"
|
||||
| "integrations"
|
||||
| "integration"
|
||||
| "integration-job"
|
||||
| "integration-connections"
|
||||
| "integration-scopes"
|
||||
| "environments"
|
||||
| "job"
|
||||
| "test"
|
||||
| "runs"
|
||||
| "run";
|
||||
link?: {
|
||||
to: string;
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
|
||||
function useBreadcrumbs(): Breadcrumb[] {
|
||||
const matches = useMatches();
|
||||
|
||||
return matches
|
||||
.filter((match) => match.handle)
|
||||
.filter((match) => match.handle!.breadcrumb)
|
||||
.map((match) => match.handle!.breadcrumb as Breadcrumb);
|
||||
}
|
||||
export type BreadcrumbItem = (match: RouteMatch, allMatches: RouteMatch[]) => ReactNode;
|
||||
|
||||
export function Breadcrumb() {
|
||||
const breadcrumbs = useBreadcrumbs();
|
||||
const organization = useOptionalOrganization();
|
||||
const project = useOptionalProject();
|
||||
const job = useOptionalJob();
|
||||
const run = useOptionalRun();
|
||||
const client = useOptionalIntegrationClient();
|
||||
const matches = useMatches();
|
||||
|
||||
return (
|
||||
<div className="hidden items-center md:flex">
|
||||
{breadcrumbs.map((breadcrumb, index) => (
|
||||
<Fragment key={breadcrumb.slug}>
|
||||
<BreadcrumbIcon />
|
||||
<BreadcrumbItem
|
||||
breadcrumb={breadcrumb}
|
||||
organization={organization}
|
||||
project={project}
|
||||
job={job}
|
||||
run={run}
|
||||
client={client}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
{matches.map((match) => {
|
||||
if (!match.handle || !match.handle.breadcrumb) return null;
|
||||
|
||||
const breadcrumb = match.handle.breadcrumb as BreadcrumbItem;
|
||||
|
||||
return (
|
||||
<Fragment key={match.id}>
|
||||
<BreadcrumbIcon />
|
||||
{breadcrumb(match, matches)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbItem({
|
||||
breadcrumb,
|
||||
organization,
|
||||
project,
|
||||
job,
|
||||
run,
|
||||
client,
|
||||
}: {
|
||||
breadcrumb: Breadcrumb;
|
||||
organization?: ReturnType<typeof useOrganization>;
|
||||
project?: ReturnType<typeof useProject>;
|
||||
job?: ReturnType<typeof useJob>;
|
||||
run?: ReturnType<typeof useRun>;
|
||||
client?: ReturnType<typeof useIntegrationClient>;
|
||||
}) {
|
||||
switch (breadcrumb.slug) {
|
||||
case "projects":
|
||||
return <ProjectsMenu key={breadcrumb.slug} />;
|
||||
case "jobs":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
key={breadcrumb.slug}
|
||||
to={projectPath(organization!, project!)}
|
||||
title="Jobs"
|
||||
/>
|
||||
);
|
||||
case "environments":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
key={breadcrumb.slug}
|
||||
to={projectEnvironmentsPath(organization!, project!)}
|
||||
title="Environments"
|
||||
/>
|
||||
);
|
||||
case "integrations":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
key={breadcrumb.slug}
|
||||
to={projectIntegrationsPath(organization!, project!)}
|
||||
title="Integrations"
|
||||
/>
|
||||
);
|
||||
case "integration":
|
||||
return (
|
||||
<Fragment key={breadcrumb.slug}>
|
||||
<BreadcrumbLink
|
||||
to={projectIntegrationsPath(organization!, project!)}
|
||||
title="Integrations"
|
||||
/>
|
||||
<BreadcrumbIcon />
|
||||
<BreadcrumbLink
|
||||
to={integrationClientPath(organization!, project!, client!)}
|
||||
title={client!.title}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
case "integration-job":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
to={integrationClientPath(organization!, project!, client!)}
|
||||
title={"Jobs"}
|
||||
/>
|
||||
);
|
||||
case "integration-connections":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
to={integrationClientConnectionsPath(
|
||||
organization!,
|
||||
project!,
|
||||
client!
|
||||
)}
|
||||
title={"Connections"}
|
||||
/>
|
||||
);
|
||||
case "integration-scopes":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
to={integrationClientScopesPath(organization!, project!, client!)}
|
||||
title={"Scopes"}
|
||||
/>
|
||||
);
|
||||
case "job":
|
||||
return (
|
||||
<Fragment key={breadcrumb.slug}>
|
||||
<BreadcrumbLink
|
||||
to={projectPath(organization!, project!)}
|
||||
title="Jobs"
|
||||
/>
|
||||
<BreadcrumbIcon />
|
||||
<JobsMenu key={breadcrumb.slug} />
|
||||
</Fragment>
|
||||
);
|
||||
case "test":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
key={breadcrumb.slug}
|
||||
to={jobTestPath(organization!, project!, job!)}
|
||||
title="Test"
|
||||
/>
|
||||
);
|
||||
case "runs":
|
||||
return (
|
||||
<BreadcrumbLink
|
||||
key={breadcrumb.slug}
|
||||
to={jobPath(organization!, project!, job!)}
|
||||
title="Runs"
|
||||
/>
|
||||
);
|
||||
case "run":
|
||||
return (
|
||||
<Fragment key={breadcrumb.slug}>
|
||||
<BreadcrumbLink
|
||||
to={jobPath(organization!, project!, job!)}
|
||||
title="Runs"
|
||||
/>
|
||||
<BreadcrumbIcon />
|
||||
<BreadcrumbLink
|
||||
to={runDashboardPath(organization!, project!, job!, run!)}
|
||||
title={`Run #${run!.number}`}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return <span className="text-red-500">{breadcrumb.slug}</span>;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Link } from "@remix-run/react";
|
||||
import { Link, RouteMatch } from "@remix-run/react";
|
||||
import { useState } from "react";
|
||||
import { useJob } from "~/hooks/useJob";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { useJobs } from "~/hooks/useJobs";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { jobPath } from "~/utils/pathBuilder";
|
||||
import { LabelValueStack } from "../primitives/LabelValueStack";
|
||||
@@ -14,11 +15,12 @@ import {
|
||||
PopoverSectionHeader,
|
||||
} from "../primitives/Popover";
|
||||
|
||||
export function JobsMenu() {
|
||||
export function JobsMenu({ matches }: { matches: RouteMatch[] }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
const currentJob = useJob();
|
||||
const organization = useOrganization(matches);
|
||||
const project = useProject(matches);
|
||||
const projectJobs = useJobs(matches);
|
||||
const currentJob = useJob(matches);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -33,7 +35,7 @@ export function JobsMenu() {
|
||||
>
|
||||
<PopoverSectionHeader title="Jobs" />
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
{project.jobs.map((job) => {
|
||||
{projectJobs.map((job) => {
|
||||
const isSelected = job.id === currentJob?.id;
|
||||
return (
|
||||
<Link
|
||||
@@ -51,9 +53,7 @@ export function JobsMenu() {
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
/>
|
||||
{isSelected && (
|
||||
<NamedIcon name="check" className="mr-1 h-6 w-6" />
|
||||
)}
|
||||
{isSelected && <NamedIcon name="check" className="mr-1 h-6 w-6" />}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user