Skip to content

Commit be455ea

Browse files
committed
fwserver: add list resources to GetProviderSchema
1 parent 7a0dfb3 commit be455ea

12 files changed

+2683
-22
lines changed

internal/fwserver/server_getproviderschema.go

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type GetProviderSchemaResponse struct {
2525
DataSourceSchemas map[string]fwschema.Schema
2626
EphemeralResourceSchemas map[string]fwschema.Schema
2727
FunctionDefinitions map[string]function.Definition
28+
ListResourceSchemas map[string]fwschema.Schema
2829
Diagnostics diag.Diagnostics
2930
}
3031

@@ -33,62 +34,51 @@ func (s *Server) GetProviderSchema(ctx context.Context, req *GetProviderSchemaRe
3334
resp.ServerCapabilities = s.ServerCapabilities()
3435

3536
providerSchema, diags := s.ProviderSchema(ctx)
36-
3737
resp.Diagnostics.Append(diags...)
38-
3938
if diags.HasError() {
4039
return
4140
}
42-
4341
resp.Provider = providerSchema
4442

4543
providerMetaSchema, diags := s.ProviderMetaSchema(ctx)
46-
4744
resp.Diagnostics.Append(diags...)
48-
4945
if diags.HasError() {
5046
return
5147
}
52-
5348
resp.ProviderMeta = providerMetaSchema
5449

5550
resourceSchemas, diags := s.ResourceSchemas(ctx)
56-
5751
resp.Diagnostics.Append(diags...)
58-
5952
if resp.Diagnostics.HasError() {
6053
return
6154
}
62-
6355
resp.ResourceSchemas = resourceSchemas
6456

6557
dataSourceSchemas, diags := s.DataSourceSchemas(ctx)
66-
6758
resp.Diagnostics.Append(diags...)
68-
6959
if resp.Diagnostics.HasError() {
7060
return
7161
}
72-
7362
resp.DataSourceSchemas = dataSourceSchemas
7463

7564
functions, diags := s.FunctionDefinitions(ctx)
76-
7765
resp.Diagnostics.Append(diags...)
78-
7966
if resp.Diagnostics.HasError() {
8067
return
8168
}
82-
8369
resp.FunctionDefinitions = functions
8470

8571
ephemeralResourceSchemas, diags := s.EphemeralResourceSchemas(ctx)
86-
8772
resp.Diagnostics.Append(diags...)
88-
8973
if resp.Diagnostics.HasError() {
9074
return
9175
}
92-
9376
resp.EphemeralResourceSchemas = ephemeralResourceSchemas
77+
78+
listResourceSchemas, diags := s.ListResourceSchemas(ctx)
79+
resp.Diagnostics.Append(diags...)
80+
if resp.Diagnostics.HasError() {
81+
return
82+
}
83+
resp.ListResourceSchemas = listResourceSchemas
9484
}

