Skip to content

Commit 4d9998e

Browse files
- Multiple Schema analysis.
- Works with xml as well.
1 parent c87fa7f commit 4d9998e

File tree

9 files changed

+611
-147
lines changed

9 files changed

+611
-147
lines changed

anysdk/expectedResponse.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ type ExpectedResponse interface {
1010
GetOpenAPIDocKey() string
1111
GetObjectKey() string
1212
GetSchema() Schema
13+
GetProjectionMap() map[string]string
14+
GetProjection(string) (string, bool)
1315
getOverrideSchema() (*LocalSchemaRef, bool)
1416
getAsyncOverrideSchema() (*LocalSchemaRef, bool)
1517
setOverrideSchemaValue(Schema)
@@ -21,17 +23,33 @@ type ExpectedResponse interface {
2123
}
2224

2325
type standardExpectedResponse struct {
24-
OverrideBodyMediaType string `json:"overrideMediaType,omitempty" yaml:"overrideMediaType,omitempty"`
25-
AsyncOverrideBodyMediaType string `json:"asyncOverrideMediaType,omitempty" yaml:"asyncOverrideMediaType,omitempty"`
26-
BodyMediaType string `json:"mediaType,omitempty" yaml:"mediaType,omitempty"`
27-
OpenAPIDocKey string `json:"openAPIDocKey,omitempty" yaml:"openAPIDocKey,omitempty"`
28-
ObjectKey string `json:"objectKey,omitempty" yaml:"objectKey,omitempty"`
26+
OverrideBodyMediaType string `json:"overrideMediaType,omitempty" yaml:"overrideMediaType,omitempty"`
27+
AsyncOverrideBodyMediaType string `json:"asyncOverrideMediaType,omitempty" yaml:"asyncOverrideMediaType,omitempty"`
28+
BodyMediaType string `json:"mediaType,omitempty" yaml:"mediaType,omitempty"`
29+
OpenAPIDocKey string `json:"openAPIDocKey,omitempty" yaml:"openAPIDocKey,omitempty"`
30+
ObjectKey string `json:"objectKey,omitempty" yaml:"objectKey,omitempty"`
31+
ProjectionMap map[string]string `json:"projection_map,omitempty" yaml:"projection_map,omitempty"`
2932
Schema Schema
3033
OverrideSchema *LocalSchemaRef `json:"schema_override,omitempty" yaml:"schema_override,omitempty"`
3134
AsyncOverrideSchema *LocalSchemaRef `json:"async_schema_override,omitempty" yaml:"async_schema_override,omitempty"`
3235
Transform *standardTransform `json:"transform,omitempty" yaml:"transform,omitempty"`
3336
}
3437

38+
func (er *standardExpectedResponse) GetProjectionMap() map[string]string {
39+
if er.ProjectionMap == nil {
40+
return make(map[string]string)
41+
}
42+
return er.ProjectionMap
43+
}
44+
45+
func (er *standardExpectedResponse) GetProjection(key string) (string, bool) {
46+
if er.ProjectionMap == nil {
47+
return "", false
48+
}
49+
v, ok := er.ProjectionMap[key]
50+
return v, ok
51+
}
52+
3553
func (er *standardExpectedResponse) setBodyMediaType(s string) {
3654
er.BodyMediaType = s
3755
}

anysdk/operation_store.go

Lines changed: 17 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,10 @@ type StandardOperationStore interface {
144144
GetRequestBodyMediaType() string
145145
getRequestBodyMediaType() string
146146
getRequestBodyMediaTypeNormalised() string
147+
GetXMLDeclaration() string
147148
getXMLDeclaration() string
149+
GetXMLRootAnnotation() string
150+
GetXMLTransform() string
148151
// getRequestBodyAttributeLineage(string) (string, error)
149152
}
150153

@@ -180,6 +183,10 @@ func (op *standardOpenAPIOperationStore) GetInline() []string {
180183
return []string{}
181184
}
182185

186+
func (op *standardOpenAPIOperationStore) GetXMLDeclaration() string {
187+
return op.getXMLDeclaration()
188+
}
189+
183190
func (op *standardOpenAPIOperationStore) getXMLDeclaration() string {
184191
rv := ""
185192
if op.Request != nil {
@@ -201,6 +208,10 @@ func (op *standardOpenAPIOperationStore) getServiceNameForProvider() string {
201208
return ""
202209
}
203210

211+
func (op *standardOpenAPIOperationStore) GetXMLRootAnnotation() string {
212+
return op.getXMLRootAnnotation()
213+
}
214+
204215
func (op *standardOpenAPIOperationStore) getXMLRootAnnotation() string {
205216
rv := ""
206217
if op.Request != nil {
@@ -209,6 +220,10 @@ func (op *standardOpenAPIOperationStore) getXMLRootAnnotation() string {
209220
return rv
210221
}
211222

223+
func (op *standardOpenAPIOperationStore) GetXMLTransform() string {
224+
return op.getXMLTransform()
225+
}
226+
212227
func (op *standardOpenAPIOperationStore) getXMLTransform() string {
213228
rv := ""
214229
if op.Request != nil {
@@ -1198,15 +1213,15 @@ func (op *standardOpenAPIOperationStore) MarshalBody(body interface{}, expectedR
11981213
func (op *standardOpenAPIOperationStore) marshalBody(body interface{}, expectedRequest ExpectedRequest) ([]byte, error) {
11991214
mediaType := expectedRequest.GetBodyMediaType()
12001215
if expectedRequest.GetSchema() != nil {
1201-
mediaType = expectedRequest.GetSchema().extractMediaTypeSynonym(mediaType)
1216+
mediaType = expectedRequest.GetSchema().ExtractMediaTypeSynonym(mediaType)
12021217
}
12031218
switch mediaType {
12041219
case media.MediaTypeJson:
12051220
return json.Marshal(body)
12061221
case media.MediaTypeXML, media.MediaTypeTextXML:
12071222
return xmlmap.MarshalXMLUserInput(
12081223
body,
1209-
expectedRequest.GetSchema().getXMLALiasOrName(),
1224+
expectedRequest.GetSchema().GetXMLALiasOrName(),
12101225
op.getXMLTransform(),
12111226
op.getXMLDeclaration(),
12121227
op.getXMLRootAnnotation(),
@@ -1215,139 +1230,6 @@ func (op *standardOpenAPIOperationStore) marshalBody(body interface{}, expectedR
12151230
return nil, fmt.Errorf("media type = '%s' not supported", expectedRequest.GetBodyMediaType())
12161231
}
12171232

1218-
type AnalyzedInput interface {
1219-
GetQueryParams() map[string]any
1220-
GetHeaderParam(string) (string, bool)
1221-
GetPathParam(string) (string, bool)
1222-
GetServerVars() map[string]any
1223-
GetREquestBody() any
1224-
}
1225-
1226-
func (op *standardOpenAPIOperationStore) parameterizeFromAnalyzedInput(prov Provider, parentDoc Service, inputParams AnalyzedInput) (*openapi3filter.RequestValidationInput, error) {
1227-
1228-
params := op.OperationRef.Value.Parameters
1229-
pathParams := make(map[string]string)
1230-
q := make(url.Values)
1231-
prefilledHeader := make(http.Header)
1232-
1233-
queryParamsRemaining := inputParams.GetQueryParams()
1234-
for _, p := range params {
1235-
if p.Value == nil {
1236-
continue
1237-
}
1238-
name := p.Value.Name
1239-
1240-
if p.Value.In == openapi3.ParameterInHeader {
1241-
val, present := inputParams.GetHeaderParam(p.Value.Name)
1242-
if present {
1243-
prefilledHeader.Set(name, fmt.Sprintf("%v", val))
1244-
} else if p.Value != nil && p.Value.Schema != nil && p.Value.Schema.Value != nil && p.Value.Schema.Value.Default != nil {
1245-
prefilledHeader.Set(name, fmt.Sprintf("%v", p.Value.Schema.Value.Default))
1246-
} else if isOpenapi3ParamRequired(p.Value) {
1247-
return nil, fmt.Errorf("standardOpenAPIOperationStore.parameterize() failure; missing required header '%s'", name)
1248-
}
1249-
}
1250-
if p.Value.In == openapi3.ParameterInPath {
1251-
val, present := inputParams.GetPathParam(p.Value.Name)
1252-
if present {
1253-
pathParams[name] = fmt.Sprintf("%v", val)
1254-
}
1255-
if !present && isOpenapi3ParamRequired(p.Value) {
1256-
return nil, fmt.Errorf("standardOpenAPIOperationStore.parameterize() failure; missing required path parameter '%s'", name)
1257-
}
1258-
} else if p.Value.In == openapi3.ParameterInQuery {
1259-
1260-
pVal, present := queryParamsRemaining[p.Value.Name]
1261-
if present {
1262-
switch val := pVal.(type) {
1263-
case []interface{}:
1264-
for _, v := range val {
1265-
q.Add(name, fmt.Sprintf("%v", v))
1266-
}
1267-
default:
1268-
q.Set(name, fmt.Sprintf("%v", val))
1269-
}
1270-
delete(queryParamsRemaining, name)
1271-
}
1272-
}
1273-
}
1274-
for k, v := range queryParamsRemaining {
1275-
q.Set(k, fmt.Sprintf("%v", v))
1276-
delete(queryParamsRemaining, k)
1277-
}
1278-
openapiSvc, openapiSvcOk := op.OpenAPIService.(OpenAPIService)
1279-
if !openapiSvcOk {
1280-
return nil, fmt.Errorf("could not cast OpenAPIService to standardOpenAPIServiceStore")
1281-
}
1282-
router, err := queryrouter.NewRouter(openapiSvc.getT())
1283-
if err != nil {
1284-
return nil, err
1285-
}
1286-
servers, _ := op.getServers()
1287-
serverParams := inputParams.GetServerVars()
1288-
if err != nil {
1289-
return nil, err
1290-
}
1291-
sv, err := selectServer(servers, serverParams)
1292-
if err != nil {
1293-
return nil, err
1294-
}
1295-
contentTypeHeaderRequired := false
1296-
var bodyReader io.Reader
1297-
1298-
requestBody := inputParams.GetREquestBody()
1299-
1300-
predOne := !util.IsNil(requestBody)
1301-
predTwo := !util.IsNil(op.Request)
1302-
if predOne && predTwo {
1303-
b, err := op.marshalBody(requestBody, op.Request)
1304-
if err != nil {
1305-
return nil, err
1306-
}
1307-
bodyReader = bytes.NewReader(b)
1308-
contentTypeHeaderRequired = true
1309-
}
1310-
// TODO: clean up
1311-
sv = strings.TrimSuffix(sv, "/")
1312-
path := replaceSimpleStringVars(fmt.Sprintf("%s%s", sv, op.OperationRef.extractPathItem()), pathParams)
1313-
u, err := url.Parse(fmt.Sprintf("%s?%s", path, q.Encode()))
1314-
if strings.Contains(path, "?") {
1315-
if len(q) > 0 {
1316-
u, err = url.Parse(fmt.Sprintf("%s&%s", path, q.Encode()))
1317-
} else {
1318-
u, err = url.Parse(path)
1319-
}
1320-
}
1321-
if err != nil {
1322-
return nil, err
1323-
}
1324-
httpReq, err := http.NewRequest(strings.ToUpper(op.OperationRef.extractMethodItem()), u.String(), bodyReader)
1325-
if err != nil {
1326-
return nil, err
1327-
}
1328-
if contentTypeHeaderRequired {
1329-
if prefilledHeader.Get("Content-Type") != "" {
1330-
prefilledHeader.Set("Content-Type", op.Request.BodyMediaType)
1331-
}
1332-
}
1333-
httpReq.Header = prefilledHeader
1334-
route, checkedPathParams, err := router.FindRoute(httpReq)
1335-
if err != nil {
1336-
return nil, err
1337-
}
1338-
options := &openapi3filter.Options{
1339-
AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
1340-
}
1341-
// Validate request
1342-
requestValidationInput := &openapi3filter.RequestValidationInput{
1343-
Options: options,
1344-
PathParams: checkedPathParams,
1345-
Request: httpReq,
1346-
Route: route,
1347-
}
1348-
return requestValidationInput, nil
1349-
}
1350-
13511233
func (op *standardOpenAPIOperationStore) parameterize(prov Provider, parentDoc Service, inputParams HttpParameters, requestBody interface{}) (*openapi3filter.RequestValidationInput, error) {
13521234

13531235
params := op.OperationRef.Value.Parameters

anysdk/schema.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type Schema interface {
6767
getDescendent(path []string) (Schema, bool)
6868
getFatItemsSchema(srs openapi3.SchemaRefs) Schema
6969
getItemsRef() (*openapi3.SchemaRef, bool)
70+
GetXMLALiasOrName() string
7071
getXMLALiasOrName() string
7172
getKey() string
7273
getOpenapiSchema() (*openapi3.Schema, bool)
@@ -86,6 +87,7 @@ type Schema interface {
8687
setKey(string)
8788
setRawProperty(string, *openapi3.SchemaRef)
8889
setXml(interface{})
90+
ExtractMediaTypeSynonym(mediaType string) string
8991
extractMediaTypeSynonym(mediaType string) string // TODO: implement upwards-searchable configurable type set matching
9092
toFlatDescriptionMap(extended bool) map[string]interface{}
9193
unmarshalJSONResponseBody(body io.Reader, path string) (interface{}, interface{}, error)
@@ -466,6 +468,10 @@ func (s *standardSchema) getName() string {
466468
return getPathSuffix(s.key)
467469
}
468470

471+
func (s *standardSchema) GetXMLALiasOrName() string {
472+
return s.getXMLALiasOrName()
473+
}
474+
469475
func (s *standardSchema) getXMLALiasOrName() string {
470476
xa := s.getXmlAlias()
471477
if xa != "" {
@@ -791,6 +797,10 @@ func (schema *standardSchema) GetSelectSchema(itemsKey, mediaType string) (Schem
791797
return nil, "", fmt.Errorf("unable to complete schema.GetSelectSchema() for schema = '%v' and itemsKey = '%s'", schema, itemsKey)
792798
}
793799

800+
func (schema *standardSchema) ExtractMediaTypeSynonym(mediaType string) string {
801+
return schema.extractMediaTypeSynonym(mediaType)
802+
}
803+
794804
func (schema *standardSchema) extractMediaTypeSynonym(mediaType string) string {
795805
m, ok := media.DefaultMediaFuzzyMatcher.Find(mediaType)
796806
if ok {

anysdk/service.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type OpenAPIService interface {
3434
getPaginationRequestTokenSemantic() (TokenSemantic, bool)
3535
getPaginationResponseTokenSemantic() (TokenSemantic, bool)
3636
getQueryTransposeAlgorithm() string
37+
GetT() *openapi3.T
3738
getT() *openapi3.T
3839
iDiscoveryDoc()
3940
isObjectSchemaImplicitlyUnioned() bool
@@ -56,6 +57,10 @@ type localTemplatedService struct {
5657
Provider Provider `json:"-" yaml:"-"` // upwards traversal
5758
}
5859

60+
func (sv *localTemplatedService) GetT() *openapi3.T {
61+
return sv.OpenapiSvc
62+
}
63+
5964
func (sv *localTemplatedService) getT() *openapi3.T {
6065
return sv.OpenapiSvc
6166
}
@@ -174,6 +179,10 @@ func (sv *standardService) setResourceMap(rsc map[string]*standardResource) {
174179

175180
func (sv *standardService) iDiscoveryDoc() {}
176181

182+
func (sv *standardService) GetT() *openapi3.T {
183+
return sv.getT()
184+
}
185+
177186
func (sv *standardService) getT() *openapi3.T {
178187
return sv.T
179188
}

docs/aliasing.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Request and response **sub-component** (body, header) attributes can be exposed
4949
- Not exposing response header attributes at all.
5050
- Heuristics that effectively clobber any input attribute that collides with a server variable.
5151
- Routing request body attributes based on a prefix eg: `data__`. This is undesirable on the grounds of poor encapsulation and weakened semantics.
52+
- Interesting schema search implementation in `getSelectItemsSchema()`.
5253

5354
### Proposed HTTP implementation
5455

pkg/xmlmap/xmlmap.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,16 @@ func (s permissableMapWrapper) MarshalXML(e *xml.Encoder, start xml.StartElement
367367
tokens := []xml.Token{start}
368368

369369
for key, value := range s.m {
370-
t := xml.StartElement{Name: xml.Name{"", key}}
371-
tokens = append(tokens, t, xml.CharData(fmt.Sprintf("%v", value)), xml.EndElement{t.Name})
370+
t := xml.StartElement{Name: xml.Name{
371+
Space: "",
372+
Local: key,
373+
}}
374+
tokens = append(tokens, t, xml.CharData(fmt.Sprintf("%v", value)), xml.EndElement{Name: t.Name})
372375
}
373376

374-
tokens = append(tokens, xml.EndElement{start.Name})
377+
tokens = append(tokens, xml.EndElement{
378+
Name: start.Name,
379+
})
375380

376381
for _, t := range tokens {
377382
err := e.EncodeToken(t)

0 commit comments

Comments
 (0)