github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/api/converter.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/pkg/str"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/domain/version"
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/internal/model"
    14  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    15  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    16  )
    17  
    18  // VersionConverter converts Version between model.Version, graphql.Version and repo-layer version.Version
    19  //
    20  //go:generate mockery --name=VersionConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    21  type VersionConverter interface {
    22  	ToGraphQL(in *model.Version) *graphql.Version
    23  	InputFromGraphQL(in *graphql.VersionInput) *model.VersionInput
    24  	FromEntity(version version.Version) *model.Version
    25  	ToEntity(version model.Version) version.Version
    26  }
    27  
    28  // SpecConverter converts Specifications between the model.Spec service-layer representation and the graphql-layer representation graphql.APISpec.
    29  //
    30  //go:generate mockery --name=SpecConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    31  type SpecConverter interface {
    32  	ToGraphQLAPISpec(in *model.Spec) (*graphql.APISpec, error)
    33  	InputFromGraphQLAPISpec(in *graphql.APISpecInput) (*model.SpecInput, error)
    34  }
    35  
    36  type converter struct {
    37  	version       VersionConverter
    38  	specConverter SpecConverter
    39  }
    40  
    41  // NewConverter returns a new Converter that can later be used to make the conversions between the GraphQL, service, and repository layer representations of a Compass APIDefinition.
    42  func NewConverter(version VersionConverter, specConverter SpecConverter) *converter {
    43  	return &converter{version: version, specConverter: specConverter}
    44  }
    45  
    46  // ToGraphQL converts the provided service-layer representation of an APIDefinition to the graphql-layer one.
    47  func (c *converter) ToGraphQL(in *model.APIDefinition, spec *model.Spec, bundleRef *model.BundleReference) (*graphql.APIDefinition, error) {
    48  	if in == nil {
    49  		return nil, nil
    50  	}
    51  
    52  	s, err := c.specConverter.ToGraphQLAPISpec(spec)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	var (
    58  		bundleID            string
    59  		apiDefaultTargetURL string
    60  	)
    61  	if bundleRef != nil {
    62  		if bundleRef.BundleID != nil {
    63  			bundleID = *bundleRef.BundleID
    64  		}
    65  
    66  		if bundleRef.APIDefaultTargetURL != nil {
    67  			apiDefaultTargetURL = *bundleRef.APIDefaultTargetURL
    68  		}
    69  	}
    70  
    71  	return &graphql.APIDefinition{
    72  		BundleID:    bundleID,
    73  		Name:        in.Name,
    74  		Description: in.Description,
    75  		Spec:        s,
    76  		TargetURL:   apiDefaultTargetURL,
    77  		Group:       in.Group,
    78  		Version:     c.version.ToGraphQL(in.Version),
    79  		BaseEntity: &graphql.BaseEntity{
    80  			ID:        in.ID,
    81  			Ready:     in.Ready,
    82  			CreatedAt: timePtrToTimestampPtr(in.CreatedAt),
    83  			UpdatedAt: timePtrToTimestampPtr(in.UpdatedAt),
    84  			DeletedAt: timePtrToTimestampPtr(in.DeletedAt),
    85  			Error:     in.Error,
    86  		},
    87  	}, nil
    88  }
    89  
    90  // MultipleToGraphQL converts the provided service-layer representations of an APIDefinition to the graphql-layer ones.
    91  func (c *converter) MultipleToGraphQL(in []*model.APIDefinition, specs []*model.Spec, bundleRefs []*model.BundleReference) ([]*graphql.APIDefinition, error) {
    92  	if len(in) != len(specs) || len(in) != len(bundleRefs) || len(bundleRefs) != len(specs) {
    93  		return nil, errors.New("different apis, specs and bundleRefs count provided")
    94  	}
    95  
    96  	apis := make([]*graphql.APIDefinition, 0, len(in))
    97  	for i, a := range in {
    98  		if a == nil {
    99  			continue
   100  		}
   101  
   102  		api, err := c.ToGraphQL(a, specs[i], bundleRefs[i])
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  
   107  		apis = append(apis, api)
   108  	}
   109  
   110  	return apis, nil
   111  }
   112  
   113  // MultipleInputFromGraphQL converts the provided graphql-layer representations of an APIDefinition to the service-layer ones.
   114  func (c *converter) MultipleInputFromGraphQL(in []*graphql.APIDefinitionInput) ([]*model.APIDefinitionInput, []*model.SpecInput, error) {
   115  	apiDefs := make([]*model.APIDefinitionInput, 0, len(in))
   116  	specs := make([]*model.SpecInput, 0, len(in))
   117  
   118  	for _, item := range in {
   119  		api, spec, err := c.InputFromGraphQL(item)
   120  		if err != nil {
   121  			return nil, nil, err
   122  		}
   123  
   124  		apiDefs = append(apiDefs, api)
   125  		specs = append(specs, spec)
   126  	}
   127  
   128  	return apiDefs, specs, nil
   129  }
   130  
   131  // InputFromGraphQL converts the provided graphql-layer representation of an APIDefinition to the service-layer one.
   132  func (c *converter) InputFromGraphQL(in *graphql.APIDefinitionInput) (*model.APIDefinitionInput, *model.SpecInput, error) {
   133  	if in == nil {
   134  		return nil, nil, nil
   135  	}
   136  
   137  	spec, err := c.specConverter.InputFromGraphQLAPISpec(in.Spec)
   138  	if err != nil {
   139  		return nil, nil, err
   140  	}
   141  
   142  	return &model.APIDefinitionInput{
   143  		Name:         in.Name,
   144  		Description:  in.Description,
   145  		TargetURLs:   ConvertTargetURLToJSONArray(in.TargetURL),
   146  		Group:        in.Group,
   147  		VersionInput: c.version.InputFromGraphQL(in.Version),
   148  	}, spec, nil
   149  }
   150  
   151  // FromEntity converts the provided Entity repo-layer representation of an APIDefinition to the service-layer representation model.APIDefinition.
   152  func (c *converter) FromEntity(entity *Entity) *model.APIDefinition {
   153  	return &model.APIDefinition{
   154  		ApplicationID:                           repo.StringPtrFromNullableString(entity.ApplicationID),
   155  		ApplicationTemplateVersionID:            repo.StringPtrFromNullableString(entity.ApplicationTemplateVersionID),
   156  		PackageID:                               repo.StringPtrFromNullableString(entity.PackageID),
   157  		Name:                                    entity.Name,
   158  		Description:                             repo.StringPtrFromNullableString(entity.Description),
   159  		TargetURLs:                              repo.JSONRawMessageFromNullableString(entity.TargetURLs),
   160  		Group:                                   repo.StringPtrFromNullableString(entity.Group),
   161  		OrdID:                                   repo.StringPtrFromNullableString(entity.OrdID),
   162  		LocalTenantID:                           repo.StringPtrFromNullableString(entity.LocalTenantID),
   163  		ShortDescription:                        repo.StringPtrFromNullableString(entity.ShortDescription),
   164  		SystemInstanceAware:                     repo.BoolPtrFromNullableBool(entity.SystemInstanceAware),
   165  		PolicyLevel:                             repo.StringPtrFromNullableString(entity.PolicyLevel),
   166  		CustomPolicyLevel:                       repo.StringPtrFromNullableString(entity.CustomPolicyLevel),
   167  		APIProtocol:                             repo.StringPtrFromNullableString(entity.APIProtocol),
   168  		Tags:                                    repo.JSONRawMessageFromNullableString(entity.Tags),
   169  		Countries:                               repo.JSONRawMessageFromNullableString(entity.Countries),
   170  		Links:                                   repo.JSONRawMessageFromNullableString(entity.Links),
   171  		APIResourceLinks:                        repo.JSONRawMessageFromNullableString(entity.APIResourceLinks),
   172  		ReleaseStatus:                           repo.StringPtrFromNullableString(entity.ReleaseStatus),
   173  		SunsetDate:                              repo.StringPtrFromNullableString(entity.SunsetDate),
   174  		Successors:                              repo.JSONRawMessageFromNullableString(entity.Successors),
   175  		ChangeLogEntries:                        repo.JSONRawMessageFromNullableString(entity.ChangeLogEntries),
   176  		Labels:                                  repo.JSONRawMessageFromNullableString(entity.Labels),
   177  		Visibility:                              &entity.Visibility,
   178  		Disabled:                                repo.BoolPtrFromNullableBool(entity.Disabled),
   179  		PartOfProducts:                          repo.JSONRawMessageFromNullableString(entity.PartOfProducts),
   180  		LineOfBusiness:                          repo.JSONRawMessageFromNullableString(entity.LineOfBusiness),
   181  		Industry:                                repo.JSONRawMessageFromNullableString(entity.Industry),
   182  		ImplementationStandard:                  repo.StringPtrFromNullableString(entity.ImplementationStandard),
   183  		CustomImplementationStandard:            repo.StringPtrFromNullableString(entity.CustomImplementationStandard),
   184  		CustomImplementationStandardDescription: repo.StringPtrFromNullableString(entity.CustomImplementationStandardDescription),
   185  		Version:                                 c.version.FromEntity(entity.Version),
   186  		Extensible:                              repo.JSONRawMessageFromNullableString(entity.Extensible),
   187  		ResourceHash:                            repo.StringPtrFromNullableString(entity.ResourceHash),
   188  		Hierarchy:                               repo.JSONRawMessageFromNullableString(entity.Hierarchy),
   189  		SupportedUseCases:                       repo.JSONRawMessageFromNullableString(entity.SupportedUseCases),
   190  		DocumentationLabels:                     repo.JSONRawMessageFromNullableString(entity.DocumentationLabels),
   191  		BaseEntity: &model.BaseEntity{
   192  			ID:        entity.ID,
   193  			Ready:     entity.Ready,
   194  			CreatedAt: entity.CreatedAt,
   195  			UpdatedAt: entity.UpdatedAt,
   196  			DeletedAt: entity.DeletedAt,
   197  			Error:     repo.StringPtrFromNullableString(entity.Error),
   198  		},
   199  	}
   200  }
   201  
   202  // ToEntity converts the provided service-layer representation of an APIDefinition to the repository-layer one.
   203  func (c *converter) ToEntity(apiModel *model.APIDefinition) *Entity {
   204  	visibility := apiModel.Visibility
   205  	if visibility == nil {
   206  		visibility = str.Ptr("public")
   207  	}
   208  
   209  	return &Entity{
   210  		ApplicationID:                           repo.NewNullableString(apiModel.ApplicationID),
   211  		ApplicationTemplateVersionID:            repo.NewNullableString(apiModel.ApplicationTemplateVersionID),
   212  		PackageID:                               repo.NewNullableString(apiModel.PackageID),
   213  		Name:                                    apiModel.Name,
   214  		Description:                             repo.NewNullableString(apiModel.Description),
   215  		Group:                                   repo.NewNullableString(apiModel.Group),
   216  		TargetURLs:                              repo.NewNullableStringFromJSONRawMessage(apiModel.TargetURLs),
   217  		OrdID:                                   repo.NewNullableString(apiModel.OrdID),
   218  		LocalTenantID:                           repo.NewNullableString(apiModel.LocalTenantID),
   219  		ShortDescription:                        repo.NewNullableString(apiModel.ShortDescription),
   220  		SystemInstanceAware:                     repo.NewNullableBool(apiModel.SystemInstanceAware),
   221  		PolicyLevel:                             repo.NewNullableString(apiModel.PolicyLevel),
   222  		CustomPolicyLevel:                       repo.NewNullableString(apiModel.CustomPolicyLevel),
   223  		APIProtocol:                             repo.NewNullableString(apiModel.APIProtocol),
   224  		Tags:                                    repo.NewNullableStringFromJSONRawMessage(apiModel.Tags),
   225  		Countries:                               repo.NewNullableStringFromJSONRawMessage(apiModel.Countries),
   226  		Links:                                   repo.NewNullableStringFromJSONRawMessage(apiModel.Links),
   227  		APIResourceLinks:                        repo.NewNullableStringFromJSONRawMessage(apiModel.APIResourceLinks),
   228  		ReleaseStatus:                           repo.NewNullableString(apiModel.ReleaseStatus),
   229  		SunsetDate:                              repo.NewNullableString(apiModel.SunsetDate),
   230  		Successors:                              repo.NewNullableStringFromJSONRawMessage(apiModel.Successors),
   231  		ChangeLogEntries:                        repo.NewNullableStringFromJSONRawMessage(apiModel.ChangeLogEntries),
   232  		Labels:                                  repo.NewNullableStringFromJSONRawMessage(apiModel.Labels),
   233  		Visibility:                              *visibility,
   234  		Disabled:                                repo.NewNullableBool(apiModel.Disabled),
   235  		PartOfProducts:                          repo.NewNullableStringFromJSONRawMessage(apiModel.PartOfProducts),
   236  		LineOfBusiness:                          repo.NewNullableStringFromJSONRawMessage(apiModel.LineOfBusiness),
   237  		Industry:                                repo.NewNullableStringFromJSONRawMessage(apiModel.Industry),
   238  		ImplementationStandard:                  repo.NewNullableString(apiModel.ImplementationStandard),
   239  		CustomImplementationStandard:            repo.NewNullableString(apiModel.CustomImplementationStandard),
   240  		CustomImplementationStandardDescription: repo.NewNullableString(apiModel.CustomImplementationStandardDescription),
   241  		Version:                                 c.convertVersionToEntity(apiModel.Version),
   242  		Extensible:                              repo.NewNullableStringFromJSONRawMessage(apiModel.Extensible),
   243  		ResourceHash:                            repo.NewNullableString(apiModel.ResourceHash),
   244  		Hierarchy:                               repo.NewNullableStringFromJSONRawMessage(apiModel.Hierarchy),
   245  		DocumentationLabels:                     repo.NewNullableStringFromJSONRawMessage(apiModel.DocumentationLabels),
   246  		SupportedUseCases:                       repo.NewNullableStringFromJSONRawMessage(apiModel.SupportedUseCases),
   247  		BaseEntity: &repo.BaseEntity{
   248  			ID:        apiModel.ID,
   249  			Ready:     apiModel.Ready,
   250  			CreatedAt: apiModel.CreatedAt,
   251  			UpdatedAt: apiModel.UpdatedAt,
   252  			DeletedAt: apiModel.DeletedAt,
   253  			Error:     repo.NewNullableString(apiModel.Error),
   254  		},
   255  	}
   256  }
   257  
   258  func (c *converter) convertVersionToEntity(inVer *model.Version) version.Version {
   259  	if inVer == nil {
   260  		return version.Version{}
   261  	}
   262  
   263  	return c.version.ToEntity(*inVer)
   264  }
   265  
   266  func timePtrToTimestampPtr(time *time.Time) *graphql.Timestamp {
   267  	if time == nil {
   268  		return nil
   269  	}
   270  
   271  	t := graphql.Timestamp(*time)
   272  	return &t
   273  }
   274  
   275  // ExtractTargetURLFromJSONArray extracts targetURL into a string from a JSON array representation.
   276  func ExtractTargetURLFromJSONArray(jsonTargetURL json.RawMessage) string {
   277  	strTargetURL := string(jsonTargetURL)
   278  	strTargetURL = strings.TrimPrefix(strTargetURL, `["`)
   279  	strTargetURL = strings.TrimSuffix(strTargetURL, `"]`)
   280  
   281  	return strTargetURL
   282  }
   283  
   284  // ConvertTargetURLToJSONArray converts targetURL string value to a JSON array.
   285  func ConvertTargetURLToJSONArray(targetURL string) json.RawMessage {
   286  	if targetURL == "" {
   287  		return nil
   288  	}
   289  
   290  	return json.RawMessage(`["` + targetURL + `"]`)
   291  }