Files
libopenapi/renderer/mock_generator.go
2023-09-01 06:00:03 -04:00

151 lines
3.8 KiB
Go

// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package renderer
import (
"encoding/json"
"fmt"
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
"gopkg.in/yaml.v3"
"reflect"
)
const Example = "Example"
const Examples = "Examples"
const Schema = "Schema"
type MockType int
const (
MockJSON MockType = iota
MockYAML
)
type MockGenerator struct {
renderer *SchemaRenderer
mockType MockType
}
func NewMockGeneratorWithDictionary(dictionaryLocation string, mockType MockType) *MockGenerator {
renderer := CreateRendererUsingDictionary(dictionaryLocation)
return &MockGenerator{renderer: renderer, mockType: mockType}
}
func NewMockGenerator(mockType MockType) *MockGenerator {
renderer := CreateRendererUsingDefaultDictionary()
return &MockGenerator{renderer: renderer, mockType: mockType}
}
func (mg *MockGenerator) GenerateMock(mock any, name string) ([]byte, error) {
v := reflect.ValueOf(mock).Elem()
num := v.NumField()
fieldCount := 0
for i := 0; i < num; i++ {
fieldName := v.Type().Field(i).Name
switch fieldName {
case Example:
fieldCount++
case Examples:
fieldCount++
}
}
mockReady := false
// check if all fields are present, if so, we can generate a mock
if fieldCount == 2 {
mockReady = true
}
if !mockReady {
return nil, fmt.Errorf("mockable struct only contains %d of the required "+
"fields (%s, %s)", fieldCount, Example, Examples)
}
// if the value has an example, try and render it out as is.
exampleValue := v.FieldByName(Example).Interface()
if exampleValue != nil {
// try and serialize the example value
return mg.renderMock(exampleValue), nil
}
// if there is no example, but there are multi-examples.
examples := v.FieldByName(Examples)
examplesValue := examples.Interface()
if examplesValue != nil && !examples.IsNil() {
// cast examples to map[string]interface{}
examplesMap := examplesValue.(map[string]*highbase.Example)
// if the name is not empty, try and find the example by name
for k, exp := range examplesMap {
if k == name {
return mg.renderMock(exp.Value), nil
}
}
// if the name is empty, just return the first example
for _, exp := range examplesMap {
return mg.renderMock(exp.Value), nil
}
}
// no examples? no problem, we can try and generate a mock from the schema.
// check if this is a SchemaProxy, if not, then see if it has a Schema, if not, then we can't generate a mock.
var schemaValue *highbase.Schema
switch reflect.TypeOf(mock) {
case reflect.TypeOf(&highbase.Schema{}):
schemaValue = mock.(*highbase.Schema)
default:
if sv, ok := v.FieldByName(Schema).Interface().(*highbase.Schema); ok {
if sv != nil {
schemaValue = sv
}
}
if sv, ok := v.FieldByName(Schema).Interface().(*highbase.SchemaProxy); ok {
if sv != nil {
schemaValue = sv.Schema()
}
}
}
if schemaValue != nil {
renderMap := mg.renderer.RenderSchema(schemaValue)
if renderMap != nil {
return mg.renderMock(renderMap), nil
}
}
return nil, nil
}
func (mg *MockGenerator) renderMock(v any) []byte {
switch {
case mg.mockType == MockYAML:
return mg.renderMockYAML(v)
default:
return mg.renderMockJSON(v)
}
}
func (mg *MockGenerator) renderMockJSON(v any) []byte {
var data []byte
// determine the type, render properly.
switch reflect.ValueOf(v).Kind() {
case reflect.Map, reflect.Slice, reflect.Array, reflect.Struct, reflect.Ptr:
data, _ = json.Marshal(v)
default:
data = []byte(fmt.Sprint(v))
}
return data
}
func (mg *MockGenerator) renderMockYAML(v any) []byte {
var data []byte
// determine the type, render properly.
switch reflect.ValueOf(v).Kind() {
case reflect.Map, reflect.Slice, reflect.Array, reflect.Struct, reflect.Ptr:
data, _ = yaml.Marshal(v)
default:
data = []byte(fmt.Sprint(v))
}
return data
}