Rate this page:
The following is a simplified implementation of a PAPI client in Go Lang intended to be used as a reference
Go version at the time of implementation: go 1.21rc1
The folder structure we have defined is as follows:
1 2├── client │ ├── papi │ │ ├── papi.go ├── service │ ├── catalog │ │ ├── catalog.go │── go.mod │ ├── go.sum └── main.go
We are leveraging the following library for the GraphQL requests: machinebox/graphql
1 2require github.com/machinebox/graphql v0.2.2
The PAPI client is first initialized in papi
package by the init()
function:
1 2// /client/papi/papi.go package papi import ( "github.com/machinebox/graphql" "os" ) var Client *graphql.Client func init() { // Initialize the PAPI client Client = graphql.NewClient("https://api.atlassian.com/graphql") }
Now, let's add a couple supporting methods that we'll use to set the authorization
header as well as build a
GraphQL query request. With this, we are able to dynamically pass a query to the client from each service.
For simplicity, TOKEN
is passed in as a runtime property
1 2func buildAuthHeader() string { return "Bearer " + os.Getenv("TOKEN") } func Query(query string) *graphql.Request { // Create new request req := graphql.NewRequest(query) req.Header.Add("Authorization", buildAuthHeader()) return req }
Each service can now import the PAPI client to facilitate requests
1 2// /service/catalog/catalog.go package catalog import ( "context" "log" "papi-client-go-example/client/papi" )
Supporting types for the OfferingCatalog
query response:
1 2type OfferingCatalogResponse struct { Partner struct { OfferingCatalog struct { BtfProducts []struct { ProductKey string `json:"productKey"` ProductDescription string `json:"productDescription"` } `json:"btfProducts"` CloudProducts []struct { ID string `json:"id"` Name string `json:"name"` } `json:"cloudProducts"` } `json:"offeringCatalog"` } `json:"partner"` }
Let's create a function which we can use to make a OfferingCatalog
request:
1 2func FetchOfferingCatalog() OfferingCatalogResponse { req := papi.Query(` query fetchOfferingCatalog{ partner { offeringCatalog{ btfProducts{ productKey productDescription } cloudProducts{ id name } } } } `) var response OfferingCatalogResponse if err := papi.Client.Run(context.Background(), req, &response); err != nil { log.Fatal(err) } return response }
Now that we've created the catalog service request for OfferingCatalog
query, we can import it and call the service
1 2package main import ( "encoding/json" "log" papiCatalogService "papi-client-go-example/service/catalog" ) func main() { /** Fetch OfferingCatalog */ // Fetch the offering catalog response := papiCatalogService.FetchOfferingCatalog() // Convert the response to JSON res, err := json.Marshal(response) if err != nil { panic(err) } log.Println("FetchOfferingCatalog | response: ", string(res)) }
Supporting type structs for the OfferingDetails
query response:
1 2type OfferingDetailsResponse struct { Partner struct { OfferingDetails struct { CloudProducts []struct { ID string `json:"id"` Name string `json:"name"` Offerings []struct { BillingType interface{} `json:"billingType"` HostingType string `json:"hostingType"` ID string `json:"id"` Level int `json:"level"` Name string `json:"name"` PricingType string `json:"pricingType"` Sku string `json:"sku"` PricingPlans []struct { Currency string `json:"currency"` Description string `json:"description"` Items []struct { ChargeElement string `json:"chargeElement"` ChargeType string `json:"chargeType"` Cycle struct { Count int `json:"count"` Interval string `json:"interval"` Name string `json:"name"` } `json:"cycle"` Tiers []struct { Amount interface{} `json:"amount"` Ceiling float64 `json:"ceiling"` FlatAmount interface{} `json:"flatAmount"` Floor float64 `json:"floor"` Policy interface{} `json:"policy"` UnitAmount float64 `json:"unitAmount"` } `json:"tiers"` TiersMode string `json:"tiersMode"` } `json:"items"` ID string `json:"id"` PrimaryCycle struct { Count int `json:"count"` Interval string `json:"interval"` Name string `json:"name"` } `json:"primaryCycle"` Sku string `json:"sku"` Type string `json:"type"` } `json:"pricingPlans"` } `json:"offerings"` } `json:"cloudProducts,omitempty"` CloudApps []struct { BillingType interface{} `json:"billingType"` HostingType interface{} `json:"hostingType"` ID string `json:"id"` Level int `json:"level"` Name string `json:"name"` Parent interface{} `json:"parent"` PricingType interface{} `json:"pricingType"` Sku string `json:"sku"` SupportedBillingSystems interface{} `json:"supportedBillingSystems"` } `json:"cloudApps,omitempty"` BtfProducts []struct { BillingType string `json:"billingType"` ContactSalesForAdditionalPricing bool `json:"contactSalesForAdditionalPricing"` DataCenter bool `json:"dataCenter"` DiscountOptOut bool `json:"discountOptOut"` LastModified string `json:"lastModified"` MarketplaceAddon bool `json:"marketplaceAddon"` ParentKey interface{} `json:"parentKey"` ParentDescription interface{} `json:"parentDescription"` ProductDescription string `json:"productDescription"` ProductKey string `json:"productKey"` UserCountEnforced bool `json:"userCountEnforced"` ProductType string `json:"productType"` Monthly []interface{} `json:"monthly"` OrderableItems []struct { Amount int `json:"amount"` Currency string `json:"currency"` Description string `json:"description"` Edition interface{} `json:"edition"` EditionDescription string `json:"editionDescription"` EditionID string `json:"editionId"` EditionType string `json:"editionType"` EditionTypeIsDeprecated bool `json:"editionTypeIsDeprecated"` Enterprise bool `json:"enterprise"` LicenseType string `json:"licenseType"` MonthsValid int `json:"monthsValid"` NewPricingPlanItem interface{} `json:"newPricingPlanItem"` OrderableItemID string `json:"orderableItemId"` PubliclyAvailable bool `json:"publiclyAvailable"` RenewalAmount int `json:"renewalAmount"` RenewalFrequency string `json:"renewalFrequency"` SaleType string `json:"saleType"` Sku interface{} `json:"sku"` Starter bool `json:"starter"` UnitCount int `json:"unitCount"` UnitLabel string `json:"unitLabel"` } `json:"orderableItems"` } `json:"btfProducts,omitempty"` BtfApps []struct { BillingType string `json:"billingType"` ContactSalesForAdditionalPricing bool `json:"contactSalesForAdditionalPricing"` DataCenter bool `json:"dataCenter"` DiscountOptOut bool `json:"discountOptOut"` LastModified string `json:"lastModified"` MarketplaceAddon bool `json:"marketplaceAddon"` ParentDescription string `json:"parentDescription"` ParentKey string `json:"parentKey"` ProductDescription string `json:"productDescription"` ProductKey string `json:"productKey"` ProductType string `json:"productType"` UserCountEnforced bool `json:"userCountEnforced"` OrderableItems []struct { Amount int `json:"amount"` Currency string `json:"currency"` Description string `json:"description"` Edition interface{} `json:"edition"` EditionDescription string `json:"editionDescription"` EditionID string `json:"editionId"` EditionType string `json:"editionType"` EditionTypeIsDeprecated bool `json:"editionTypeIsDeprecated"` Enterprise bool `json:"enterprise"` MonthsValid int `json:"monthsValid"` LicenseType string `json:"licenseType"` NewPricingPlanItem interface{} `json:"newPricingPlanItem"` OrderableItemID string `json:"orderableItemId"` PubliclyAvailable bool `json:"publiclyAvailable"` RenewalAmount int `json:"renewalAmount"` SaleType string `json:"saleType"` RenewalFrequency string `json:"renewalFrequency"` Sku interface{} `json:"sku"` Starter bool `json:"starter"` UnitCount int `json:"unitCount"` UnitLabel string `json:"unitLabel"` } `json:"orderableItems"` Monthly []interface{} `json:"monthly"` Annual []interface{} `json:"annual"` } `json:"btfApps,omitempty"` } `json:"offeringDetails"` } `json:"partner"` }
For the details query we have a filter which is required to specify either a cloud of a BTF product identifier,
along with additional optional parameters. This is defined as the PartnerOfferingFilter
GraphQL type. We will
create the supporting type definitions in Go to implement this.
1 2type OfferingFilter struct { CloudProductFilter *CloudFilter `json:"cloudProduct,omitempty"` BtfProductFilter *BtfFilter `json:"btfProduct,omitempty"` } type CloudFilter struct { Id string `json:"id,omitempty"` Currency string `json:"currency,omitempty"` PricingPlanType string `json:"pricingPlanType,omitempty"` } type BtfFilter struct { ProductKey string `json:"productKey,omitempty"` Currency string `json:"currency,omitempty"` LicenseType string `json:"licenseType,omitempty"` }
Let's create a function which we can use to make a OfferingDetails
request:
1 2func FetchOfferingDetails(filter OfferingFilter) OfferingDetailsResponse { req := papi.Query(` query fetchOfferingDetails($where: PartnerOfferingFilter) { partner { offeringDetails(where: $where){ cloudProducts { id name offerings { billingType hostingType id level name pricingType sku pricingPlans { currency description items { chargeElement chargeType cycle { count interval name } tiers { amount ceiling flatAmount floor policy unitAmount } tiersMode } id primaryCycle{ count interval name } sku type } } } cloudApps { billingType hostingType id level name parent pricingType sku supportedBillingSystems } btfProducts { billingType contactSalesForAdditionalPricing dataCenter discountOptOut lastModified marketplaceAddon parentKey parentDescription productDescription productKey userCountEnforced productType monthly { currency editionType entitionTypeIsDepercated price unitBlockSize unitLabel unitLimit unitStart } orderableItems { amount currency description edition editionDescription editionId editionType editionTypeIsDeprecated enterprise licenseType monthsValid newPricingPlanItem orderableItemId publiclyAvailable renewalAmount renewalFrequency saleType sku starter unitCount unitLabel } } btfApps { billingType contactSalesForAdditionalPricing dataCenter discountOptOut lastModified marketplaceAddon parentDescription parentKey productDescription productKey productType userCountEnforced orderableItems { amount currency description edition editionDescription editionId editionType editionTypeIsDeprecated enterprise monthsValid licenseType newPricingPlanItem orderableItemId publiclyAvailable renewalAmount saleType renewalFrequency sku starter unitCount unitLabel } monthly { currency editionType entitionTypeIsDepercated price unitBlockSize unitLabel unitLimit unitStart } annual { currency editionType entitionTypeIsDepercated price unitBlockSize unitLabel unitLimit unitStart } } } } } `) req.Var("where", filter) var response OfferingDetailsResponse if err := papi.Client.Run(context.Background(), req, &response); err != nil { log.Fatal(err) } return response }
Now that we've created the catalog service request for OfferingDetails
query, we
can either fetch cloud or BTF product and app details. Only one product can be fetched
at a time.
Cloud products can be fetched by passing a cloud product filter to the FetchOfferingDetails
service
1 2package main import ( "encoding/json" "log" papiCatalogService "papi-client-go-example/service/catalog" ) func main() { /** Fetch OfferingDetails (Cloud) */ // Construct the filter filter := papiCatalogService.OfferingFilter{ CloudProductFilter: &papiCatalogService.CloudFilter{ Id: "6aab3f4e-cc71-474d-8d28-abef8b057808", }, } // Fetch the offering details response := papiCatalogService.FetchOfferingDetails(filter) // Convert the response to JSON res, err := json.Marshal(response) if err != nil { panic(err) } log.Println("FetchOfferingDetails (Cloud) | response: ", string(res)) }
BTF products can be fetched by passing a BTF product filter to the FetchOfferingDetails
service
1 2package main import ( "encoding/json" "log" papiCatalogService "papi-client-go-example/service/catalog" ) func main() { /** Fetch OfferingDetails (BTF) */ // Construct the filter filter := papiCatalogService.OfferingFilter{ BtfProductFilter: &papiCatalogService.BtfFilter{ ProductKey: "confluence", }, } // Fetch the offering details response := papiCatalogService.FetchOfferingDetails(filter) // Convert the response to JSON res, err := json.Marshal(response) if err != nil { panic(err) } log.Println("FetchOfferingDetails (BTF) | response: ", string(res)) }
Rate this page: