Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions csharp/rtl/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,11 @@ public interface IValues : IDictionary<string, object>
public class Values : Dictionary<string, object>, IValues
{
}

/// <summary>
/// Type for explicitly specifying url-encoded form values
/// </summary>
public class FormValues : Values
{
}
}
12 changes: 12 additions & 0 deletions csharp/rtl/Transport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,18 @@ public async Task<IRawResponse> RawRequest(
Encoding.UTF8,
"application/x-www-form-urlencoded");
}
else if (body is FormValues)
{
var dict = ((FormValues)body)
.Where(k => k.Value != null)
.ToDictionary(k => k.Key, k =>
{
if (k.Value is DateTime dt) return dt.ToString("o");
if (k.Value is string || k.Value.GetType().IsPrimitive) return k.Value.ToString();
return JsonSerializer.Serialize(k.Value, new JsonSerializerOptions { IgnoreNullValues = true });
});
request.Content = new FormUrlEncodedContent(dict);
}
else
{
request.Content =
Expand Down
50 changes: 30 additions & 20 deletions go/rtl/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ func NewAuthSessionWithTransport(config ApiSettings, transport http.RoundTripper
}
}

// Values is a map alias used for form urlencoded bodies
type Values map[string]interface{}

func (s *AuthSession) Do(result interface{}, method, ver, path string, reqPars map[string]interface{}, body interface{}, options *ApiSettings) error {

// prepare URL
Expand All @@ -97,29 +100,36 @@ func (s *AuthSession) Do(result interface{}, method, ver, path string, reqPars m

// serialize body to string and determine request's Content-Type header
if body != nil {
// get the `body`'s type
kind := reflect.TypeOf(body).Kind()
value := reflect.ValueOf(body)

// check if it is pointer
if kind == reflect.Ptr {
// if so find the type it points to
kind = reflect.ValueOf(body).Elem().Kind()
value = reflect.ValueOf(body).Elem()
}

if kind == reflect.String {
contentTypeHeader = "text/plain"
bodyString = fmt.Sprintf("%v", value)
if v, ok := body.(Values); ok {
contentTypeHeader = "application/x-www-form-urlencoded"
u := &url.URL{}
setQuery(u, map[string]interface{}(v))
bodyString = u.RawQuery
} else {
contentTypeHeader = "application/json"
serializedBody, err := json.Marshal(body)

if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "error serializing body: %v", err)
// get the `body`'s type
kind := reflect.TypeOf(body).Kind()
value := reflect.ValueOf(body)

// check if it is pointer
if kind == reflect.Ptr {
// if so find the type it points to
kind = reflect.ValueOf(body).Elem().Kind()
value = reflect.ValueOf(body).Elem()
}

bodyString = string(serializedBody)
if kind == reflect.String {
contentTypeHeader = "text/plain"
bodyString = fmt.Sprintf("%v", value)
} else {
contentTypeHeader = "application/json"
serializedBody, err := json.Marshal(body)

if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "error serializing body: %v", err)
}

bodyString = string(serializedBody)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion kotlin/src/main/com/looker/rtl/Transport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,10 @@ fun encodeParam(value: Any?): String {
value.toOffsetDateTime().format(utcFormat)
} else if (value is Date) {
value.toInstant().atZone(ZoneOffset.UTC).format(utcFormat)
} else {
} else if (value is String || value is Number || value is Boolean) {
"$value"
} else {
GSON.toJson(value)
}
try {
val decoded = URLDecoder.decode(encoded, utf8)
Expand Down
15 changes: 10 additions & 5 deletions packages/sdk-codegen/src/codeGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1317,12 +1317,13 @@ export abstract class CodeGen implements ICodeGen {
assignFormArgs(
method: IMethod,
body: string,
query: string
): { body: string; query: string } {
query: string,
options: string
): { body: string; query: string; options: string } {
if (method.isFormUrlEncoded) {
return { body: query, query: this.nullStr };
return { body: query, query: this.nullStr, options };
}
return { body, query };
return { body, query, options };
}

// build the http argument list from back to front, so trailing undefined arguments
Expand All @@ -1338,9 +1339,13 @@ export abstract class CodeGen implements ICodeGen {
result = this.argFill(result, this.argGroup(indent, method.headerArgs));
let body = method.bodyArg ? method.bodyArg : this.nullStr;
let query = this.argGroup(indent, method.queryArgs);
const formArgs = this.assignFormArgs(method, body, query);
// Add 'options' since it might be modified for form encoding
let options = this.nullStr;
const formArgs = this.assignFormArgs(method, body, query, options);
body = formArgs.body;
query = formArgs.query;
options = formArgs.options;
result = this.argFill(result, options);
result = this.argFill(result, body);
result = this.argFill(result, query);
return result;
Expand Down
23 changes: 19 additions & 4 deletions packages/sdk-codegen/src/csharp.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,15 +341,30 @@ namespace Looker.SDK.API${this.apiRef}
: this.nullStr;
}

assignFormArgs(
method: IMethod,
body: string,
query: string,
options: string
): { body: string; query: string; options: string } {
if (method.isFormUrlEncoded) {
// CSharp uses "new FormValues { ... }" to trigger RTL-level URL encoding.
query = query.replace(/new Values \{/g, 'new FormValues {');
return { body: query, query: this.nullStr, options };
}
return { body, query, options };
}

httpArgs(indent: string, method: IMethod) {
let result = this.argFill('', 'options');
// result = this.argFill(result, this.argGroup(indent, method.cookieArgs))
// result = this.argFill(result, this.argGroup(indent, method.headerArgs))
let options = 'options';
let body = method.bodyArg ? method.bodyArg : this.nullStr;
let query = this.argGroup(indent, method.queryArgs);
const formArgs = this.assignFormArgs(method, body, query);
const formArgs = this.assignFormArgs(method, body, query, options);
body = formArgs.body;
query = formArgs.query;
options = formArgs.options;

let result = this.argFill('', options);
result = this.argFill(result, body);
result = this.argFill(result, query);
return result;
Expand Down
22 changes: 18 additions & 4 deletions packages/sdk-codegen/src/go.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,21 +284,35 @@ export class GoGen extends CodeGen {
// add options at the end of the request calls. this will cause all other arguments to be
// filled in but there's no way to avoid this for passing in the last optional parameter.
// Fortunately, this code bloat is minimal and also hidden from the consumer.
let result = this.argFill('', 'options');
// let result = this.argFill('', this.argGroup(indent, method.cookieArgs, request))
// result = this.argFill(result, this.argGroup(indent, method.headerArgs, request))
let options = 'options';
let body = method.bodyArg
? this.accessor(method.bodyArg, request)
: this.nullStr;
let query = this.argGroup(indent, method.queryArgs, request);
const formArgs = this.assignFormArgs(method, body, query);
const formArgs = this.assignFormArgs(method, body, query, options);
body = formArgs.body;
query = formArgs.query;
options = formArgs.options;

let result = this.argFill('', options);
result = this.argFill(result, body);
result = this.argFill(result, query);
return result;
}

assignFormArgs(
method: IMethod,
body: string,
query: string,
options: string
): { body: string; query: string; options: string } {
if (method.isFormUrlEncoded) {
body = query.replace('map[string]interface{}', 'rtl.Values');
query = this.nullStr;
}
return { body, query, options };
}

argGroup(_indent: string, args: Arg[], prefix?: string) {
if (!args || args.length === 0) return this.nullStr;
const hash: string[] = [];
Expand Down
14 changes: 10 additions & 4 deletions packages/sdk-codegen/src/kotlin.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,12 +458,16 @@ ${props.join(this.propDelimiter)}
assignFormArgs(
method: IMethod,
body: string,
query: string
): { body: string; query: string } {
const result = super.assignFormArgs(method, body, query);
query: string,
options: string
): { body: string; query: string; options: string } {
const result = super.assignFormArgs(method, body, query, options);
if (result.query === this.nullStr) {
result.query = 'mapOf()';
}
if (method.isFormUrlEncoded) {
result.body = `encodeValues(${result.body})`;
}
return result;
}

Expand All @@ -483,11 +487,13 @@ ${props.join(this.propDelimiter)}
// let result = this.argFill('', 'options')
// let result = this.argFill('', this.argGroup(indent, method.cookieArgs, request))
// result = this.argFill(result, this.argGroup(indent, method.headerArgs, request))
let options = 'options';
let body = method.bodyArg ? `${request}${method.bodyArg}` : this.nullStr;
let query = this.argGroup(indent, method.queryArgs, request);
const formArgs = this.assignFormArgs(method, body, query);
const formArgs = this.assignFormArgs(method, body, query, options);
body = formArgs.body;
query = formArgs.query;
options = formArgs.options; // Kotlin generator ignores options down the line currently
let result = this.argFill('', body);
result = this.argFill(result, query);
return result;
Expand Down
1 change: 1 addition & 0 deletions packages/sdk-codegen/src/python.gen.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import warnings

from . import models as mdls
from looker_sdk.rtl import api_methods
from looker_sdk.rtl import model
from looker_sdk.rtl import transport

class LookerSDK(api_methods.APIMethods):
Expand Down
32 changes: 26 additions & 6 deletions packages/sdk-codegen/src/python.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import warnings

from . import models as mdls
from ${this.packagePath}.rtl import api_methods
from ${this.packagePath}.rtl import model
from ${this.packagePath}.rtl import transport

class ${this.packageName}(api_methods.APIMethods):
Expand Down Expand Up @@ -371,16 +372,22 @@ ${this.hooks.join('\n')}

httpArgs(callerIndent: string, method: IMethod): string {
const currIndent = this.bumper(callerIndent);
let args = '';
args = this.argFill(
args,
`${currIndent}transport_options=transport_options`
);
const body = method.bodyArg ? method.bodyArg : this.nullStr;
const query = method.queryArgs.length
? this.argGroup('', method.queryArgs)
: this.nullStr;
const formArgs = this.assignFormArgs(method, body, query);
const formArgs = this.assignFormArgs(
method,
body,
query,
'transport_options'
);

let args = '';
args = this.argFill(
args,
`${currIndent}transport_options=${formArgs.options}`
);

if (formArgs.body !== this.nullStr) {
args = this.argFill(args, `${currIndent}body=${formArgs.body}`);
Expand Down Expand Up @@ -438,6 +445,19 @@ ${this.hooks.join('\n')}
return encodings;
}

assignFormArgs(
method: IMethod,
body: string,
query: string,
options: string
): { body: string; query: string; options: string } {
if (method.isFormUrlEncoded) {
body = `model.URLSearchParams(${query})`;
query = this.nullStr;
}
return { body, query, options };
}

declareMethod(indent: string, method: IMethod) {
const bump = this.bumper(indent);

Expand Down
18 changes: 12 additions & 6 deletions packages/sdk-codegen/src/swift.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -724,16 +724,22 @@ ${indent}}\n`;
// add options at the end of the request calls. this will cause all other arguments to be
// filled in but there's no way to avoid this for passing in the last optional parameter.
// Fortunately, this code bloat is minimal and also hidden from the consumer.
let result = this.argFill('', 'options');
// let result = this.argFill('', this.argGroup(indent, method.cookieArgs, request))
// result = this.argFill(result, this.argGroup(indent, method.headerArgs, request))
let options = 'options';
let body = method.bodyArg
? `try! self.encode(${request}${method.bodyArg})`
: this.nullStr;
let query = this.queryGroup(indent, method, request);
const formArgs = this.assignFormArgs(method, body, query);
body = formArgs.body;
query = formArgs.query;
if (method.isFormUrlEncoded) {
body = `FormValues(${query})`;
query = this.nullStr;
} else {
const formArgs = this.assignFormArgs(method, body, query, options);
body = formArgs.body;
query = formArgs.query;
options = formArgs.options;
}

let result = this.argFill('', options);
result = this.argFill(result, body);
result = this.argFill(result, query);
return result;
Expand Down
22 changes: 18 additions & 4 deletions packages/sdk-codegen/src/typescript.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,19 @@ ${indent}})`;
return `${args}${current ? this.argDelimiter : ''}${current}`;
}

assignFormArgs(
method: IMethod,
body: string,
query: string,
options: string
): { body: string; query: string; options: string } {
if (method.isFormUrlEncoded) {
body = `new URLSearchParams(${query} as unknown as Record<string, string>)`;
query = this.nullStr;
}
return { body, query, options };
}

// build the http argument list from back to front, so trailing undefined arguments
// can be omitted. Path arguments are resolved as part of the path parameter to general
// purpose API method call
Expand All @@ -805,16 +818,17 @@ ${indent}})`;
// add options at the end of the request calls. this will cause all other arguments to be
// filled in but there's no way to avoid this for passing in the last optional parameter.
// Fortunately, this code bloat is minimal and also hidden from the consumer.
let result = this.argFill('', 'options');
// let result = this.argFill('', this.argGroup(indent, method.cookieArgs, request))
// result = this.argFill(result, this.argGroup(indent, method.headerArgs, request))
let options = 'options';
let body = method.bodyArg
? this.accessor(method.bodyArg, request)
: this.nullStr;
let query = this.argGroup(indent, method.queryArgs, request);
const formArgs = this.assignFormArgs(method, body, query);
const formArgs = this.assignFormArgs(method, body, query, options);
body = formArgs.body;
query = formArgs.query;
options = formArgs.options;

let result = this.argFill('', options);
result = this.argFill(result, body);
result = this.argFill(result, query);
return result;
Expand Down
Loading
Loading