Skip to content

Commit 7ff7c75

Browse files
better-analysis
Summary: - Strategy for `aws` `v4` signing in comments. - Strategy for namespace and aliasing. - Aliasing ideas expanding. - Beginnings of address space analysis. - Unit tests working. - Canoncial test coverage. - Faster analysis.
1 parent 92a3123 commit 7ff7c75

File tree

72 files changed

+337816
-55
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+337816
-55
lines changed

anysdk/addressable.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ func (ns *namedSchema) GetName() string {
1919
return ns.name
2020
}
2121

22+
func (ns *namedSchema) GetAlias() string {
23+
if ns.s != nil {
24+
return ns.s.GetAlias()
25+
}
26+
return ""
27+
}
28+
2229
func (ns *namedSchema) GetSchema() (Schema, bool) {
2330
return ns.s, true
2431
}
@@ -65,6 +72,7 @@ type Addressable interface {
6572
ConditionIsValid(lhs string, rhs interface{}) bool
6673
GetLocation() string
6774
GetName() string
75+
GetAlias() string
6876
GetSchema() (Schema, bool)
6977
GetType() string
7078
IsRequired() bool

anysdk/const.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
ExtensionKeyProvider string = "x-stackql-provider"
2222
ExtensionKeyResources string = "x-stackQL-resources"
2323
ExtensionKeyStringOnly string = "x-stackQL-stringOnly"
24+
ExtensionKeyAlias string = "x-stackQL-alias"
2425
)
2526

