diff --git a/.gitignore b/.gitignore index 1d1c7c41..3daba8a7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ __pycache__ /pyproject.toml /style_suppressions.xml bin +bulkins node_modules target vendor diff --git a/community/golang/concurrent_insurance_buy/README.md b/community/golang/concurrent_insurance_buy/README.md index 87b2152c..25f91806 100644 --- a/community/golang/concurrent_insurance_buy/README.md +++ b/community/golang/concurrent_insurance_buy/README.md @@ -1,11 +1,27 @@ # Concurrently Buy Insurance -This script allows you to concurrently buy EasyPost insurance. It will work with EasyPost Shipments or standalone Insurance. Use the `sample.csv` file as a template, fill in the details, and run the script. +This script allows you to concurrently buy EasyPost Insurance. It works when insuring EasyPost Shipments or when buying standalone Insurance for a package bought outside of the EasyPost ecosystem. Use the `sample.csv` file as a template, fill in the details (do not delete the header), and run the script. + +## Things to Know + +- You must follow the `sample.csv` template or the script will not work correctly. Do not delete the header row +- Only run the script once! Running the script multiple times with the same data will create duplicate insurance objects and fees +- The script will spin up 20 concurrent requests at a time + - Because requests are sent concurrently, they may complete in a different order than they were specified in the original CSV. Sorting the results CSV by Tracking Code and the original `sample.csv` will allow you to match up requests with input data for debugging purchases in the event of errors +- The status of each request will be saved to a CSV once the script is complete. Each line will include error messages if there were any, the time each request took, and the status of the request +- If there are errors reported in the results CSV, correct input data as necessary and run the script again + - NOTE: Ensure you delete successful rows from the `sample.csv` file before running the script again. Failure to do so will result in duplicate insurance objects and fees (eg: If I had 5 rows, 3 succeeded, 2 failed - I would remove the 3 rows with a success status of true so that my sample CSV contained the 2 rows that initially failed, I'd correct the data as needed, and re-run the script with only those two failed rows) +- It's recommended to run the `sample.csv` file as-is with a test API key to get a feel for how it works prior to loading real data and using a production API key ## Usage -After running the script, a CSV file will be saved with the success status of each insurance purchase attempt and the time it took. If there are errors, those messages will be included. +```shell +EASYPOST_API_KEY=123... CSV=path/to/sample.csv go run concurrent_insurance_buy.go +``` + +## Development ```shell -EASYPOST_API_KEY=123... CSV=sample.csv go run concurrent_insurance_buy.go +# To build a standalone binary of this tool (eg: call it bulkins) +go build -o bulkins ``` diff --git a/community/golang/concurrent_insurance_buy/concurrent_insurance_buy.go b/community/golang/concurrent_insurance_buy/concurrent_insurance_buy.go index 70970828..eac22f22 100644 --- a/community/golang/concurrent_insurance_buy/concurrent_insurance_buy.go +++ b/community/golang/concurrent_insurance_buy/concurrent_insurance_buy.go @@ -13,18 +13,20 @@ import ( "github.com/EasyPost/easypost-go/v4" ) -/* -* Concurrently buy EasyPost Insurance via a CSV file -* -* Usage: EASYPOST_API_KEY=123... CSV=sample.csv go run concurrent_insurance_buy.go - */ +// Concurrently buy EasyPost Insurance via a CSV file +// Usage: EASYPOST_API_KEY=123... CSV=sample.csv go run concurrent_insurance_buy.go func main() { scriptStart := time.Now() records := getCsvRecords() - client := easypost.New(os.Getenv("EASYPOST_API_KEY")) + apiKeyParam := os.Getenv("EASYPOST_API_KEY") + if apiKeyParam == "" { + handleGoErr(errors.New("EASYPOST_API_KEY param not set")) + } + + client := easypost.New(apiKeyParam) numOfGoroutines := int(math.Min(float64(len(records)), 20)) semaphore := make(chan bool, numOfGoroutines) lineMessageList := make([][]string, 0) @@ -44,6 +46,7 @@ func main() { to_address_id := currentLine[4] from_address_id := currentLine[5] + fmt.Printf("Sending request for %s...\n", tracking_code) success, message := createInsurance(client, reference, tracking_code, carrier_string, amount, to_address_id, from_address_id) elapsedTime := time.Since(goroutineStartTime) @@ -72,7 +75,12 @@ func main() { // getCsvRecords builds the set of data without including the header and validates it func getCsvRecords() [][]string { - file, err := os.Open(os.Getenv("CSV")) + csvParam := os.Getenv("CSV") + if csvParam == "" { + handleGoErr(errors.New("CSV parameter not set")) + } + + file, err := os.Open(csvParam) handleGoErr(err) defer file.Close() reader := csv.NewReader(file) @@ -129,7 +137,6 @@ func createInsurance(client *easypost.Client, reference string, tracking_code st } if tracker.ShipmentID != "" { - // shipment.Tracker is just the amount and not the object _, err := client.InsureShipment(tracker.ShipmentID, amount) var eperr *easypost.APIError if errors.As(err, &eperr) { diff --git a/community/golang/concurrent_insurance_buy/sample.csv b/community/golang/concurrent_insurance_buy/sample.csv index d0bd024b..6f7a95f3 100644 --- a/community/golang/concurrent_insurance_buy/sample.csv +++ b/community/golang/concurrent_insurance_buy/sample.csv @@ -1,4 +1,4 @@ -Reference,Tracking Code,Carrier String,Amount,To Address (optional),From Address (optional) +Reference,Tracking Code,Carrier String,Amount,To Address ID (optional),From Address ID (optional) ref_1,EZ1000000001,USPS,100.00,, ref_2,EZ2000000002,USPS,200.00,, ref_3,EZ3000000003,USPS,300.00,,