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 }