Package opslevel
provides an OpsLevel API client implementation.
NOTE: this library is still a WIP and does not match the API 100% yet
opslevel
requires Go version 1.8 or later.
go get -u github.com/opslevel/opslevel-go
Construct a client, specifying the API token. Then, you can use it to make GraphQL queries and mutations.
client := opslevel.NewClient("XXX_API_TOKEN_XXX")
// Use client...
You can validate the client can successfully talk to the OpsLevel API.
client := opslevel.NewClient("XXX_API_TOKEN_XXX")
if err := client.Validate() {
panic(err)
}
Every resource (IE: service, lifecycle, tier, etc) in OpsLevel API has a corresponding data structure in go as well as the graphql query & mutation inputs. Additionally there is also some helper functions that use native go types like string
and []string
to make it easier to work with. The following are a handful of examples:
Find a service given an alias and print the owning team name:
foundService, foundServiceErr := client.GetServiceWithAlias("MyCoolService")
if foundServiceErr != nil {
panic(foundServiceErr)
}
fmt.Println(foundService.Owner.Name)
Create a new service in OpsLevel and print the ID:
serviceCreateInput := opslevel.ServiceCreateInput{
Name: "MyCoolService",
Product: "MyProduct",
Description: "The Coolest Service",
Language: "go",
}
newService, newServiceErr := client.CreateService(serviceCreateInput)
if newServiceErr != nil {
panic(newServiceErr)
}
fmt.Println(newService.Id)
Assign the tag {"hello": "world"}
to our newly created service and print all the tags currently on it:
allTagsOnThisService, err := client.AssignTagForId(newService.Id, "Hello", "World")
for tagKey, tagValue := range allTagsOnThisService {
fmt.Printf("Tag '{%s : %s}'", tagKey, tagValue)
}
List all the tags for a service:
tags, tagsErr := client.GetTagsForServiceWithAlias("MyCoolService")
for _, tag := range tags {
fmt.Printf("Tag '{%s : %s}'\n", tag.Key, tag.Value)
}
// OR
service, serviceErr := client.GetServiceWithAlias("MyCoolService")
tags, tagsErr := client.GetTagsForService(service.Id)
for _, tag := range tags {
fmt.Printf("Tag '{%s : %s}'\n", tag.Key, tag.Value)
}
Build a lookup table of teams:
func GetTeams(client *opslevel.Client) (map[string]opslevel.Team, error) {
teams := make(map[string]opslevel.Team)
data, dataErr := client.ListTeams()
if dataErr != nil {
return teams, dataErr
}
for _, team := range data {
teams[string(team.Alias)] = team
}
return teams, nil
}
The client also exposes functions Query
and Mutate
for doing custom query or mutations. We are running ontop of this go graphql library so you can read up on how to define go structures that represent a query or mutation there but here is an example of each:
var query struct {
Account struct {
Tiers []Tier
}
}
if err := client.Query(&query, nil); err != nil {
panic(err)
}
for _, tier := range m.Account.Tiers {
fmt.Println(tier.Name)
}
var mutation struct {
Payload struct {
Aliases []graphql.String
OwnerId graphql.String
Errors []opslevel.OpsLevelErrors
} `graphql:"aliasCreate(input: $input)"`
}
variables := PayloadVariables{
"input": opslevel.AliasCreateInput{
Alias: "MyNewAlias",
OwnerId: "XXXXXXXXXXX",
},
}
if err := client.Mutate(&mutation, variables); err != nil {
panic(err)
}
for _, alias := range m.Payload.Aliases {
fmt.Println(alias)
}
Wrapping the client can be useful when you want to override default behavior, such as always setting
context or disallowing (to the best of Go's ability) access to specific receiver functions on
opslevel.Client
. Here is an example of wrapping the client to always require context to be passed
while also maintaining the ability to pass in default options, with the ability to extend your own
set of options if need-be:
type Client struct{
*opslevel.Client
do opsLevelDefaultOptions
customOption string
}
func NewClient(ctx context.Context, apiToken string, options ...Option) *Client {
var c Client
for i := range options{
options[i](&c)
}
c.Client = opslevel.NewClient(apiToken, c.do.opsLevelOptions(ctx)...)
return &c, nil
}
type opsLevelDefaultOptions struct{
url *string
pageSize *int
}
func (o *opsLevelDefaultOptions) opsLevelOptions(ctx context.Context) []opslevel.Option {
opts := []opslevel.Option{opslevel.SetContext(ctx)} // Always set the context.
if o.url != nil {
ops = append(opts, opslevel.SetURL(*o.url))
}
if o.pageSize != nil {
ops = append(opts, opslevel.SetPageSize(*o.pageSize))
}
}
// Option is our own functional option type for our own custom client.
type Option func(*Client)
func WithURL(url string) Option {
return func(c *Client) {
c.do.url = &url
}
}
func WithPageSize(pageSize int) Option {
return func(c *Client) {
c.do.pageSize = &pageSize
}
}
func WithCustomOption(custom string) Option {
return func(c *Client) {
c.customOption = custom
}
}