Skip to content

Commit 101d12f

Browse files
authored
chore: Support generation of singleton resources with no delete operation (#3736)
1 parent 92598fc commit 101d12f

File tree

14 files changed

+546
-27
lines changed

14 files changed

+546
-27
lines changed

internal/serviceapi/projectsettingsapi/resource.go

Lines changed: 120 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/serviceapi/projectsettingsapi/resource_schema.go

Lines changed: 72 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/codegen/codespec/api_to_provider_spec_mapper.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,19 @@ func ToCodeSpecModel(atlasAdminAPISpecFilePath, configPath string, resourceName
2929

3030
resourceConfigsToIterate := configModel.Resources
3131
if resourceName != nil { // only generate a specific resource
32+
resource, ok := configModel.Resources[*resourceName]
33+
if !ok {
34+
return nil, fmt.Errorf("resource %s not found in config file", *resourceName)
35+
}
3236
resourceConfigsToIterate = map[string]config.Resource{
33-
*resourceName: configModel.Resources[*resourceName],
37+
*resourceName: resource,
3438
}
3539
}
3640

41+
if err := validateRequiredOperations(resourceConfigsToIterate); err != nil {
42+
return nil, err
43+
}
44+
3745
var results []Resource
3846
for name, resourceConfig := range resourceConfigsToIterate {
3947
log.Printf("Generating resource: %s", name)
@@ -49,6 +57,25 @@ func ToCodeSpecModel(atlasAdminAPISpecFilePath, configPath string, resourceName
4957
return &Model{Resources: results}, nil
5058
}
5159

60+
func validateRequiredOperations(resourceConfigs map[string]config.Resource) error {
61+
var validationErrors []error
62+
for name, resourceConfig := range resourceConfigs {
63+
if resourceConfig.Create == nil {
64+
validationErrors = append(validationErrors, fmt.Errorf("resource %s missing Create operation in config file", name))
65+
}
66+
if resourceConfig.Read == nil {
67+
validationErrors = append(validationErrors, fmt.Errorf("resource %s missing Read operation in config file", name))
68+
}
69+
if resourceConfig.Update == nil {
70+
validationErrors = append(validationErrors, fmt.Errorf("resource %s missing Update operation in config file", name))
71+
}
72+
}
73+
if len(validationErrors) > 0 {
74+
return errors.Join(validationErrors...)
75+
}
76+
return nil
77+
}
78+
5279
func apiSpecResourceToCodeSpecModel(oasResource APISpecResource, resourceConfig *config.Resource, name stringcase.SnakeCaseString) *Resource {
5380
createOp := oasResource.CreateOp
5481
updateOp := oasResource.UpdateOp
@@ -103,16 +130,19 @@ func getLatestVersionFromAPISpec(readOp *high.Operation) string {
103130

104131
func getOperationsFromConfig(resourceConfig *config.Resource) APIOperations {
105132
return APIOperations{
106-
Create: operationConfigToModel(resourceConfig.Create),
107-
Read: operationConfigToModel(resourceConfig.Read),
108-
Update: operationConfigToModel(resourceConfig.Update),
133+
Create: *operationConfigToModel(resourceConfig.Create),
134+
Read: *operationConfigToModel(resourceConfig.Read),
135+
Update: *operationConfigToModel(resourceConfig.Update),
109136
Delete: operationConfigToModel(resourceConfig.Delete),
110137
VersionHeader: resourceConfig.VersionHeader,
111138
}
112139
}
113140

114-
func operationConfigToModel(opConfig *config.APIOperation) APIOperation {
115-
return APIOperation{
141+
func operationConfigToModel(opConfig *config.APIOperation) *APIOperation {
142+
if opConfig == nil {
143+
return nil
144+
}
145+
return &APIOperation{
116146
HTTPMethod: opConfig.Method,
117147
Path: opConfig.Path,
118148
Wait: waitConfigToModel(opConfig.Wait),

tools/codegen/codespec/api_to_provider_spec_mapper_test.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func TestConvertToProviderSpec(t *testing.T) {
107107
Path: "/api/atlas/v2/groups/{groupId}/testResource",
108108
HTTPMethod: "PATCH",
109109
},
110-
Delete: codespec.APIOperation{
110+
Delete: &codespec.APIOperation{
111111
Path: "/api/atlas/v2/groups/{groupId}/testResource",
112112
HTTPMethod: "DELETE",
113113
},
@@ -323,7 +323,7 @@ func TestConvertToProviderSpec_nested(t *testing.T) {
323323
Path: "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/nestedTestResource",
324324
HTTPMethod: "PATCH",
325325
},
326-
Delete: codespec.APIOperation{
326+
Delete: &codespec.APIOperation{
327327
Path: "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/nestedTestResource",
328328
HTTPMethod: "DELETE",
329329
},
@@ -444,7 +444,7 @@ func TestConvertToProviderSpec_nested_schemaOverrides(t *testing.T) {
444444
Path: "/api/atlas/v2/groups/{projectId}/clusters/{clusterName}/nestedTestResource",
445445
HTTPMethod: "PATCH",
446446
},
447-
Delete: codespec.APIOperation{
447+
Delete: &codespec.APIOperation{
448448
Path: "/api/atlas/v2/groups/{projectId}/clusters/{clusterName}/nestedTestResource",
449449
HTTPMethod: "DELETE",
450450
},
@@ -500,7 +500,7 @@ func TestConvertToProviderSpec_pathParamPresentInPostRequest(t *testing.T) {
500500
Path: "/api/atlas/v2/groups/{groupId}/pathparaminpostreq/{specialParam}",
501501
HTTPMethod: "GET",
502502
},
503-
Delete: codespec.APIOperation{
503+
Delete: &codespec.APIOperation{
504504
Path: "/api/atlas/v2/groups/{groupId}/pathparaminpostreq/{specialParam}",
505505
HTTPMethod: "DELETE",
506506
},
@@ -517,6 +517,54 @@ func TestConvertToProviderSpec_pathParamPresentInPostRequest(t *testing.T) {
517517
runTestCase(t, tc)
518518
}
519519

520+
func TestConvertToProviderSpec_singletonResourceNoDeleteOperation(t *testing.T) {
521+
tc := convertToSpecTestCase{
522+
inputOpenAPISpecPath: testDataAPISpecPath,
523+
inputConfigPath: testDataConfigPath,
524+
inputResourceName: "test_singleton_resource_no_delete_op",
525+
526+
expectedResult: &codespec.Model{
527+
Resources: []codespec.Resource{{
528+
Schema: &codespec.Schema{
529+
Description: conversion.StringPtr("PATCH API description"),
530+
Attributes: codespec.Attributes{
531+
{
532+
Name: "flag",
533+
ComputedOptionalRequired: codespec.Optional,
534+
Bool: &codespec.BoolAttribute{},
535+
},
536+
{
537+
Name: "group_id",
538+
ComputedOptionalRequired: codespec.Required,
539+
String: &codespec.StringAttribute{},
540+
Description: conversion.StringPtr(testPathParamDesc),
541+
ReqBodyUsage: codespec.OmitAlways,
542+
},
543+
},
544+
},
545+
Name: "test_singleton_resource_no_delete_op",
546+
Operations: codespec.APIOperations{
547+
Create: codespec.APIOperation{
548+
Path: "/api/atlas/v2/groups/{groupId}/testSingletonResource",
549+
HTTPMethod: "PATCH",
550+
},
551+
Read: codespec.APIOperation{
552+
Path: "/api/atlas/v2/groups/{groupId}/testSingletonResource",
553+
HTTPMethod: "GET",
554+
},
555+
Update: codespec.APIOperation{
556+
Path: "/api/atlas/v2/groups/{groupId}/testSingletonResource",
557+
HTTPMethod: "PATCH",
558+
},
559+
Delete: nil,
560+
VersionHeader: "application/vnd.atlas.2023-01-01+json",
561+
},
562+
}},
563+
},
564+
}
565+
runTestCase(t, tc)
566+
}
567+
520568
func runTestCase(t *testing.T, tc convertToSpecTestCase) {
521569
t.Helper()
522570
result, err := codespec.ToCodeSpecModel(tc.inputOpenAPISpecPath, tc.inputConfigPath, &tc.inputResourceName)

tools/codegen/codespec/config.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ func applyAliasToPathParams(resource *Resource, aliases map[string]string) {
8888
resource.Operations.Create.Path = strings.ReplaceAll(resource.Operations.Create.Path, fmt.Sprintf("{%s}", originalCamel), fmt.Sprintf("{%s}", aliasCamel))
8989
resource.Operations.Read.Path = strings.ReplaceAll(resource.Operations.Read.Path, fmt.Sprintf("{%s}", originalCamel), fmt.Sprintf("{%s}", aliasCamel))
9090
resource.Operations.Update.Path = strings.ReplaceAll(resource.Operations.Update.Path, fmt.Sprintf("{%s}", originalCamel), fmt.Sprintf("{%s}", aliasCamel))
91-
resource.Operations.Delete.Path = strings.ReplaceAll(resource.Operations.Delete.Path, fmt.Sprintf("{%s}", originalCamel), fmt.Sprintf("{%s}", aliasCamel))
91+
if resource.Operations.Delete != nil {
92+
resource.Operations.Delete.Path = strings.ReplaceAll(resource.Operations.Delete.Path, fmt.Sprintf("{%s}", originalCamel), fmt.Sprintf("{%s}", aliasCamel))
93+
}
9294
}
9395
}
9496

tools/codegen/codespec/model.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ type Model struct {
1919
}
2020

2121
type Resource struct {
22+
Operations APIOperations
2223
Schema *Schema
2324
Name stringcase.SnakeCaseString
24-
Operations APIOperations
2525
}
2626

2727
type APIOperations struct {
28+
Delete *APIOperation
2829
Create APIOperation
2930
Read APIOperation
3031
Update APIOperation
31-
Delete APIOperation
3232
VersionHeader string
3333
}
3434

0 commit comments

Comments
 (0)