Skip to content

Commit

Permalink
Properly encode filterBy parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
olivierapivideo committed Jul 26, 2024
1 parent fc01675 commit 451baae
Show file tree
Hide file tree
Showing 17 changed files with 223 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,17 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
Map<String, Object> res = super.postProcessModels(objs);
List<Map> models = (List<Map>) res.get("models");
ArrayList<HashMap<String, CodegenModel>> models = (ArrayList<HashMap<String, CodegenModel>>) res.get("models");

models.forEach(model -> {
((CodegenModel)model.get("model")).vars.forEach(var -> {
models.forEach(map -> {
CodegenModel model = map.get("model");
if(model.isMap) {
model.vendorExtensions.put("x-implements", Collections.singletonList("DeepObject"));
}
model.vars.forEach(var -> {
if(var.name.equals("_AccessToken")) var.name = "AccessToken";
if (var.defaultValue != null) {
((CodegenModel)model.get("model")).vendorExtensions.put("x-has-defaults", true);
model.vendorExtensions.put("x-has-defaults", true);
}
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
operation.allParams.stream().filter(p -> p.baseName.equals(queryParam.baseName)).forEach(p -> p.dataType = "time.Time");
queryParam.dataType = "time.Time";
}
if(queryParam.vendorExtensions.containsKey("x-is-deep-object")) additionalImports.add("fmt");
//if(queryParam.vendorExtensions.containsKey("x-is-deep-object")) additionalImports.add("fmt");
});

// overwrite operationId & nickname values of the operation with the x-client-action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ private void handlePagination(List<Object> allModels, CodegenOperation operation
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
Map<String, Object> stringObjectMap = super.postProcessModels(objs);
ArrayList<HashMap<String, CodegenModel>> models = (ArrayList<HashMap<String, CodegenModel>>) stringObjectMap.get("models");
models.stream().forEach((map) -> {
CodegenModel model = map.get("model");
if(model.isMap) {
((List<String>)model.vendorExtensions.get("x-implements") ).add("DeepObject");
}
});

((ArrayList) stringObjectMap.get("imports")).removeIf((v) -> ((Map) v).values().contains("org.openapitools.jackson.nullable.JsonNullable"));
return stringObjectMap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,20 @@ protected String getParameterDataType(Parameter parameter, Schema p) {
} else if (ModelUtils.isMapSchema(p)) {
inner = (Schema) p.getAdditionalProperties();
return "{ [key: string]: " + this.getParameterDataType(parameter, inner) + "; }";
} else if (ModelUtils.isStringSchema(p)) {
} else if (p != null &&
parameter.getName() != null &&
parameter.getIn().equalsIgnoreCase("query") &&
parameter.getExplode() &&
parameter.getExtensions() != null &&
parameter.get$ref() != null &&
parameter.getExtensions().containsKey("x-is-deep-object")) {
String refName = parameter.get$ref().substring(parameter.get$ref().lastIndexOf("/") + 1);
refName = refName.replace("_", "");
refName = refName.substring(0, 1).toUpperCase() + refName.substring(1);

return refName;
}
else if (ModelUtils.isStringSchema(p)) {
// Handle string enums
if (p.getEnum() != null) {
return enumValuesToEnumTypeUnion(p.getEnum(), "string");
Expand Down
98 changes: 72 additions & 26 deletions oas_apivideo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11899,9 +11899,9 @@ paths:
- `media-type`: Returns analytics based on the type of content. Possible values: `video` and `live-stream`.
- `continent`: Returns analytics based on the viewers' continent. The list of supported continents names are based on the [GeoNames public database](https://www.geonames.org/countries/). Possible values are: `AS`, `AF`, `NA`, `SA`, `AN`, `EU`, `AZ`.
- `country`: Returns analytics based on the viewers' country. The list of supported country names are based on the [GeoNames public database](https://www.geonames.org/countries/).
- `device-type`: Returns analytics based on the type of device used by the viewers. Possible response values are: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`.
- `operating-system`: Returns analytics based on the operating system used by the viewers. Response values include `windows`, `mac osx`, `android`, `ios`, `linux`.
- `browser`: Returns analytics based on the browser used by the viewers. Response values include `chrome`, `firefox`, `edge`, `opera`.
- `device-type`: Returns analytics based on the type of device used by the viewers. Response values can include: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`.
- `operating-system`: Returns analytics based on the operating system used by the viewers. Response values can include `windows`, `mac osx`, `android`, `ios`, `linux`.
- `browser`: Returns analytics based on the browser used by the viewers. Response values can include `chrome`, `firefox`, `edge`, `opera`.
style: simple
explode: false
required: true
Expand Down Expand Up @@ -12300,6 +12300,40 @@ paths:
x-client-paginated: true
x-doctave:
code-samples:
- language: node
code: |
const metrics = await client.analytics.getMetricsOverTime({
metric: 'play',
filterBy: {
browser: ['chrome', 'firefox'],
continent: ['EU', 'AF'],
},
});
- language: csharp
code: |
FilterBy2 filterBy = new FilterBy2
{
continent = new List<string> { "EU", "US" },
devicetype = new List<string> { "phone" },
tag = "test"
};
Page<AnalyticsMetricsBreakdownResponseData> page = apiClient.Analytics()
.getMetricsBreakdown("play", "continent").From(new DateTime(2024, 7, 1)).FilterBy(filterBy).execute();
- language: go
code: |
res, err := cl.Analytics.GetMetricsBreakdown("play", "continent", AnalyticsApiGetMetricsBreakdownRequest{
filterBy: &FilterBy2{
DeviceType: &[]string{"computer", "phone"},
Tag: PtrString("tag"),
},
})
- language: python
code: |
video_with_tags = self.api.get_metrics_breakdown(metric='play', breakdown='continent', filter_by=FilterBy2(
device_type=["computer", "phone"],
tag="test",
),
)
/webhooks:
get:
tags:
Expand Down Expand Up @@ -14629,7 +14663,6 @@ components:
required:
- context
- data
- pagination
analytics-metrics-breakdown-response:
title: Analytics response for metrics breakdown by dimension
type: object
Expand Down Expand Up @@ -14733,7 +14766,7 @@ components:
data:
description: Returns an array of metrics and the timestamps .
type: array
items:
items:
type: object
properties:
emittedAt:
Expand Down Expand Up @@ -15053,55 +15086,68 @@ components:
Use this parameter to filter the API's response based on different data dimensions. You can serialize filters in your query to receive more detailed breakdowns of your analytics.

- If you do not set a value for `filterBy`, the API returns the full dataset for your project.
- The API only accepts the `mediaId` and `mediaType` filters when you call `/data/metrics/play/total`.
- The API only accepts the `mediaId` and `mediaType` filters when you call `/data/metrics/play/total` or `/data/buckets/play-total/media-id`.

These are the available breakdown dimensions:

- `mediaId`: Returns analytics based on the unique identifiers of a video or a live stream.
- `mediaType`: Returns analytics based on the type of content. Possible values: `video` and `live-stream`.
- `continent`: Returns analytics based on the viewers' continent. The list of supported continents names are based on the [GeoNames public database](https://www.geonames.org/countries/). You must use the ISO-3166 alpha2 format, for example `EU`. Possible values are: `AS`, `AF`, `NA`, `SA`, `AN`, `EU`, `AZ`.
- `country`: Returns analytics based on the viewers' country. The list of supported country names are based on the [GeoNames public database](https://www.geonames.org/countries/). You must use the ISO-3166 alpha2 format, for example `FR`.
- `deviceType`: Returns analytics based on the type of device used by the viewers. Possible response values are: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`.
- `operatingSystem`: Returns analytics based on the operating system used by the viewers. Response values include `windows`, `mac osx`, `android`, `ios`, `linux`.
- `browser`: Returns analytics based on the browser used by the viewers. Response values include `chrome`, `firefox`, `edge`, `opera`.
- `deviceType`: Returns analytics based on the type of device used by the viewers. Response values can include: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`.
- `operatingSystem`: Returns analytics based on the operating system used by the viewers. Response values can include `windows`, `mac osx`, `android`, `ios`, `linux`.
- `browser`: Returns analytics based on the browser used by the viewers. Response values can include `chrome`, `firefox`, `edge`, `opera`.
- `tag`: Returns analytics for videos using this tag. This filter only accepts a single value and is case sensitive. Read more about tagging your videos [here](https://docs.api.video/vod/tags-metadata).
in: query
required: false
example: {"mediaType":"video","continent":"EU","country":"FR"}
example: filterBy[country]=FR&filterBy[operatingSystem]=windows&filterBy[browser][]=firefox&filterBy[browser][]=chrome&filterBy[tag]=Cool videos
style: deepObject
x-is-deep-object: true
explode: true
schema:
type: object
properties:
mediaId:
type: string
type: array
items:
type: string
description: Returns analytics based on the unique identifiers of a video or a live stream.
example: vi4blUQJFrYWbaG44NChkH27
example: ['vi4blUQJFrYWbaG44NChkH27']
mediaType:
type: string
enum: [video, live-stream]
example: video
continent:
type: string
type: array
items:
type: string
enum: [AS, AF, NA, SA, AN, EU, AZ]
description: Returns analytics based on the viewers' continent. The list of supported continents names are based on the [GeoNames public database](https://www.geonames.org/countries/). You must use the ISO-3166 alpha2 format, for example `EU`.
enum: [AS, AF, NA, SA, AN, EU, AZ]
example: EU
example: ['EU']
country:
description: Returns analytics based on the viewers' country. The list of supported country names are based on the [GeoNames public database](https://www.geonames.org/countries/). You must use the ISO-3166 alpha2 format, for example `FR`.
type: string
example: FR
type: array
items:
type: string
example: ['FR']
deviceType:
type: string
description: 'Returns analytics based on the type of device used by the viewers. Possible response values are: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`.'
example: computer
type: array
items:
type: string
description: 'Returns analytics based on the type of device used by the viewers. Response values can include: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`.'
example: ['computer']
operatingSystem:
type: string
description: Returns analytics based on the operating system used by the viewers. Response values include `windows`, `mac osx`, `android`, `ios`, `linux`.
example: windows
type: array
items:
type: string
description: Returns analytics based on the operating system used by the viewers. Response values can include `windows`, `mac osx`, `android`, `ios`, `linux`.
example: ['windows']
browser:
description: Returns analytics based on the browser used by the viewers. Response values include `chrome`, `firefox`, `edge`, `opera`.
type: string
example: firefox
description: Returns analytics based on the browser used by the viewers. Response values can include `chrome`, `firefox`, `edge`, `opera`.
type: array
items:
type: string
example: ['firefox']
tag:
type: string
description: Returns analytics for videos using this tag. This filter only accepts a single value and is case sensitive. Read more about tagging your videos [here](https://docs.api.video/vod/tags-metadata).
Expand Down
20 changes: 20 additions & 0 deletions templates/csharp/ApiClient.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ using RestSharp.Portable.HttpClient;
{{^netStandard}}
using RestSharp;
{{/netStandard}}
using ApiVideo.Model;

namespace {{packageName}}.Client
{
Expand Down Expand Up @@ -680,6 +681,25 @@ namespace {{packageName}}.Client
parameters.Add(new KeyValuePair<string, string>(name + "[" + entry.Key + "]", ParameterToString(entry.Value)));
}
}
else if (value is DeepObject) {
var dict2 = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.Indented));
foreach (var entry in dict2)
{
if (entry.Value != null)
{
if (entry.Value is IList)
{
var valueCollection = entry.Value as IEnumerable;
foreach (object item in valueCollection)
{
parameters.Add(new KeyValuePair<string, string>(name + "[" + entry.Key + "][]", ParameterToString(item)));
}
} else {
parameters.Add(new KeyValuePair<string, string>(name + "[" + entry.Key + "]", ParameterToString(entry.Value)));
}
}
}
}
else
{
parameters.Add(new KeyValuePair<string, string>(name, ParameterToString(value)));
Expand Down
2 changes: 1 addition & 1 deletion templates/csharp/model.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace {{packageName}}.Model {
/// {{description}}
/// </summary>
[DataContract]
public class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} {
public class {{classname}}{{#parent}} : {{{parent}}}{{/parent}}{{#vendorExtensions.x-implements}}{{#-first}}: {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}} {
{{#vars}}
{{^vendorExtensions.x-optional-nullable}} /// <summary>
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}}
Expand Down
1 change: 1 addition & 0 deletions templates/csharp/post-generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ cp ../../templates/common-resources/LICENSE ./
mv src/ApiVideo ApiVideo
rm -Rf src
mv ApiVideo src
cp -R ../../templates/csharp/statics/src/Model/DeepObject.cs ./src/Model/
rm .travis.yml
rm build.bat
rm build.sh
Expand Down
4 changes: 4 additions & 0 deletions templates/csharp/statics/src/Model/DeepObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace ApiVideo.Model {
public interface DeepObject {
}
}
6 changes: 1 addition & 5 deletions templates/go/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,7 @@ func (s *{{{classname}}}Service) {{nickname}}WithContext(ctx context.Context{{#r
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
{{#vendorExtensions.x-is-deep-object}}
if r.{{paramName}} != nil && len(*r.{{paramName}}) > 0 {
for k, v := range *r.{{paramName}} {
localVarQueryParams.Add(fmt.Sprintf("{{baseName}}[%s]", k), v)
}
}
addDeepQueryParams(r.{{paramName}}, "{{paramName}}", localVarQueryParams)
{{/vendorExtensions.x-is-deep-object}}
{{^vendorExtensions.x-is-deep-object}}
localVarQueryParams.Add("{{baseName}}", parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}"))
Expand Down
41 changes: 41 additions & 0 deletions templates/go/client.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -584,3 +584,44 @@ func parameterToString(obj interface{}, collectionFormat string) string {

return fmt.Sprintf("%v", obj)
}


func addDeepQueryParams(filter interface{}, prefix string, queryParams url.Values) {
v := reflect.ValueOf(filter)
if v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
switch v.Kind() {
case reflect.Struct:
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldType := t.Field(i)
if field.Kind() == reflect.Ptr && !field.IsNil() {
field = field.Elem()
}

lowercaseFirstChar := strings.ToLower(string(fieldType.Name[0]))
restOfString := fieldType.Name[1:]
lowercaseName := lowercaseFirstChar + restOfString

if field.Kind() == reflect.Slice {
for j := 0; j < field.Len(); j++ {
item := field.Index(j)
queryParams.Add(fmt.Sprintf("%s[%s][%d]", prefix, lowercaseName, j), item.String())
}
} else if field.Kind() == reflect.String {
queryParams.Add(fmt.Sprintf("%s[%s]", prefix, lowercaseName), field.String())
}
}
case reflect.Map:
for _, key := range v.MapKeys() {
val := v.MapIndex(key)
queryParams.Add(fmt.Sprintf("%s[%s]", prefix, key.String()), fmt.Sprintf("%v", val))
}
default:
fmt.Println("Unsupported type")
}
}
Loading

0 comments on commit 451baae

Please sign in to comment.