Skip to content

Tips, Tricks, and Gotchas

Tim Burks edited this page May 30, 2023 · 3 revisions

This page serves to hold helpful information that may not be easily categorized or doesn't seem to belong anywhere else.

Registry API

Discovery Format-based Go clients don't properly handle HTTPBody responses

The following test program uses the (deprecated) Discovery Format-based Go client to request a spec body. It fails in a surprising way.

package main

import (
	"context"
	"log"
	apigeeregistry "google.golang.org/api/apigeeregistry/v1"
)

func main() {
	ctx := context.TODO()
	apigeeregistryService, err := apigeeregistry.NewService(ctx)
	if err != nil {
		log.Fatalf("%s", err)
	}
	result, err := apigeeregistryService.Projects.Locations.Apis.Versions.Specs.GetContents("projects/test/locations/global/apis/petstore/versions/v1/specs/openapi").Do()
	if err != nil {
		log.Fatalf("error: %s", err)
	}
	log.Printf("%T %+v", result, result)
}

If the spec body is JSON, it seems to run just fine, but returns an HttpBody object with the ContentType and Data fields empty:

go run main.go 
2023/04/04 09:59:47 *apigeeregistry.HttpBody &{ContentType: Data: Extensions:[] ServerResponse:{HTTPStatusCode:200 Header:map[Alt-Svc:[h3=":443"; ma=2592000,h3-29=":443"; ma=2592000] Content-Length:[4375] Content-Type:[application/x.openapi;version=3] Date:[Tue, 04 Apr 2023 16:59:47 GMT] Server:[ESF] Vary:[Origin X-Origin Referer] X-Content-Type-Options:[nosniff] X-Frame-Options:[SAMEORIGIN] X-Xss-Protection:[0]]} ForceSendFields:[] NullFields:[]}

If the spec body is something else (e.g. YAML), we see a more obvious error message:

$ go run main.go 
2023/04/04 10:01:39 error: invalid character '#' looking for beginning of value
exit status 1

It appears that the Go client library is trying to interpret the HTTP response as a JSON struct containing the ContentType and Data fields. It doesn't complain when they are missing and ignores fields that it doesn't recognize, but "succeeds" as long as the response is valid JSON.

How to make filtered API requests over HTTP

Filter expressions are written in the CEL language, which is documented here.

Using the registry tool, a filtered request could be made like this:

registry get apis -o yaml --filter "'apihub-team' in labels && labels['apihub-team'] == 'demo'"

The first part ('apihub-team' in labels &&) could be omitted if every API has an apihub-team label defined, but it's a good precaution because the CEL evaluation will throw an error if any API doesn't have a value for apihub-team. That error looks like this:

Error: rpc error: code = InvalidArgument desc = no such key: apihub-team

So 'apihub-team' in labels is just verifying that the apihub-team label is defined before we try to read it with labels['apihub-team'].

You could make this same request in an HTTP request as follows (after setting PROJECT to your project ID):

curl "https://apigeeregistry.googleapis.com/v1/projects/${PROJECT}/locations/global/apis?filter=%27apihub-team%27%20in%20labels%20%26%26%20labels%5B%27apihub-team%27%5D%20%3D%3D%20%demo%27" \
  -H "Authorization: Bearer `gcloud auth print-access-token`"

Note that the filter argument is the URL-escaped equivalent of the filter expression that we passed to the registry tool.