Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce internal/fwserver package and migrate GetProviderSchema #319

Merged
merged 1 commit into from May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/fromproto6/doc.go
@@ -0,0 +1,3 @@
// Package fromproto6 contains functions to convert from protocol version 6
// (tfprotov6) types to framework types.
package fromproto6
20 changes: 20 additions & 0 deletions internal/fromproto6/getproviderschema.go
@@ -0,0 +1,20 @@
package fromproto6

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

// GetProviderSchemaRequest returns the *fwserver.GetProviderSchemaRequest
// equivalent of a *tfprotov6.GetProviderSchemaRequest.
func GetProviderSchemaRequest(ctx context.Context, proto6 *tfprotov6.GetProviderSchemaRequest) *fwserver.GetProviderSchemaRequest {
if proto6 == nil {
return nil
}

fw := &fwserver.GetProviderSchemaRequest{}

return fw
}
43 changes: 43 additions & 0 deletions internal/fromproto6/getproviderschema_test.go
@@ -0,0 +1,43 @@
package fromproto6_test

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto6"
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

func TestGetProviderSchemaRequest(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
input *tfprotov6.GetProviderSchemaRequest
expected *fwserver.GetProviderSchemaRequest
}{
"nil": {
input: nil,
expected: nil,
},
"empty": {
input: &tfprotov6.GetProviderSchemaRequest{},
expected: &fwserver.GetProviderSchemaRequest{},
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := fromproto6.GetProviderSchemaRequest(context.Background(), testCase.input)

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
5 changes: 5 additions & 0 deletions internal/fwserver/doc.go
@@ -0,0 +1,5 @@
// Package fwserver contains the framework provider server implementation.
// This package should only ever contain framework-native types, while specific
// protocol version compatible implementations, such as proto6server, are
// implemented on top of this abstraction.
package fwserver
12 changes: 12 additions & 0 deletions internal/fwserver/server.go
@@ -0,0 +1,12 @@
package fwserver

import (
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

// Server implements the framework provider server. Protocol specific
// implementations wrap this handling along with calling all request and
// response type conversions.
type Server struct {
Provider tfsdk.Provider
}
120 changes: 120 additions & 0 deletions internal/fwserver/server_getproviderschema.go
@@ -0,0 +1,120 @@
package fwserver

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/logging"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

// GetProviderSchemaRequest is the framework server request for the
// GetProviderSchema RPC.
type GetProviderSchemaRequest struct{}

// GetProviderSchemaResponse is the framework server response for the
// GetProviderSchema RPC.
type GetProviderSchemaResponse struct {
Provider *tfsdk.Schema
ProviderMeta *tfsdk.Schema
ResourceSchemas map[string]*tfsdk.Schema
DataSourceSchemas map[string]*tfsdk.Schema
Diagnostics diag.Diagnostics
}

// GetProviderSchema implements the framework server GetProviderSchema RPC.
func (s *Server) GetProviderSchema(ctx context.Context, req *GetProviderSchemaRequest, resp *GetProviderSchemaResponse) {
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetSchema")
providerSchema, diags := s.Provider.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider GetSchema")

resp.Diagnostics.Append(diags...)

if diags.HasError() {
return
}

resp.Provider = &providerSchema

if pm, ok := s.Provider.(tfsdk.ProviderWithProviderMeta); ok {
logging.FrameworkTrace(ctx, "Provider implements ProviderWithProviderMeta")

logging.FrameworkDebug(ctx, "Calling provider defined Provider GetMetaSchema")
providerMetaSchema, diags := pm.GetMetaSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider GetMetaSchema")

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

resp.ProviderMeta = &providerMetaSchema
}

// TODO: Cache GetDataSources call
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/299
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetResources")
resourceSchemas, diags := s.Provider.GetResources(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider GetResources")

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

if len(resourceSchemas) > 0 {
resp.ResourceSchemas = map[string]*tfsdk.Schema{}
}

for k, v := range resourceSchemas {
// KeyResourceType field only necessary here since we are in GetProviderSchema RPC
logging.FrameworkTrace(ctx, "Found resource type", map[string]interface{}{logging.KeyResourceType: k})

logging.FrameworkDebug(ctx, "Calling provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: k})
schema, diags := v.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: k})

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

resp.ResourceSchemas[k] = &schema
}

// TODO: Cache GetDataSources call
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/299
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetDataSources")
dataSourceSchemas, diags := s.Provider.GetDataSources(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider GetDataSources")

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

if len(dataSourceSchemas) > 0 {
resp.DataSourceSchemas = map[string]*tfsdk.Schema{}
}

for k, v := range dataSourceSchemas {
// KeyDataSourceType field only necessary here since we are in GetProviderSchema RPC
logging.FrameworkTrace(ctx, "Found data source type", map[string]interface{}{logging.KeyDataSourceType: k})

logging.FrameworkDebug(ctx, "Calling provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: k})
schema, diags := v.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: k})

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

resp.DataSourceSchemas[k] = &schema
}
}
53 changes: 53 additions & 0 deletions internal/fwserver/server_getproviderschema_test.go
@@ -0,0 +1,53 @@
package fwserver_test

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/emptyprovider"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

// TODO: Migrate tfsdk.Provider bits of proto6server.testProviderServer to
// new internal/testing/provider.Provider that allows customization of all
// method implementations via struct fields. Then, create additional test
// cases in this unit test.
//
// For now this testing is covered by proto6server.GetProviderSchema.
//
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/215
func TestServerGetProviderSchema(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
server fwserver.Server
request *fwserver.GetProviderSchemaRequest
expectedResponse *fwserver.GetProviderSchemaResponse
}{
"empty-provider": {
server: fwserver.Server{
Provider: &emptyprovider.Provider{},
},
expectedResponse: &fwserver.GetProviderSchemaResponse{
Provider: &tfsdk.Schema{},
},
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

response := &fwserver.GetProviderSchemaResponse{}
testCase.server.GetProviderSchema(context.Background(), testCase.request, response)

if diff := cmp.Diff(response, testCase.expectedResponse); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}