lexicon devex tutorial
1 2`atwork-cli`: basic demo tool for interacting with `at://work` 3=============================================================== 4 5`at://work` (https://atwork.place) is a job board build using AT ([atproto](https://atproto.com)). 6 7This little CLI project is really a demo/tutorial for AT developer tools, including [indigo](https://github.com/bluesky-social/indigo) (the Go SDK) and [glot](https://tangled.org/bnewbold.net/cobalt/tree/main/cmd/glot) (lexicon management and codegen for Go). 8 9 10## Project Setup Tutorial 11 12In 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. 13 14We'll use [gloat](https://tangled.org/bnewbold.net/cobalt/tree/main/cmd/glot) to work with published lexicons, though note a bunch of glot functionality is expected to get merged in to the `goat` command soon. 15 16Starting 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: 17 18``` 19$ glot pull place.atwork. 20 🟢 place.atwork.searchListings 21 🟢 place.atwork.profile 22 🟢 place.atwork.listing 23 🟢 place.atwork.getListings 24 🟢 place.atwork.getListing 25 🟢 place.atwork.endorsementProof 26 🟢 place.atwork.endorsement 27 28``` 29 30Next we'll initialize the Go project, and try to generate types for these Lexicons: 31 32``` 33$ go mod init tangled.org/bnewbold.net/atwork-cli 34go: creating new go.mod: module tangled.org/bnewbold.net/atwork-cli 35 36$ glot codegen lexicons/place/atwork --output-dir . 37 🟠 lexicons/place/atwork/endorsement.json 38 [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 39 🟢 lexicons/place/atwork/endorsementProof.json 40 🟢 lexicons/place/atwork/getListing.json 41 🟢 lexicons/place/atwork/getListings.json 42 🟠 lexicons/place/atwork/listing.json 43 [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 44 🟠 lexicons/place/atwork/profile.json 45 [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 46 🟢 lexicons/place/atwork/searchListings.json 47error: some codegen failed 48``` 49 50This 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: 51 52``` 53$ glot pull app.bsky.richtext.facet community.lexicon.location.hthree com.atproto.repo.strongRef 54 🟢 app.bsky.richtext.facet 55 🟢 community.lexicon.location.hthree 56 🟢 com.atproto.repo.strongRef 57``` 58 59The `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: 60 61``` 62$ glot codegen lexicons/place/atwork lexicons/community/lexicon --output-dir . 63 🟢 lexicons/community/lexicon/location/hthree.json 64 🟢 lexicons/place/atwork/endorsement.json 65 🟢 lexicons/place/atwork/endorsementProof.json 66 🟢 lexicons/place/atwork/getListing.json 67 🟢 lexicons/place/atwork/getListings.json 68 🟢 lexicons/place/atwork/listing.json 69 🟢 lexicons/place/atwork/profile.json 70 🟢 lexicons/place/atwork/searchListings.json 71``` 72 73Great, now we have generated types for all the Lexicons we use. 74 75 76## Basic API Request: Search 77 78The `place.atwork.*` lexicons include both record types and API endpoints. Let's try calling an unauthenticated public API endpoint using the indigo API client. 79 80```go 81// pseudo-code; see main.go for actual implementation 82 83import ( 84 "github.com/bluesky-social/indigo/atproto/atclient" 85 "tangled.org/bnewbold.net/atwork-cli/placeatwork" 86) 87 88func main() { 89 90 searchQuery := "intern" 91 apiServer := "https://atwork.place" 92 93 client := atclient.NewAPIClient(apiServer) 94 95 resp, err := placeatwork.SearchListings(ctx, client, searchQuery) 96 if err != nil { 97 return err 98 } 99 100 for _, hit := range resp.Listings { 101 fmt.Prinln(hit.Value.Title) 102 } 103} 104``` 105 106The API client gets injected in to the generated API endpoint code (`placeatwork.SearchListings`), which converts the response JSON into the expected struct type.