atwork-cli: basic demo tool for interacting with at://work#
at://work (https://atwork.place) is a job board build using AT (atproto).
This little CLI project is really a demo/tutorial for AT developer tools, including indigo (the Go SDK) and glot (lexicon management and codegen for Go).
Project Setup Tutorial#
In the context of this little CLI project, we are going to work with existing Lexicons, instead of declaring our own. These Lexicons span several namespaces, and not all of them have maintained public SDKs for Go.
We'll use gloat to work with published lexicons, though note a bunch of glot functionality is expected to get merged in to the goat command soon.
Starting in an empty project directory, and with glot installed, we'll start by pulling down the place.atwork.* Lexicons. The trailing . in this command tells it to fetch all schemas under the Lexicon group:
$ glot pull place.atwork.
🟢 place.atwork.searchListings
🟢 place.atwork.profile
🟢 place.atwork.listing
🟢 place.atwork.getListings
🟢 place.atwork.getListing
🟢 place.atwork.endorsementProof
🟢 place.atwork.endorsement
Next we'll initialize the Go project, and try to generate types for these Lexicons:
$ go mod init tangled.org/bnewbold.net/atwork-cli
go: creating new go.mod: module tangled.org/bnewbold.net/atwork-cli
$ glot codegen lexicons/place/atwork --output-dir .
🟠 lexicons/place/atwork/endorsement.json
[failed]: failed to format codegen output (lexicons/place/atwork/endorsement.json): could not resolve lexicon reference (com.atproto.repo.strongRef): schema not found in catalog: com.atproto.repo.strongRef#main
🟢 lexicons/place/atwork/endorsementProof.json
🟢 lexicons/place/atwork/getListing.json
🟢 lexicons/place/atwork/getListings.json
🟠 lexicons/place/atwork/listing.json
[failed]: failed to format codegen output (lexicons/place/atwork/listing.json): could not resolve lexicon reference (app.bsky.richtext.facet): schema not found in catalog: app.bsky.richtext.facet#main
🟠 lexicons/place/atwork/profile.json
[failed]: failed to format codegen output (lexicons/place/atwork/profile.json): could not resolve lexicon reference (app.bsky.richtext.facet): schema not found in catalog: app.bsky.richtext.facet#main
🟢 lexicons/place/atwork/searchListings.json
error: some codegen failed
This hits some errors because the Lexicons reference other namespaces (lexicons.community.*, app.bsky.richtext.*, com.atproto.repo.strongRef). In the future these might get pulled in automatically, but for now let's pull them in to our namespace:
$ glot pull app.bsky.richtext.facet community.lexicon.location.hthree com.atproto.repo.strongRef
🟢 app.bsky.richtext.facet
🟢 community.lexicon.location.hthree
🟢 com.atproto.repo.strongRef
The app.bsky.* namespace has a published Go package, and the com.atproto.* namespace is protocol-level, but lexicons.community.* doesn't have package, so we'll generate code for that Lexicon as well:
$ glot codegen lexicons/place/atwork lexicons/community/lexicon --output-dir .
🟢 lexicons/community/lexicon/location/hthree.json
🟢 lexicons/place/atwork/endorsement.json
🟢 lexicons/place/atwork/endorsementProof.json
🟢 lexicons/place/atwork/getListing.json
🟢 lexicons/place/atwork/getListings.json
🟢 lexicons/place/atwork/listing.json
🟢 lexicons/place/atwork/profile.json
🟢 lexicons/place/atwork/searchListings.json
Great, now we have generated types for all the Lexicons we use.
Basic API Request: Search#
The place.atwork.* lexicons include both record types and API endpoints. Let's try calling an unauthenticated public API endpoint using the indigo API client.
// pseudo-code; see main.go for actual implementation
import (
"github.com/bluesky-social/indigo/atproto/atclient"
"tangled.org/bnewbold.net/atwork-cli/placeatwork"
)
func main() {
searchQuery := "intern"
apiServer := "https://atwork.place"
client := atclient.NewAPIClient(apiServer)
resp, err := placeatwork.SearchListings(ctx, client, searchQuery)
if err != nil {
return err
}
for _, hit := range resp.Listings {
fmt.Prinln(hit.Value.Title)
}
}
The API client gets injected in to the generated API endpoint code (placeatwork.SearchListings), which converts the response JSON into the expected struct type.