lexicon devex tutorial
Go 100.0%
2 1 0

Clone this repository

https://tangled.org/bnewbold.net/atwork-cli
git@tangled.org:bnewbold.net/atwork-cli

For self-hosted knots, clone URLs may differ based on your setup.

README.md

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.

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.