internal/fwserver/server_getproviderschema_test.go

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1919
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
2020
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider"
21+
"github.com/hashicorp/terraform-plugin-framework/list"
22+
listschema "github.com/hashicorp/terraform-plugin-framework/list/schema"
2123
"github.com/hashicorp/terraform-plugin-framework/provider"
2224
"github.com/hashicorp/terraform-plugin-framework/provider/metaschema"
2325
providerschema "github.com/hashicorp/terraform-plugin-framework/provider/schema"
@@ -41,6 +43,7 @@ func TestServerGetProviderSchema(t *testing.T) {
4143
DataSourceSchemas: map[string]fwschema.Schema{},
4244
EphemeralResourceSchemas: map[string]fwschema.Schema{},
4345
FunctionDefinitions: map[string]function.Definition{},
46+
ListResourceSchemas: map[string]fwschema.Schema{},
4447
Provider: providerschema.Schema{},
4548
ResourceSchemas: map[string]fwschema.Schema{},
4649
ServerCapabilities: &fwserver.ServerCapabilities{
@@ -111,6 +114,7 @@ func TestServerGetProviderSchema(t *testing.T) {
111114
},
112115
EphemeralResourceSchemas: map[string]fwschema.Schema{},
113116
FunctionDefinitions: map[string]function.Definition{},
117+
ListResourceSchemas: map[string]fwschema.Schema{},
114118
Provider: providerschema.Schema{},
115119
ResourceSchemas: map[string]fwschema.Schema{},
116120
ServerCapabilities: &fwserver.ServerCapabilities{
@@ -318,6 +322,7 @@ func TestServerGetProviderSchema(t *testing.T) {
318322
},
319323
EphemeralResourceSchemas: map[string]fwschema.Schema{},
320324
FunctionDefinitions: map[string]function.Definition{},
325+
ListResourceSchemas: map[string]fwschema.Schema{},
321326
Provider: providerschema.Schema{},
322327
ResourceSchemas: map[string]fwschema.Schema{},
323328
ServerCapabilities: &fwserver.ServerCapabilities{
@@ -388,6 +393,7 @@ func TestServerGetProviderSchema(t *testing.T) {
388393
},
389394
},
390395
FunctionDefinitions: map[string]function.Definition{},
396+
ListResourceSchemas: map[string]fwschema.Schema{},
391397
Provider: providerschema.Schema{},
392398
ResourceSchemas: map[string]fwschema.Schema{},
393399
ServerCapabilities: &fwserver.ServerCapabilities{
@@ -601,6 +607,7 @@ func TestServerGetProviderSchema(t *testing.T) {
601607
},
602608
},
603609
FunctionDefinitions: map[string]function.Definition{},
610+
ListResourceSchemas: map[string]fwschema.Schema{},
604611
Provider: providerschema.Schema{},
605612
ResourceSchemas: map[string]fwschema.Schema{},
606613
ServerCapabilities: &fwserver.ServerCapabilities{
@@ -655,8 +662,9 @@ func TestServerGetProviderSchema(t *testing.T) {
655662
Return: function.StringReturn{},
656663
},
657664
},
658-
Provider: providerschema.Schema{},
659-
ResourceSchemas: map[string]fwschema.Schema{},
665+
ListResourceSchemas: map[string]fwschema.Schema{},
666+
Provider: providerschema.Schema{},
667+
ResourceSchemas: map[string]fwschema.Schema{},
660668
ServerCapabilities: &fwserver.ServerCapabilities{
661669
GetProviderSchemaOptional: true,
662670
MoveResourceState: true,
@@ -808,6 +816,158 @@ func TestServerGetProviderSchema(t *testing.T) {
808816
},
809817
},
810818
},
819+
"listresource-schemas": {
820+
server: &fwserver.Server{
821+
Provider: &testprovider.Provider{
822+
ListResourcesMethod: func(_ context.Context) []func() list.ListResource {
823+
return []func() list.ListResource{
824+
func() list.ListResource {
825+
return &testprovider.ListResource{
826+
ListResourceConfigSchemaMethod: func(_ context.Context, _ list.ListResourceSchemaRequest, resp *list.ListResourceSchemaResponse) {
827+
resp.Schema = listschema.Schema{
828+
Attributes: map[string]listschema.Attribute{
829+
"test1": listschema.StringAttribute{
830+
Required: true,
831+
},
832+
},
833+
}
834+
},
835+
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
836+
resp.TypeName = "test_resource"
837+
},
838+
}
839+
},
840+
}
841+
},
842+
ResourcesMethod: func(_ context.Context) []func() resource.Resource {
843+
return []func() resource.Resource{
844+
func() resource.Resource {
845+
return &testprovider.Resource{
846+
SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
847+
resp.Schema = resourceschema.Schema{
848+
Attributes: map[string]resourceschema.Attribute{
849+
"test1": resourceschema.StringAttribute{
850+
Required: true,
851+
},
852+
},
853+
}
854+
},
855+
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
856+
resp.TypeName = "test_resource"
857+
},
858+
}
859+
},
860+
}
861+
},
862+
},
863+
},
864+
request: &fwserver.GetProviderSchemaRequest{},
865+
expectedResponse: &fwserver.GetProviderSchemaResponse{
866+
DataSourceSchemas: map[string]fwschema.Schema{},
867+
EphemeralResourceSchemas: map[string]fwschema.Schema{},
868+
FunctionDefinitions: map[string]function.Definition{},
869+
ListResourceSchemas: map[string]fwschema.Schema{
870+
"test_resource": listschema.Schema{
871+
Attributes: map[string]listschema.Attribute{
872+
"test1": listschema.StringAttribute{
873+
Required: true,
874+
},
875+
},
876+
},
877+
},
878+
Provider: providerschema.Schema{},
879+
ResourceSchemas: map[string]fwschema.Schema{
880+
"test_resource": resourceschema.Schema{
881+
Attributes: map[string]resourceschema.Attribute{
882+
"test1": resourceschema.StringAttribute{
883+
Required: true,
884+
},
885+
},
886+
},
887+
},
888+
ServerCapabilities: &fwserver.ServerCapabilities{
889+
GetProviderSchemaOptional: true,
890+
MoveResourceState: true,
891+
PlanDestroy: true,
892+
},
893+
},
894+
},
895+
"listresource-schemas-invalid-attribute-name": {
896+
server: &fwserver.Server{
897+
Provider: &testprovider.Provider{
898+
ListResourcesMethod: func(_ context.Context) []func() list.ListResource {
899+
return []func() list.ListResource{
900+
func() list.ListResource {
901+
return &testprovider.ListResource{
902+
ListResourceConfigSchemaMethod: func(_ context.Context, _ list.ListResourceSchemaRequest, resp *list.ListResourceSchemaResponse) {
903+
resp.Schema = listschema.Schema{
904+
Attributes: map[string]listschema.Attribute{
905+
"$filter": listschema.StringAttribute{
906+
Required: true,
907+
},
908+
},
909+
}
910+
},
911+
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
912+
resp.TypeName = "test_resource"
913+
},
914+
}
915+
},
916+
}
917+
},
918+
ResourcesMethod: func(_ context.Context) []func() resource.Resource {
919+
return []func() resource.Resource{
920+
func() resource.Resource {
921+
return &testprovider.Resource{
922+
SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
923+
resp.Schema = resourceschema.Schema{
924+
Attributes: map[string]resourceschema.Attribute{
925+
"name": resourceschema.StringAttribute{
926+
Required: true,
927+
},
928+
},
929+
}
930+
},
931+
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
932+
resp.TypeName = "test_resource"
933+
},
934+
}
935+
},
936+
}
937+
},
938+
},
939+
},
940+
request: &fwserver.GetProviderSchemaRequest{},
941+
expectedResponse: &fwserver.GetProviderSchemaResponse{
942+
Provider: providerschema.Schema{},
943+
DataSourceSchemas: map[string]fwschema.Schema{},
944+
EphemeralResourceSchemas: map[string]fwschema.Schema{},
945+
FunctionDefinitions: map[string]function.Definition{},
946+
ResourceSchemas: map[string]fwschema.Schema{
947+
"test_resource": resourceschema.Schema{
948+
Attributes: map[string]resourceschema.Attribute{
949+
"name": resourceschema.StringAttribute{
950+
Required: true,
951+
},
952+
},
953+
},
954+
},
955+
ServerCapabilities: &fwserver.ServerCapabilities{
956+
GetProviderSchemaOptional: true,
957+
MoveResourceState: true,
958+
PlanDestroy: true,
959+
},
960+
Diagnostics: diag.Diagnostics{
961+
diag.NewErrorDiagnostic(
962+
"Invalid Attribute/Block Name",
963+
"When validating the schema, an implementation issue was found. "+
964+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
965+
"\"$filter\" at schema path \"$filter\" is an invalid attribute/block name. "+
966+
"Names must only contain lowercase alphanumeric characters (a-z, 0-9) and underscores (_).",
967+
),
968+
},
969+
},
970+
},
811971
"provider": {
812972
server: &fwserver.Server{
813973
Provider: &testprovider.Provider{
@@ -827,6 +987,7 @@ func TestServerGetProviderSchema(t *testing.T) {
827987
DataSourceSchemas: map[string]fwschema.Schema{},
828988
EphemeralResourceSchemas: map[string]fwschema.Schema{},
829989
FunctionDefinitions: map[string]function.Definition{},
990+
ListResourceSchemas: map[string]fwschema.Schema{},
830991
Provider: providerschema.Schema{
831992
Attributes: map[string]providerschema.Attribute{
832993
"test": providerschema.StringAttribute{
@@ -894,6 +1055,7 @@ func TestServerGetProviderSchema(t *testing.T) {
8941055
DataSourceSchemas: map[string]fwschema.Schema{},
8951056
EphemeralResourceSchemas: map[string]fwschema.Schema{},
8961057
FunctionDefinitions: map[string]function.Definition{},
1058+
ListResourceSchemas: map[string]fwschema.Schema{},
8971059
Provider: providerschema.Schema{},
8981060
ProviderMeta: metaschema.Schema{
8991061
Attributes: map[string]metaschema.Attribute{
@@ -990,6 +1152,7 @@ func TestServerGetProviderSchema(t *testing.T) {
9901152
DataSourceSchemas: map[string]fwschema.Schema{},
9911153
EphemeralResourceSchemas: map[string]fwschema.Schema{},
9921154
FunctionDefinitions: map[string]function.Definition{},
1155+
ListResourceSchemas: map[string]fwschema.Schema{},
9931156
Provider: providerschema.Schema{},
9941157
ResourceSchemas: map[string]fwschema.Schema{
9951158
"test_resource1": resourceschema.Schema{
@@ -1203,6 +1366,7 @@ func TestServerGetProviderSchema(t *testing.T) {
12031366
DataSourceSchemas: map[string]fwschema.Schema{},
12041367
EphemeralResourceSchemas: map[string]fwschema.Schema{},
12051368
FunctionDefinitions: map[string]function.Definition{},
1369+
ListResourceSchemas: map[string]fwschema.Schema{},
12061370
Provider: providerschema.Schema{},
12071371
ResourceSchemas: map[string]fwschema.Schema{
12081372
"testprovidertype_resource": resourceschema.Schema{

internal/fwserver/server_listresources.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99

1010
"github.com/hashicorp/terraform-plugin-framework/diag"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1112
"github.com/hashicorp/terraform-plugin-framework/internal/logging"
1213
"github.com/hashicorp/terraform-plugin-framework/list"
1314
"github.com/hashicorp/terraform-plugin-framework/provider"
@@ -22,12 +23,12 @@ func (s *Server) ListResourceFuncs(ctx context.Context) (map[string]func() list.
2223
return nil, nil
2324
}
2425

25-
logging.FrameworkTrace(ctx, "Checking ListResourceTypes lock")
26+
logging.FrameworkTrace(ctx, "Checking ListResourceFuncs lock")
2627
s.listResourceFuncsMutex.Lock()
2728
defer s.listResourceFuncsMutex.Unlock()
2829

2930
if s.listResourceFuncs != nil {
30-
return s.listResourceFuncs, s.resourceTypesDiags
31+
return s.listResourceFuncs, s.listResourceFuncsDiags
3132
}
3233

3334
providerTypeName := s.ProviderTypeName(ctx)
@@ -98,3 +99,37 @@ func (s *Server) ListResourceMetadatas(ctx context.Context) ([]ListResourceMetad
9899

99100
return resourceMetadatas, diags
100101
}
102+
103+
// ListResourceSchemas returns a map of ListResource Schemas for the
104+
// GetProviderSchema RPC without caching since not all schemas are guaranteed to
105+
// be necessary for later provider operations. The schema implementations are
106+
// also validated.
107+
func (s *Server) ListResourceSchemas(ctx context.Context) (map[string]fwschema.Schema, diag.Diagnostics) {
108+
listResourceSchemas := make(map[string]fwschema.Schema)
109+
listResourceFuncs, diags := s.ListResourceFuncs(ctx)
110+
111+
for typeName, listResourceFunc := range listResourceFuncs {
112+
listResource := listResourceFunc()
113+
schemaReq := list.ListResourceSchemaRequest{}
114+
schemaResp := list.ListResourceSchemaResponse{}
115+
116+
logging.FrameworkTrace(ctx, "Calling provider defined ListResource Schemas", map[string]interface{}{logging.KeyListResourceType: typeName})
117+
listResource.ListResourceConfigSchema(ctx, schemaReq, &schemaResp)
118+
logging.FrameworkTrace(ctx, "Called provider defined ListResource Schemas", map[string]interface{}{logging.KeyListResourceType: typeName})
119+
120+
diags.Append(schemaResp.Diagnostics...)
121+
if schemaResp.Diagnostics.HasError() {
122+
continue
123+
}
124+
125+
validateDiags := schemaResp.Schema.ValidateImplementation(ctx)
126+
diags.Append(validateDiags...)
127+
if validateDiags.HasError() {
128+
continue
129+
}
130+
131+
listResourceSchemas[typeName] = schemaResp.Schema
132+
}
133+
134+
return listResourceSchemas, diags
135+
}

0 commit comments

Comments
 (0)