From d7a69bc8bc9aa941500fee772d9c3cb8dbbd86b4 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 8 May 2024 14:53:57 -0700 Subject: [PATCH 1/3] Add constructor functions --- Makefile | 5 ++- cmd/resources/resources.go | 32 ++++++++++++++ cmd/resources/resources_test.go | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 cmd/resources/resources_test.go diff --git a/Makefile b/Makefile index 5ce43965..b1341e04 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ -.PHONY: vendor +.PHONY: build generate log test vendor build: go build -o ldcli +generate: + go generate ./... + log: tail -f *.log diff --git a/cmd/resources/resources.go b/cmd/resources/resources.go index 38d75dd0..a417520c 100644 --- a/cmd/resources/resources.go +++ b/cmd/resources/resources.go @@ -59,6 +59,38 @@ func jsonString(s string) string { return string(bs) } +func NewResourceData(tag openapi3.Tag) ResourceData { + resourceName, _ := getResourceNames(tag.Name) + + return ResourceData{ + GoName: strcase.ToCamel(resourceName), + DisplayName: strings.ToLower(resourceName), + Description: jsonString(tag.Description), + Operations: make(map[string]OperationData, 0), + } +} + +func NewResources(tags openapi3.Tags) map[string]ResourceData { + resources := make(map[string]ResourceData) + for _, t := range tags { + if strings.Contains(t.Name, "(beta)") || + strings.ToLower(t.Name) == "other" || + strings.ToLower(t.Name) == "oauth2 clients" { + continue + } + + resourceName, resourceKey := getResourceNames(t.Name) + resources[resourceKey] = ResourceData{ + GoName: strcase.ToCamel(resourceName), + DisplayName: strings.ToLower(resourceName), + Description: jsonString(t.Description), + Operations: make(map[string]OperationData, 0), + } + } + + return resources +} + func GetTemplateData(fileName string) (TemplateData, error) { rawFile, err := os.ReadFile(fileName) if err != nil { diff --git a/cmd/resources/resources_test.go b/cmd/resources/resources_test.go new file mode 100644 index 00000000..9f24a64d --- /dev/null +++ b/cmd/resources/resources_test.go @@ -0,0 +1,75 @@ +package resources_test + +import ( + "testing" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "ldcli/cmd/resources" +) + +func TestNewResourceData(t *testing.T) { + t.Run("converts OpenAPI spec to a resource", func(t *testing.T) { + tag := openapi3.Tag{ + Name: "My resources", + Description: "description", + } + + resource := resources.NewResourceData(tag) + + assert.Equal(t, "MyResources", resource.GoName) + assert.Equal(t, "my resources", resource.DisplayName) + assert.Equal(t, `"description"`, resource.Description) + }) +} + +func TestNewResources(t *testing.T) { + t.Run("builds a map of resources", func(t *testing.T) { + tags := openapi3.Tags{ + { + Name: "My resources", + }, + { + Name: "My resources2", + }, + } + + rs := resources.NewResources(tags) + + require.Len(t, rs, 2) + assert.Equal(t, "MyResources", rs["my-resources"].GoName) + assert.Equal(t, "MyResources2", rs["my-resources-2"].GoName) + }) + + t.Run("skips certain endpoints", func(t *testing.T) { + tests := map[string]struct { + name string + }{ + "skips beta endpoints": { + name: "My resources (beta)", + }, + "skips oauth2- endpoints": { + name: "OAuth2 clients", + }, + "skips other endpoints": { + name: "Other", + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + tags := openapi3.Tags{ + { + Name: tt.name, + }, + } + + rs := resources.NewResources(tags) + + assert.Len(t, rs, 0) + }) + } + }) +} From 28a189d7e142abf294137ca0631f36c6ba2f885e Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 8 May 2024 14:58:18 -0700 Subject: [PATCH 2/3] Filter spec paths --- cmd/resources/resource_cmds.go | 145 --------------------------------- cmd/resources/resources.go | 30 ++----- 2 files changed, 9 insertions(+), 166 deletions(-) diff --git a/cmd/resources/resource_cmds.go b/cmd/resources/resource_cmds.go index bd6579f3..87097e57 100644 --- a/cmd/resources/resource_cmds.go +++ b/cmd/resources/resource_cmds.go @@ -124,22 +124,6 @@ func AddAllResourceCmds(rootCmd *cobra.Command, client resources.Client, analyti "\u003e ### Available for Pro and Enterprise plans\n\u003e\n\u003e Metrics is available to customers on a Pro or Enterprise plan. To learn more, [read about our pricing](https://launchdarkly.com/pricing/). To add metrics to your plan, [contact Sales](https://launchdarkly.com/contact-sales/).\n\nMetrics track flag behavior over time when an experiment is running. The data generated from experiments gives you more insight into the impact of a particular flag. To learn more, read [Metrics](https://docs.launchdarkly.com/home/metrics).\n\nUsing the metrics API, you can create, delete, and manage metrics.\n\n\u003e ### Are you importing metric events?\n\u003e\n\u003e If you want to import metric events into LaunchDarkly from an existing data source, use the metric import API. To learn more, read [Importing metric events](/home/metrics/import-metric-events).\n\n\u003e ### Metric keys and event keys are different\n\u003e\n\u003e LaunchDarkly automatically generates a metric key when you create a metric. You can use the metric key to identify the metric in API calls.\n\u003e\n\u003e Custom conversion/binary and custom numeric metrics also require an event key. You can set the event key to anything you want. Adding this event key to your codebase lets your SDK track actions customers take in your app as events. To learn more, read [Sending custom events](https://docs.launchdarkly.com/sdk/features/events).\n", ) - gen_Oauth2ClientsResourceCmd := NewResourceCmd( - rootCmd, - analyticsTracker, - "oauth-2-clients", - "Make requests (list, create, etc.) on oauth2 clients", - "The OAuth2 client API allows you to register a LaunchDarkly OAuth client for use in your own custom integrations. Registering a LaunchDarkly OAuth client allows you to use LaunchDarkly as an identity provider so that account members can log into your application with their LaunchDarkly account.\n\nYou can create and manage LaunchDarkly OAuth clients using the LaunchDarkly OAuth client API. This API acknowledges creation of your client with a response containing a one-time, unique `_clientSecret`. If you lose your client secret, you will have to register a new client. LaunchDarkly does not store client secrets in plain text.\n\nSeveral of the endpoints in the OAuth2 client API require an OAuth client ID. The OAuth client ID is returned as part of the [Create a LaunchDarkly OAuth 2.0 client](/tag/OAuth2-Clients#operation/createOAuth2Client) and [Get clients](/tag/OAuth2-Clients#operation/getOAuthClients) responses. It is the `_clientId` field, or the `_clientId` field of each element in the `items` array.\n\nYou must have _Admin_ privileges or an access token created by a member with _Admin_ privileges in order to be able to use this feature.\n\nPlease note that `redirectUri`s must be absolute URIs that conform to the https URI scheme. If you wish to register a client with a different URI scheme, please contact LaunchDarkly Support.\n", - ) - - gen_OtherResourceCmd := NewResourceCmd( - rootCmd, - analyticsTracker, - "other", - "Make requests (list, create, etc.) on other", - "Other requests available in the LaunchDarkly API. \n", - ) - gen_ProjectsResourceCmd := NewResourceCmd( rootCmd, analyticsTracker, @@ -3009,135 +2993,6 @@ func AddAllResourceCmds(rootCmd *cobra.Command, client resources.Client, analyti SupportsSemanticPatch: false, }) - NewOperationCmd(gen_Oauth2ClientsResourceCmd, client, OperationData{ - Short: "Create a LaunchDarkly OAuth 2.0 client", - Long: "Create (register) a LaunchDarkly OAuth2 client. OAuth2 clients allow you to build custom integrations using LaunchDarkly as your identity provider.", - Use: "create-o-auth-2-client", - Params: []Param{}, - HTTPMethod: "POST", - HasBody: true, - RequiresBody: true, - Path: "/api/v2/oauth/clients", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_Oauth2ClientsResourceCmd, client, OperationData{ - Short: "Delete OAuth 2.0 client", - Long: "Delete an existing OAuth 2.0 client by unique client ID.", - Use: "delete-o-auth-client", - Params: []Param{ - { - Name: "client-id", - In: "path", - Description: "The client ID", - Type: "string", - }, - }, - HTTPMethod: "DELETE", - HasBody: false, - RequiresBody: false, - Path: "/api/v2/oauth/clients/{clientId}", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_Oauth2ClientsResourceCmd, client, OperationData{ - Short: "Get client by ID", - Long: "Get a registered OAuth 2.0 client by unique client ID.", - Use: "get-o-auth-client-by-id", - Params: []Param{ - { - Name: "client-id", - In: "path", - Description: "The client ID", - Type: "string", - }, - }, - HTTPMethod: "GET", - HasBody: false, - RequiresBody: false, - Path: "/api/v2/oauth/clients/{clientId}", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_Oauth2ClientsResourceCmd, client, OperationData{ - Short: "Get clients", - Long: "Get all OAuth 2.0 clients registered by your account.", - Use: "list-o-auth-clients", - Params: []Param{}, - HTTPMethod: "GET", - HasBody: false, - RequiresBody: false, - Path: "/api/v2/oauth/clients", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_Oauth2ClientsResourceCmd, client, OperationData{ - Short: "Patch client by ID", - Long: "Patch an existing OAuth 2.0 client by client ID. Updating an OAuth2 client uses a [JSON patch](https://datatracker.ietf.org/doc/html/rfc6902) representation of the desired changes. To learn more, read [Updates](/#section/Overview/Updates). Only `name`, `description`, and `redirectUri` may be patched.", - Use: "update-o-auth-client", - Params: []Param{ - { - Name: "client-id", - In: "path", - Description: "The client ID", - Type: "string", - }, - }, - HTTPMethod: "PATCH", - HasBody: true, - RequiresBody: true, - Path: "/api/v2/oauth/clients/{clientId}", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_OtherResourceCmd, client, OperationData{ - Short: "Gets the public IP list", - Long: "Get a list of IP ranges the LaunchDarkly service uses. You can use this list to allow LaunchDarkly through your firewall. We post upcoming changes to this list in advance on our [status page](https://status.launchdarkly.com/). \u003cbr /\u003e\u003cbr /\u003eIn the sandbox, click 'Try it' and enter any string in the 'Authorization' field to test this endpoint.", - Use: "get-ips", - Params: []Param{}, - HTTPMethod: "GET", - HasBody: false, - RequiresBody: false, - Path: "/api/v2/public-ip-list", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_OtherResourceCmd, client, OperationData{ - Short: "Gets the OpenAPI spec in json", - Long: "Get the latest version of the OpenAPI specification for LaunchDarkly's API in JSON format. In the sandbox, click 'Try it' and enter any string in the 'Authorization' field to test this endpoint.", - Use: "get-openapi-spec", - Params: []Param{}, - HTTPMethod: "GET", - HasBody: false, - RequiresBody: false, - Path: "/api/v2/openapi.json", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_OtherResourceCmd, client, OperationData{ - Short: "Root resource", - Long: "Get all of the resource categories the API supports. In the sandbox, click 'Try it' and enter any string in the 'Authorization' field to test this endpoint.", - Use: "get-root", - Params: []Param{}, - HTTPMethod: "GET", - HasBody: false, - RequiresBody: false, - Path: "/api/v2", - SupportsSemanticPatch: false, - }) - - NewOperationCmd(gen_OtherResourceCmd, client, OperationData{ - Short: "Get version information", - Long: "Get the latest API version, the list of valid API versions in ascending order, and the version being used for this request. These are all in the external, date-based format.", - Use: "get-versions", - Params: []Param{}, - HTTPMethod: "GET", - HasBody: false, - RequiresBody: false, - Path: "/api/v2/versions", - SupportsSemanticPatch: false, - }) - NewOperationCmd(gen_ProjectsResourceCmd, client, OperationData{ Short: "Delete project", Long: "Delete a project by key. Use this endpoint with caution. Deleting a project will delete all associated environments and feature flags. You cannot delete the last project in an account.", diff --git a/cmd/resources/resources.go b/cmd/resources/resources.go index a417520c..e7dac15e 100644 --- a/cmd/resources/resources.go +++ b/cmd/resources/resources.go @@ -73,9 +73,7 @@ func NewResourceData(tag openapi3.Tag) ResourceData { func NewResources(tags openapi3.Tags) map[string]ResourceData { resources := make(map[string]ResourceData) for _, t := range tags { - if strings.Contains(t.Name, "(beta)") || - strings.ToLower(t.Name) == "other" || - strings.ToLower(t.Name) == "oauth2 clients" { + if shouldFilter(t.Name) { continue } @@ -91,6 +89,12 @@ func NewResources(tags openapi3.Tags) map[string]ResourceData { return resources } +func shouldFilter(name string) bool { + return strings.Contains(name, "(beta)") || + strings.ToLower(name) == "other" || + strings.ToLower(name) == "oauth2 clients" +} + func GetTemplateData(fileName string) (TemplateData, error) { rawFile, err := os.ReadFile(fileName) if err != nil { @@ -103,27 +107,11 @@ func GetTemplateData(fileName string) (TemplateData, error) { return TemplateData{}, err } - resources := make(map[string]ResourceData) - for _, r := range spec.Tags { - if strings.Contains(r.Name, "(beta)") { - // skip beta resources for now - continue - } - - resourceName, resourceKey := getResourceNames(r.Name) - resources[resourceKey] = ResourceData{ - GoName: strcase.ToCamel(resourceName), - DisplayName: strings.ToLower(resourceName), - Description: jsonString(r.Description), - Operations: make(map[string]OperationData, 0), - } - } - + resources := NewResources(spec.Tags) for path, pathItem := range spec.Paths.Map() { for method, op := range pathItem.Operations() { tag := op.Tags[0] // each op only has one tag - if strings.Contains(tag, "(beta)") { - // skip beta resources for now + if shouldFilter(tag) { continue } _, resourceKey := getResourceNames(tag) From 86e6906b49cb4fe843305ad0c2c9e8dbe6f7de42 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 8 May 2024 15:03:53 -0700 Subject: [PATCH 3/3] Move functions --- cmd/resources/resources.go | 72 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/cmd/resources/resources.go b/cmd/resources/resources.go index e7dac15e..7b58b096 100644 --- a/cmd/resources/resources.go +++ b/cmd/resources/resources.go @@ -59,42 +59,6 @@ func jsonString(s string) string { return string(bs) } -func NewResourceData(tag openapi3.Tag) ResourceData { - resourceName, _ := getResourceNames(tag.Name) - - return ResourceData{ - GoName: strcase.ToCamel(resourceName), - DisplayName: strings.ToLower(resourceName), - Description: jsonString(tag.Description), - Operations: make(map[string]OperationData, 0), - } -} - -func NewResources(tags openapi3.Tags) map[string]ResourceData { - resources := make(map[string]ResourceData) - for _, t := range tags { - if shouldFilter(t.Name) { - continue - } - - resourceName, resourceKey := getResourceNames(t.Name) - resources[resourceKey] = ResourceData{ - GoName: strcase.ToCamel(resourceName), - DisplayName: strings.ToLower(resourceName), - Description: jsonString(t.Description), - Operations: make(map[string]OperationData, 0), - } - } - - return resources -} - -func shouldFilter(name string) bool { - return strings.Contains(name, "(beta)") || - strings.ToLower(name) == "other" || - strings.ToLower(name) == "oauth2 clients" -} - func GetTemplateData(fileName string) (TemplateData, error) { rawFile, err := os.ReadFile(fileName) if err != nil { @@ -169,6 +133,42 @@ func GetTemplateData(fileName string) (TemplateData, error) { return TemplateData{Resources: resources}, nil } +func NewResourceData(tag openapi3.Tag) ResourceData { + resourceName, _ := getResourceNames(tag.Name) + + return ResourceData{ + GoName: strcase.ToCamel(resourceName), + DisplayName: strings.ToLower(resourceName), + Description: jsonString(tag.Description), + Operations: make(map[string]OperationData, 0), + } +} + +func NewResources(tags openapi3.Tags) map[string]ResourceData { + resources := make(map[string]ResourceData) + for _, t := range tags { + if shouldFilter(t.Name) { + continue + } + + resourceName, resourceKey := getResourceNames(t.Name) + resources[resourceKey] = ResourceData{ + GoName: strcase.ToCamel(resourceName), + DisplayName: strings.ToLower(resourceName), + Description: jsonString(t.Description), + Operations: make(map[string]OperationData, 0), + } + } + + return resources +} + +func shouldFilter(name string) bool { + return strings.Contains(name, "(beta)") || + strings.ToLower(name) == "other" || + strings.ToLower(name) == "oauth2 clients" +} + func NewResourceCmd(parentCmd *cobra.Command, analyticsTracker analytics.Tracker, resourceName, shortDescription, longDescription string) *cobra.Command { cmd := &cobra.Command{ Use: resourceName,