@@ -14,6 +14,7 @@ import (
1414 "github.com/hashicorp/terraform-plugin-framework/function"
1515 "github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1616 "github.com/hashicorp/terraform-plugin-framework/internal/logging"
17+ "github.com/hashicorp/terraform-plugin-framework/list"
1718 "github.com/hashicorp/terraform-plugin-framework/provider"
1819 "github.com/hashicorp/terraform-plugin-framework/resource"
1920)
@@ -172,6 +173,20 @@ type Server struct {
172173 // Provider.Resources() method.
173174 resourceFuncs map [string ]func () resource.Resource
174175
176+ // listResourceFuncs is the cached Resource List functions for RPCs that need to
177+ // access resource lists. If not found, it will be fetched from the
178+ // Provider.ListResources() method.
179+ listResourceFuncs map [string ]func () list.ListResource
180+
181+ // listResourceTypesDiags is the cached Diagnostics obtained while populating
182+ // listResourceTypes. This is to ensure any warnings or errors are also
183+ // returned appropriately when fetching listResourceTypes.
184+ listResourceTypesDiags diag.Diagnostics
185+
186+ // listResourceTypesMutex is a mutex to protect concurrent listResourceTypes
187+ // access from race conditions.
188+ listResourceTypesMutex sync.Mutex
189+
175190 // resourceTypesDiags is the cached Diagnostics obtained while populating
176191 // resourceTypes. This is to ensure any warnings or errors are also
177192 // returned appropriately when fetching resourceTypes.
@@ -804,3 +819,88 @@ func (s *Server) ResourceIdentitySchemas(ctx context.Context) (map[string]fwsche
804819
805820 return resourceIdentitySchemas , diags
806821}
822+
823+ // ListResourceFuncs returns a map of ListResource functions. The results are
824+ // cached on first use.
825+ func (s * Server ) ListResourceFuncs (ctx context.Context ) (map [string ]func () list.ListResource , diag.Diagnostics ) {
826+ provider , ok := s .Provider .(provider.ProviderWithListResources )
827+ if ! ok {
828+ return nil , nil
829+ }
830+
831+ logging .FrameworkTrace (ctx , "Checking ListResourceTypes lock" )
832+ s .listResourceTypesMutex .Lock ()
833+ defer s .listResourceTypesMutex .Unlock ()
834+
835+ if s .listResourceFuncs != nil {
836+ return s .listResourceFuncs , s .resourceTypesDiags
837+ }
838+
839+ providerTypeName := s .ProviderTypeName (ctx )
840+ s .listResourceFuncs = make (map [string ]func () list.ListResource )
841+
842+ logging .FrameworkTrace (ctx , "Calling provider defined ListResources" )
843+ listResourceFuncSlice := provider .ListResources (ctx )
844+ logging .FrameworkTrace (ctx , "Called provider defined ListResources" )
845+
846+ for _ , listResourceFunc := range listResourceFuncSlice {
847+ listResource := listResourceFunc ()
848+
849+ metadataReq := resource.MetadataRequest {
850+ ProviderTypeName : providerTypeName ,
851+ }
852+ metadataResp := resource.MetadataResponse {}
853+ listResource .Metadata (ctx , metadataReq , & metadataResp )
854+
855+ typeName := metadataResp .TypeName
856+ if typeName == "" {
857+ s .listResourceTypesDiags .AddError (
858+ "ListResource Type Name Missing" ,
859+ fmt .Sprintf ("The %T ListResource returned an empty string from the Metadata method. " , listResource )+
860+ "This is always an issue with the provider and should be reported to the provider developers." ,
861+ )
862+ continue
863+ }
864+
865+ logging .FrameworkTrace (ctx , "Found resource type" , map [string ]interface {}{logging .KeyListResourceType : typeName }) // TODO: y?
866+
867+ if _ , ok := s .listResourceFuncs [typeName ]; ok {
868+ s .listResourceTypesDiags .AddError (
869+ "Duplicate ListResource Type Defined" ,
870+ fmt .Sprintf ("The %s ListResource type name was returned for multiple list resources. " , typeName )+
871+ "ListResource type names must be unique. " +
872+ "This is always an issue with the provider and should be reported to the provider developers." ,
873+ )
874+ continue
875+ }
876+
877+ if _ , ok := s .resourceFuncs [typeName ]; ! ok {
878+ s .listResourceTypesDiags .AddError (
879+ "ListResource Type Defined without a Matching Managed Resource Type" ,
880+ fmt .Sprintf ("The %s ListResource type name was returned, but no matching managed Resource type was defined. " , typeName )+
881+ "This is always an issue with the provider and should be reported to the provider developers." ,
882+ )
883+ continue
884+ }
885+
886+ s .listResourceFuncs [typeName ] = listResourceFunc
887+ }
888+
889+ return s .listResourceFuncs , s .listResourceTypesDiags
890+ }
891+
892+ // ListResourceMetadatas returns a slice of ListResourceMetadata for the GetMetadata
893+ // RPC.
894+ func (s * Server ) ListResourceMetadatas (ctx context.Context ) ([]ListResourceMetadata , diag.Diagnostics ) {
895+ resourceFuncs , diags := s .ListResourceFuncs (ctx )
896+
897+ resourceMetadatas := make ([]ListResourceMetadata , 0 , len (resourceFuncs ))
898+
899+ for typeName := range resourceFuncs {
900+ resourceMetadatas = append (resourceMetadatas , ListResourceMetadata {
901+ TypeName : typeName ,
902+ })
903+ }
904+
905+ return resourceMetadatas , diags
906+ }
0 commit comments