2627
const (

anysdk/loader.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ func (l *standardLoader) mergeResources(svc OpenAPIService, rscMap map[string]Re
317317
if rsc.GetServiceDocPath() != nil {
318318
sr = rsc.GetServiceDocPath()
319319
}
320-
err := l.mergeResource(svc, rsc, sr)
320+
err := l.mergeResource(k, svc, rsc, sr)
321321
if err != nil {
322322
return err
323323
}
@@ -330,7 +330,7 @@ func (l *standardLoader) mergeResourcesScoped(svc OpenAPIService, svcUrl string,
330330
scopedMap := make(map[string]Resource)
331331
for k, rsc := range rr.GetResources() {
332332
if rr.ObtainServiceDocUrl(k) == svcUrl {
333-
err := l.mergeResource(svc, rsc, &ServiceRef{Ref: svcUrl})
333+
err := l.mergeResource(k, svc, rsc, &ServiceRef{Ref: svcUrl})
334334
if err != nil {
335335
return err
336336
}
@@ -349,14 +349,28 @@ func (l *standardLoader) mergeResourcesScoped(svc OpenAPIService, svcUrl string,
349349
return nil
350350
}
351351

352-
func (l *standardLoader) mergeResource(svc OpenAPIService,
352+
func (l *standardLoader) mergeResource(
353+
rscKey string,
354+
svc OpenAPIService,
353355
rsc Resource,
354356
sr *ServiceRef,
355357
) error {
356358
rsc.setService(svc) // must happen before resolving inverses
359+
existingMethods := make(Methods)
360+
existingResource, existingResourceErr := svc.GetResource(rscKey)
361+
if existingResourceErr == nil && existingResource != nil {
362+
existingMethods = existingResource.GetMethods()
363+
}
357364
for k, vOp := range rsc.GetMethods() {
358365
v := vOp
359366
v.setMethodKey(k)
367+
existingMethod, existingMethodExists := existingMethods[k]
368+
if existingMethodExists {
369+
v = existingMethod
370+
v.setMethodKey(k)
371+
v.setResource(rsc)
372+
continue
373+
}
360374
// TODO: replicate this for the damned inverse
361375
err := l.resolveOperationRef(svc, rsc, &v, v.GetPathRef(), sr)
362376
if err != nil {
@@ -763,6 +777,13 @@ func (loader *standardLoader) resolveOperationRef(doc OpenAPIService, rsc Resour
763777
}
764778

765779
component.setOperationRef(&OperationRef{Value: op, Ref: component.GetOperationRef().Ref})
780+
response, responseExists := component.GetResponse()
781+
if responseExists {
782+
err = loader.resolveExpectedResponse(doc, component.GetOperationRef().Value, response)
783+
if err != nil {
784+
return err
785+
}
786+
}
766787
component.setPathItem(pi)
767788
err = loader.extractAndMergeQueryTransposeOpLevel(component)
768789
if err != nil {

anysdk/operation_store.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ type OperationStore interface {
110110

111111
type StandardOperationStore interface {
112112
OperationStore
113+
// Assist analysis
114+
GetRequestBodyAttributesNoRename() (map[string]Addressable, error)
115+
GetSchemaAtPath(key string) (Schema, error)
116+
GetSelectItemsKeySimple() string
117+
LookupSelectItemsKey() string
113118
//
114119
getQueryTransposeAlgorithm() string
115120
getRequiredNonBodyParameters() map[string]Addressable
@@ -624,6 +629,10 @@ func (m *standardOpenAPIOperationStore) getUnionRequiredParameters() (map[string
624629
return m.Resource.getUnionRequiredParameters(m)
625630
}
626631

632+
func (m *standardOpenAPIOperationStore) GetSelectItemsKeySimple() string {
633+
return m.getSelectItemsKeySimple()
634+
}
635+
627636
func (m *standardOpenAPIOperationStore) getSelectItemsKeySimple() string {
628637
if m.Response != nil {
629638
return m.Response.ObjectKey
@@ -1048,6 +1057,10 @@ func (m *standardOpenAPIOperationStore) getName() string {
10481057
return m.MethodKey
10491058
}
10501059

1060+
func (m *standardOpenAPIOperationStore) GetRequestBodyAttributesNoRename() (map[string]Addressable, error) {
1061+
return m.getRequestBodyAttributesNoRename()
1062+
}
1063+
10511064
func (m *standardOpenAPIOperationStore) ToPresentationMap(extended bool) map[string]interface{} {
10521065
requiredParams := m.getRequiredNonBodyParameters()
10531066
var requiredParamNames []string
@@ -1415,6 +1428,19 @@ func (op *standardOpenAPIOperationStore) GetSelectSchemaAndObjectPath() (Schema,
14151428
return nil, "", fmt.Errorf("no response body for operation = %s", op.GetName())
14161429
}
14171430

1431+
func (op *standardOpenAPIOperationStore) GetSchemaAtPath(path string) (Schema, error) {
1432+
k := path
1433+
if op.Response != nil && op.Response.OverrideSchema != nil && op.Response.OverrideSchema.Value != nil {
1434+
rv, _, err := op.Response.OverrideSchema.Value.getSelectItemsSchema(k, op.Response.OverrideBodyMediaType)
1435+
return rv, err
1436+
}
1437+
if op.Response != nil && op.Response.Schema != nil {
1438+
rv, _, err := op.Response.Schema.getSelectItemsSchema(k, op.getOptimalResponseMediaType())
1439+
return rv, err
1440+
}
1441+
return nil, fmt.Errorf("no response body for operation = %s", op.GetName())
1442+
}
1443+
14181444
type ProcessedOperationResponse interface {
14191445
GetResponse() (response.Response, bool)
14201446
GetReversal() (HTTPPreparator, bool)
@@ -1551,6 +1577,10 @@ func (op *standardOpenAPIOperationStore) ProcessResponse(httpResponse *http.Resp
15511577
return newStandardOperationResponse(rv, reversal), err
15521578
}
15531579

1580+
func (ops *standardOpenAPIOperationStore) LookupSelectItemsKey() string {
1581+
return ops.lookupSelectItemsKey()
1582+
}
1583+
15541584
func (ops *standardOpenAPIOperationStore) lookupSelectItemsKey() string {
15551585
s := ops.getSelectItemsKeySimple()
15561586
if s != "" {

anysdk/params.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ func (p *standardParameter) GetName() string {
4242
return p.Name
4343
}
4444

45+
func (p *standardParameter) GetAlias() string {
46+
return getAliasFromExtensions(p.ExtensionProps)
47+
}
48+
4549
func (p *standardParameter) GetLocation() string {
4650
return p.In
4751
}
@@ -72,6 +76,11 @@ func (p *standardParameter) GetType() string {
7276
func (p parameters) getParameterFromInSubset(key, inSubset string) (Addressable, bool) {
7377
for _, paramRef := range p.Parameters {
7478
param := paramRef.Value
79+
paramAlias := getAliasFromExtensions(param.ExtensionProps)
80+
hasAlias := paramAlias != ""
81+
if hasAlias && param.In == inSubset && paramAlias == key {
82+
return NewParameter(param, p.svc), true
83+
}
7584
if param.In == inSubset && param.Name == key {
7685
return NewParameter(param, p.svc), true
7786
}

anysdk/resource.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@ type Resource interface {
3131
GetFirstMethodFromSQLVerb(sqlVerb string) (StandardOperationStore, string, bool)
3232
GetFirstMethodMatchFromSQLVerb(sqlVerb string, parameters map[string]interface{}) (StandardOperationStore, map[string]interface{}, bool)
3333
GetService() (OpenAPIService, bool)
34+
GetProvider() (Provider, bool)
3435
GetViewsForSqlDialect(sqlDialect string) ([]View, bool)
3536
GetMethodsMatched() Methods
3637
ToMap(extended bool) map[string]interface{}
3738
// unexported mutators
3839
getSQLVerbs() map[string][]OpenAPIOperationStoreRef
3940
setProvider(p Provider)
4041
setService(s OpenAPIService)
42+
SetProvider(p Provider)
43+
SetService(s OpenAPIService)
44+
SetProviderService(ps ProviderService)
4145
setProviderService(ps ProviderService)
4246
getUnionRequiredParameters(method StandardOperationStore) (map[string]Addressable, error)
4347
setMethod(string, *standardOpenAPIOperationStore)
@@ -83,10 +87,21 @@ func (r *standardResource) GetService() (OpenAPIService, bool) {
8387
return r.OpenAPIService, true
8488
}
8589

90+
func (r *standardResource) GetProvider() (Provider, bool) {
91+
if r.Provider == nil {
92+
return nil, false
93+
}
94+
return r.Provider, true
95+
}
96+
8697
func (r *standardResource) getSQLVerbs() map[string][]OpenAPIOperationStoreRef {
8798
return r.SQLVerbs
8899
}
89100

101+
func (r *standardResource) SetService(s OpenAPIService) {
102+
r.setService(s)
103+
}
104+
90105
func (r *standardResource) setService(s OpenAPIService) {
91106
r.OpenAPIService = s
92107
}
@@ -105,6 +120,14 @@ func (r *standardResource) setMethod(k string, v *standardOpenAPIOperationStore)
105120
r.Methods[k] = *v
106121
}
107122

123+
func (r *standardResource) SetProvider(p Provider) {
124+
r.Provider = p
125+
}
126+
127+
func (r *standardResource) SetProviderService(ps ProviderService) {
128+
r.ProviderService = ps
129+
}
130+
108131
func (r *standardResource) setProvider(p Provider) {
109132
r.Provider = p
110133
}

anysdk/schema.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type Schema interface {
4747
GetType() string
4848
GetPropertySchema(key string) (Schema, error)
4949
GetRequired() []string
50+
GetAlias() string
5051
GetSelectSchema(itemsKey, mediaType string) (Schema, string, error)
5152
IsArrayRef() bool
5253
IsBoolean() bool
@@ -135,6 +136,17 @@ func (s *standardSchema) getAdditionalProperties() (Schema, bool) {
135136
return nil, false
136137
}
137138

139+
func (s *standardSchema) GetAlias() string {
140+
alias, hasAlias := s.Extensions[ExtensionKeyAlias]
141+
if hasAlias {
142+
aliasStr, isStr := alias.(string)
143+
if isStr {
144+
return aliasStr
145+
}
146+
}
147+
return ""
148+
}
149+
138150
func (s *standardSchema) setPropertyOpenapi3(k string, ps *openapi3.SchemaRef) {
139151
s.Properties[k] = ps
140152
}
@@ -787,6 +799,11 @@ func (schema *standardSchema) extractMediaTypeSynonym(mediaType string) string {
787799
return mediaType
788800
}
789801

802+
func (schema *standardSchema) GetSchemaAtPath(key string, mediaType string) (Schema, error) {
803+
rv, _, err := schema.getSelectItemsSchema(key, mediaType)
804+
return rv, err
805+
}
806+
790807
func (schema *standardSchema) getSelectItemsSchema(key string, mediaType string) (Schema, string, error) {
791808
if key == "" {
792809
if schema.Items != nil && schema.Items.Value != nil {

anysdk/shims.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"sort"
77
"strings"
88

9+
"github.com/getkin/kin-openapi/openapi3"
910
"github.com/stackql/any-sdk/pkg/brickmap"
1011
"github.com/stackql/stackql-parser/go/vt/sqlparser"
1112
)
@@ -289,3 +290,14 @@ func getStringFromStringFunc(fe *sqlparser.FuncExpr) (string, error) {
289290
}
290291
return "", fmt.Errorf("cannot extract string from func '%s'", fe.Name)
291292
}
293+
294+
func getAliasFromExtensions(extensionObj openapi3.ExtensionProps) string {
295+
alias, hasAlias := extensionObj.Extensions[ExtensionKeyAlias]
296+
if hasAlias {
297+
aliasStr, isStr := alias.(string)
298+
if isStr {
299+
return aliasStr
300+
}
301+
}
302+
return ""
303+
}

docs/aliasing.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
2+
3+
# Aliasing
4+
5+
## In the abstract
6+
7+
8+
Any query's address space is composed of:
9+
10+
1. Zero or one URLs (eg https://host:port/path/to/resource?queryParam1=a&queryParam2=b).
11+
2. Input data on some logical "page" eg: an HTTP request. Another example: a golang template and fuinctionality to call a CLI tool.
12+
3. Returned data on some logical "page", eg: an HTTP response.
13+
14+
Pages can in theory be collections of pages and populated with a mixture of static and dynamic data. The namespace is a search structure to locate any address.
15+
16+
> For arbitrary address location, some system that traverses the entire address space at arbitrary depth.
17+
18+
`openapi` provides some, but ceertainly not all, of this functionality. Putting aside the implementation, this is possible so long as page collections are named or strictly ordered.
19+
20+
### Desired Namespace
21+
22+
For SQL semantics, a flat namespace without arbitrary prefixes and some semantic relevance is preferred. This suggests a configurable aliasing capability, coupled with a collision resolution algorithm. The aliasing capability:
23+
24+
- For input data, must support transparent rewrite w.r.t. the provider system. Ie: the provider receives precisely the unaliased data. This implies that any existing rewrite logic must either be ignored or be consistent with the aliasing.
25+
- For output data, simply a lazy rewrite before staging in RDBMS / relational algebra engine is ok.
26+
27+
Something like:
28+
29+
- Flatten the address space based upon config and default behaviour. There is already a simply version of this in `objectKey`.
30+
- Search the flattened address space based on cofiguration and default behaviour, identifying conflicts.
31+
- For each conflict, apply a configurable resolution algorithm.
32+
- At runtime, perform the aliasing transform relations and display data accordingly.
33+
34+
35+
36+
## For HTTP
37+
38+
39+
A useful consideration to begin with is the native capabilities of `openapi`, which supports dynamic substitution of URLs and requests for:
40+
41+
- Named [`parameter` objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#parameter-object) in "query", "header", "path" or "cookie" aspects. These are unique in the tuple (`name`, `location`), and map to one or more locations in the query address space. For an example of plurality: query parameters may be repeated per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
42+
- Named [server `variable` objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#server-variable-object) in server URLs.
43+
44+
45+
Request and response **sub-component** (body, header) attributes can be exposed unqualified, provided they do not collide with other exposed attributes. Such collisions are frequent and so naive and sub-optimal measures are available:
46+
47+
- Not exposing response header attributes at all.
48+
- Heuristics that effectively clobber any input attribute that collides with a server variable.
49+
- Routing request body attributes based on a prefix eg: `data__`. This is undesirable on the grounds of poor encapsulation and weakened semantics.
50+
51+
### Proposed HTTP implementation
52+
53+
Requirements:
54+
55+
- Aliased `openapi` parameters to include an alias extension attribute.
56+
- Some config exists denoting aliased request and response page attributes. Call this `PageAliasDirectory`.
57+
- **v2** The existing `objectKey` is enhanced / replaced with a fucnction that supports unions etc.
58+
- A flattening algorithm exists. Eg: strings -> string, int -> int, object -> string....
59+
60+
Then:
61+
62+
- For all `openapi` parameters, cache alias extension attribute.
63+
- AOT validate flattened namespace.
64+
- Build namespace search structure. This will both detect violations and be used downstream, for search and rewrite.
65+
- Runtime perform transform relations.
66+
67+
Search structure:
68+
69+
- Want to be extensible to arbitrary depth.
70+
- This suggests tree / graph rather than flat map.
71+
- Some kind of Trie (prefix tree).
72+
- Sits at method level.
73+
- Makes sense to place aliasing at sql method level, eg sql
74+

pkg/awssign/aws_sign.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ import (
1515
"github.com/aws/aws-sdk-go-v2/credentials"
1616
)
1717

18+
// This interface is not fully compliant.
19+
// Ultimately, for full coverage,
20+
// we need to emulate [the SDK auth specifications](https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-auth.html).
21+
//
22+
// This is the sort of stuff we need to emulate:
23+
// - [Resolver doc based resolution](https://github.com/aws/aws-sdk-go-v2/blob/2e08461090ccba679456c05264e2c04bf228138e/service/accessanalyzer/options.go#L150).
24+
// - SDK doc based code gen settings for auth:
25+
// - [For the `account` service](https://github.com/aws/aws-sdk-go-v2/blob/3ac24f20bb3b05955fcb1b3fae7883d3a03fe60d/codegen/sdk-codegen/aws-models/account.json#L133).
26+
//
27+
1828
var (
1929
_ Transport = &standardAwsSignTransport{}
2030
emptyPayloadHash string = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

0 commit comments

Comments
 (0)