diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Common.java b/apivideo-generator/src/main/java/video/api/client/generator/Common.java index 39f802ec..35b56a4b 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Common.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Common.java @@ -18,6 +18,13 @@ public class Common { + public static void preprocessOpenAPI(OpenAPI openAPI) { + // remove paths that have the x-ignored flag + openAPI.getPaths().keySet().removeIf(path -> openAPI.getPaths().get(path).getExtensions() != null && openAPI.getPaths().get(path).getExtensions().containsKey("x-ignored")); + // remove models that have the x-ignored flag + openAPI.getComponents().getSchemas().keySet().removeIf(schema -> openAPI.getComponents().getSchemas().get(schema).getExtensions() != null && openAPI.getComponents().getSchemas().get(schema).getExtensions().containsKey("x-ignored")); + } + public static Comparator getCodegenOperationComparator() { Map operationIndexes = new HashMap<>(); operationIndexes.put("create", 1); diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Csharp.java b/apivideo-generator/src/main/java/video/api/client/generator/Csharp.java index 53eba048..ae445bc8 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Csharp.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Csharp.java @@ -3,6 +3,7 @@ import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; import io.swagger.util.Json; +import io.swagger.v3.oas.models.OpenAPI; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.CodegenModel; import org.openapitools.codegen.CodegenOperation; @@ -37,6 +38,13 @@ public Csharp() { packageGuid = "{" + java.util.UUID.nameUUIDFromBytes(this.packageVersion.getBytes()).toString().toUpperCase(Locale.ROOT) + "}"; } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + Common.preprocessOpenAPI(openAPI); + } + @Override public void postProcessParameter(CodegenParameter parameter) { super.postProcessParameter(parameter); @@ -165,13 +173,17 @@ public Map postProcessOperationsWithModels(Map o @Override public Map postProcessModels(Map objs) { Map res = super.postProcessModels(objs); - List models = (List) res.get("models"); + ArrayList> models = (ArrayList>) 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); } }); }); @@ -187,7 +199,7 @@ private void handlePagination(List allModels, CodegenOperation operation System.out.println(model); model.allVars.stream().filter(v -> v.name.equals("Data")).findFirst().ifPresent(codegenProperty -> { Map paginationProperties = new HashMap<>(); - paginationProperties.put("type", codegenProperty.complexType); + paginationProperties.put("type", codegenProperty.dataType.substring(codegenProperty.dataType.indexOf("<") + 1, codegenProperty.dataType.indexOf(">"))); paginationProperties.put("getter", codegenProperty.getter); operation.vendorExtensions.put("x-pagination", paginationProperties); }); diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Go.java b/apivideo-generator/src/main/java/video/api/client/generator/Go.java index 4ccf216f..75c7a7e7 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Go.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Go.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.collect.Maps; import io.swagger.util.Json; +import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; @@ -40,6 +41,13 @@ public CodegenProperty fromProperty(String name, Schema p) { return super.fromProperty(name, p); } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + Common.preprocessOpenAPI(openAPI); + } + @Override public Map postProcessOperationsWithModels(Map objs, List allModels) { Common.replaceDescriptionsAndSamples(objs, "go"); @@ -82,7 +90,11 @@ public Map postProcessOperationsWithModels(Map o } operation.queryParams.forEach(queryParam -> { - if(queryParam.vendorExtensions.containsKey("x-is-deep-object")) additionalImports.add("fmt"); + if(queryParam.dataFormat != null && queryParam.dataFormat.equalsIgnoreCase("date-time")) { + 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"); }); // overwrite operationId & nickname values of the operation with the x-client-action diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Java.java b/apivideo-generator/src/main/java/video/api/client/generator/Java.java index 62bfffab..e68412f6 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Java.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Java.java @@ -1,5 +1,6 @@ package video.api.client.generator; +import io.swagger.v3.oas.models.OpenAPI; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.CodegenModel; import org.openapitools.codegen.CodegenOperation; @@ -140,7 +141,11 @@ public void processOpts() { supportingFiles.removeIf(e -> skippedFiles.contains(e.getTemplateFile())); } - + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + Common.preprocessOpenAPI(openAPI); + } private void handlePagination(List allModels, CodegenOperation operation) { Optional map = allModels.stream().filter(m -> ((CodegenModel) ((Map) m).get("model")).classname.equals(operation.returnType)).map(a -> (Map) a).findFirst(); @@ -149,7 +154,11 @@ private void handlePagination(List allModels, CodegenOperation operation System.out.println(model); model.allVars.stream().filter(v -> v.name.equals("data")).findFirst().ifPresent(codegenProperty -> { Map paginationProperties = new HashMap<>(); - paginationProperties.put("type", codegenProperty.complexType); + if(codegenProperty.dataType.contains("<") && codegenProperty.dataType.contains(">")) { + paginationProperties.put("type", codegenProperty.dataType.substring(codegenProperty.dataType.indexOf("<") + 1, codegenProperty.dataType.indexOf(">"))); + } else { + paginationProperties.put("type", codegenProperty.dataType); + } paginationProperties.put("getter", codegenProperty.getter); operation.vendorExtensions.put("x-pagination", paginationProperties); }); @@ -159,6 +168,14 @@ private void handlePagination(List allModels, CodegenOperation operation @Override public Map postProcessModels(Map objs) { Map stringObjectMap = super.postProcessModels(objs); + ArrayList> models = (ArrayList>) stringObjectMap.get("models"); + models.stream().forEach((map) -> { + CodegenModel model = map.get("model"); + if(model.isMap) { + ((List)model.vendorExtensions.get("x-implements") ).add("DeepObject"); + } + }); + ((ArrayList) stringObjectMap.get("imports")).removeIf((v) -> ((Map) v).values().contains("org.openapitools.jackson.nullable.JsonNullable")); return stringObjectMap; } diff --git a/apivideo-generator/src/main/java/video/api/client/generator/JavaUploader.java b/apivideo-generator/src/main/java/video/api/client/generator/JavaUploader.java index a9c6f919..2d1821c4 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/JavaUploader.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/JavaUploader.java @@ -25,6 +25,7 @@ public void processOpts() { } + /** * Filter the OpenAPI description file entries in order to keep only stuff related to video upload & authentication * @param openAPI @@ -32,6 +33,7 @@ public void processOpts() { @Override public void preprocessOpenAPI(OpenAPI openAPI) { super.preprocessOpenAPI(openAPI); + Common.preprocessOpenAPI(openAPI); Set pathsToRemove = new HashSet<>(); diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Php.java b/apivideo-generator/src/main/java/video/api/client/generator/Php.java index 1551334a..875b9055 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Php.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Php.java @@ -97,7 +97,10 @@ public String getHelp() { return "Generates a PHP client library."; } + + public void preprocessOpenAPI(OpenAPI openAPI) { + Common.preprocessOpenAPI(openAPI); Map pathItems = openAPI.getPaths(); io.swagger.v3.oas.models.Paths newPaths = new io.swagger.v3.oas.models.Paths(); diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Python.java b/apivideo-generator/src/main/java/video/api/client/generator/Python.java index a83473af..238daa02 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Python.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Python.java @@ -85,6 +85,7 @@ public CodegenProperty fromProperty(String name, Schema p) { } public void preprocessOpenAPI(OpenAPI openAPI) { + Common.preprocessOpenAPI(openAPI); Map pathItems = openAPI.getPaths(); io.swagger.v3.oas.models.Paths newPaths = new io.swagger.v3.oas.models.Paths(); diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Swift5.java b/apivideo-generator/src/main/java/video/api/client/generator/Swift5.java index 0df11cb6..e4a79e14 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Swift5.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Swift5.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.util.Json; +import io.swagger.v3.oas.models.OpenAPI; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; import org.openapitools.codegen.languages.Swift5ClientCodegen; @@ -24,6 +25,11 @@ public class Swift5 extends Swift5ClientCodegen { public static final String VENDOR_X_CLIENT_HIDDEN = "x-client-hidden"; public static final List PARAMETERS_TO_HIDE_IN_CLIENT_DOC = Arrays.asList("currentPage", "pageSize"); + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + Common.preprocessOpenAPI(openAPI); + } @Override public Map postProcessOperationsWithModels(Map objs, List allModels) { Common.replaceDescriptionsAndSamples(objs, "swift5"); @@ -53,7 +59,16 @@ public Map postProcessOperationsWithModels(Map o } applyToAllParams(operation, (params) -> params.forEach(pp -> { - if("deepObject".equals(pp.style)) pp.collectionFormat = "deepObject"; + if(pp.vendorExtensions != null && pp.vendorExtensions.containsKey("x-is-deep-object")) { + if(!pp.getHasVars()) { + pp.vendorExtensions.remove("x-is-deep-object"); + } else { + if("deepObject".equals(pp.style)) { + pp.collectionFormat = "deepObject"; + } + } + } + })); applyToAllParams(operation, (params) -> params.removeIf(pp -> getVendorExtensionBooleanValue(pp, VENDOR_X_CLIENT_IGNORE)) ); @@ -74,6 +89,7 @@ private void applyToAllParams(CodegenOperation operation, Consumer postProcessModels(Map objs) { Map stringObjectMap = super.postProcessModels(objs); @@ -102,6 +117,12 @@ public Map postProcessModels(Map objs) { List> models = (List)objs.get("models"); models.forEach(map -> { CodegenModel model = ((CodegenModel)map.get("model")); + if(model.vendorExtensions != null && model.vendorExtensions.containsKey("x-is-deep-object")) { + model.allVars.forEach(var -> { + String nameWithoutEndDigits = model.name.replaceAll("_\\d+$", ""); + var.vendorExtensions.put("x-model-name", nameWithoutEndDigits); + }); + } model.vars.forEach(var -> { if(var.vendorExtensions.containsKey("x-optional-nullable") && var.dataType.equals("String")) { var.dataType = "NullableString"; diff --git a/apivideo-generator/src/main/java/video/api/client/generator/Swift5Uploader.java b/apivideo-generator/src/main/java/video/api/client/generator/Swift5Uploader.java index e01c2ccf..82dac8ab 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/Swift5Uploader.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/Swift5Uploader.java @@ -22,6 +22,7 @@ public class Swift5Uploader extends Swift5 { @Override public void preprocessOpenAPI(OpenAPI openAPI) { super.preprocessOpenAPI(openAPI); + Common.preprocessOpenAPI(openAPI); Set pathsToRemove = new HashSet<>(); diff --git a/apivideo-generator/src/main/java/video/api/client/generator/TypeScript.java b/apivideo-generator/src/main/java/video/api/client/generator/TypeScript.java index 2a871bb8..449c4d2d 100644 --- a/apivideo-generator/src/main/java/video/api/client/generator/TypeScript.java +++ b/apivideo-generator/src/main/java/video/api/client/generator/TypeScript.java @@ -475,6 +475,8 @@ public CodegenType getTag() { @Override public void preprocessOpenAPI(OpenAPI openAPI) { + Common.preprocessOpenAPI(openAPI); + if (additionalProperties.containsKey(NPM_NAME)) { // If no npmVersion is provided in additional properties, version from API specification is used. // If none of them is provided then fallbacks to default version @@ -635,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"); diff --git a/config/android.yaml b/config/android.yaml index 855abd00..2974c2fb 100644 --- a/config/android.yaml +++ b/config/android.yaml @@ -1,4 +1,7 @@ changelog: + - 1.6.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.5.7 (2024-04-25): - Add API to get rate limiting headers - 1.5.6 (2024-03-21): diff --git a/config/csharp.yaml b/config/csharp.yaml index b3b00209..fa2ffebe 100644 --- a/config/csharp.yaml +++ b/config/csharp.yaml @@ -1,4 +1,7 @@ changelog: + - 1.6.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.5.0 (2024-07-19): - fix nuget package (closes \#131) - add *Async() methods (closes \#132) diff --git a/config/go.yaml b/config/go.yaml index be2a200d..97c167d8 100644 --- a/config/go.yaml +++ b/config/go.yaml @@ -1,4 +1,7 @@ changelog: + - 1.4.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.3.1 (2024-02-19): - Update VideoStatusIngest enum - 1.3.0 (2023-06-28): diff --git a/config/java.yaml b/config/java.yaml index fbf96358..93a4a709 100644 --- a/config/java.yaml +++ b/config/java.yaml @@ -1,4 +1,7 @@ changelog: + - 1.4.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.3.3 (2024-04-25): - Add API to get rate limiting headers - 1.3.2 (2024-02-19): diff --git a/config/nodejs.yaml b/config/nodejs.yaml index c906ea16..373a4a7e 100644 --- a/config/nodejs.yaml +++ b/config/nodejs.yaml @@ -1,4 +1,7 @@ changelog: + - 2.6.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 2.5.7 (2024-04-23): - Add *WithResponseHeaders() methods - 2.5.6 (2024-02-19): diff --git a/config/php.yaml b/config/php.yaml index fb1f0064..5438c88e 100644 --- a/config/php.yaml +++ b/config/php.yaml @@ -1,4 +1,7 @@ changelog: + - 1.4.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.3.2 (2024-02-19): - Update VideoStatusIngest enum - 1.3.1 (2023-06-28): diff --git a/config/python.yaml b/config/python.yaml index 4c379998..edcdd37f 100644 --- a/config/python.yaml +++ b/config/python.yaml @@ -1,4 +1,7 @@ changelog: + - 1.4.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.3.2 (2024-07-01): - Fix python "context" error - 1.3.1 (2024-02-19): diff --git a/config/swift5.yaml b/config/swift5.yaml index b3cdb9ef..d4b100c1 100644 --- a/config/swift5.yaml +++ b/config/swift5.yaml @@ -1,4 +1,7 @@ changelog: + - 1.3.0 (2024-07-29): + - Add new analytics methods + - Add livestream complete() method - 1.2.3 (2024-04-25): - Add API to get rate limiting headers - 1.2.2 (2024-02-19): diff --git a/oas_apivideo.yaml b/oas_apivideo.yaml index 461b6462..5ce48786 100644 --- a/oas_apivideo.yaml +++ b/oas_apivideo.yaml @@ -50,7 +50,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/authenticationinvalid_credentials' + type: 'https://docs.api.video/reference/authentication-invalid-user-credentials' title: The user credentials were incorrect. name: '' status: 400 @@ -126,7 +126,7 @@ paths: response: value: status: 400 - type: 'https://docs.api.video/docs/authenticationinvalid_credentials' + type: 'https://docs.api.video/reference/authentication-invalid-user-credentials' title: The user credentials were incorrect. name: '' x-client-action: refresh @@ -412,7 +412,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -618,21 +618,21 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/attributerequired' + type: 'https://docs.api.video/reference/attribute-required' title: This attribute is required. name: title status: 400 problems: - - type: 'https://docs.api.video/docs/attributerequired' + - type: 'https://docs.api.video/reference/attribute-required' title: This attribute is required. name: title - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be a ISO8601 date. name: scheduledAt - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be an array. name: tags - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be an array. name: metadata '429': @@ -655,7 +655,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -1010,21 +1010,21 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/filealreadyuploaded' + type: 'https://docs.api.video/reference/video-source-already-uploaded' title: The source of the video is already uploaded. name: file status: 400 problems: - - type: 'https://docs.api.video/docs/filealreadyuploaded' + - type: 'https://docs.api.video/reference/video-source-already-uploaded' title: The source of the video is already uploaded. name: file - - type: 'https://docs.api.video/docs/filealreadyuploaded' + - type: 'https://docs.api.video/reference/video-source-already-uploaded' title: The video xxxx has already been uploaded. name: video - - type: 'https://docs.api.video/docs/filemissing' + - type: 'https://docs.api.video/reference/uploaded-file-no-file' title: There is no uploaded file in the request. name: file - - type: 'https://docs.api.video/docs/multiplefilesuploaded' + - type: 'https://docs.api.video/reference/uploaded-file-multiple-files' title: There is more than one uploaded file in the request. name: file '404': @@ -1049,7 +1049,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -1073,7 +1073,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -1352,7 +1352,7 @@ paths: response: value: status: 400 - type: 'https://docs.api.video/docs/fileextension' + type: 'https://docs.api.video/reference/uploaded-file-extension-invalid' title: 'Only [jpeg, jpg, JPG, JPEG, png, PNG] extensions are supported.' name: file '429': @@ -1375,7 +1375,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -1690,7 +1690,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -1812,7 +1812,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: watermarkId status: 404 @@ -1836,7 +1836,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -1996,7 +1996,7 @@ paths: response: value: status: 400 - type: 'https://docs.api.video/docs/fileextension' + type: 'https://docs.api.video/reference/uploaded-file-extension-invalid' title: 'Only [jpeg, jpg, JPG, JPEG] extensions are supported.' name: file '404': @@ -2021,7 +2021,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -2045,7 +2045,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -2322,7 +2322,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -2346,7 +2346,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -2622,7 +2622,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -2646,7 +2646,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -2866,7 +2866,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -2890,7 +2890,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -3136,18 +3136,18 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/attributeinvalid' + type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be a ISO-8601 date. name: scheduledAt status: 400 problems: - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be a ISO-8601 date. name: scheduledAt - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be an array. name: tags - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be an array. name: metadata '404': @@ -3172,7 +3172,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -3196,7 +3196,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -3515,7 +3515,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: videoId status: 404 @@ -3539,7 +3539,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -3801,7 +3801,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -4077,7 +4077,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -4332,7 +4332,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -4568,7 +4568,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -4848,7 +4848,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -5071,7 +5071,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -5355,20 +5355,20 @@ paths: detail: This value should not be blank. name: restreams[0][name] Missing app name in serverUrl: - description: This error occurs when the rtmp app name is missing from `serverURL` in the `restreams` array. + description: This error occurs when the app name is missing from `serverURL` in the `restreams` array. value: type: https://docs.api.video/reference/invalid-attribute title: An attribute is invalid. status: 400 - detail: 'Missing app name: rtmp://[host]/[app name].' + detail: 'Missing app name: [rtmp|rtmps]://[host]/[app name].' name: restreams[0][serverUrl] Incorrect URL in serverUrl: - description: This error occurs when the URL you set in `serverURL` is not `rtmp`. + description: This error occurs when the URL you set in `serverURL` is not `rtmps` or `rtmp`. value: type: https://docs.api.video/reference/invalid-attribute title: An attribute is invalid. status: 400 - detail: 'RTMP URL should have the following format: rtmp://[host]/[app name].' + detail: 'RTMP URL should have the following format: [rtmp|rtmps]://[host]/[app name].' name: restreams[0][serverUrl] Too many restream destinations: description: This error occurs when you set more than 5 restream destinations. @@ -5406,7 +5406,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -5644,7 +5644,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -5893,7 +5893,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -6125,20 +6125,20 @@ paths: detail: This value should not be blank. name: restreams[0][name] Missing app name in serverUrl: - description: This error occurs when the rtmp app name is missing from `serverURL` in the `restreams` array. + description: This error occurs when the app name is missing from `serverURL` in the `restreams` array. value: type: https://docs.api.video/reference/invalid-attribute title: An attribute is invalid. status: 400 - detail: 'Missing app name: rtmp://[host]/[app name].' + detail: 'Missing app name: [rtmp|rtmps]://[host]/[app name].' name: restreams[0][serverUrl] Incorrect URL in serverUrl: - description: This error occurs when the URL you set in `serverURL` is not `rtmp`. + description: This error occurs when the URL you set in `serverURL` is not `rtmps` or `rtmp`. value: type: https://docs.api.video/reference/invalid-attribute title: An attribute is invalid. status: 400 - detail: 'RTMP URL should have the following format: rtmp://[host]/[app name].' + detail: 'RTMP URL should have the following format: [rtmp|rtmps]://[host]/[app name].' name: restreams[0][serverUrl] Too many restream destinations: description: This error occurs when you set more than 5 restream destinations. @@ -6176,7 +6176,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -6479,7 +6479,7 @@ paths: response: value: status: 400 - type: 'https://docs.api.video/docs/fileextension' + type: 'https://docs.api.video/reference/uploaded-file-extension-invalid' title: 'Only [jpeg, jpg, JPG, JPEG] extensions are supported.' name: file '404': @@ -6504,7 +6504,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: liveStreamId status: 404 @@ -6528,7 +6528,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -6762,7 +6762,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: liveStreamId status: 404 @@ -6786,7 +6786,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -6952,6 +6952,98 @@ paths: code: | // First install the api client: https://github.com/apivideo/api.video-swift-client#getting-started // Documentation: https://github.com/apivideo/api.video-swift-client/blob/main/docs/LiveStreamsAPI.md#deleteThumbnail + '/live-streams/{liveStreamId}/complete': + put: + tags: + - Live Streams + summary: Complete a live stream + description: | + Request the completion of a live stream that is currently running. This operation is asynchronous and the live stream will stop after a few seconds. + + The API adds the `EXT-X-ENDLIST` tag to the live stream's HLS manifest. This stops the live stream on the player and also stops the recording of the live stream. The API keeps the incoming connection from the streamer open for at most 1 minute, which can be used to terminate the stream. + operationId: PUT_live-streams-liveStreamId-complete + parameters: + - name: liveStreamId + in: path + description: The unique ID for the live stream you want to complete. + required: true + style: simple + explode: false + schema: + type: string + example: vi4k0jvEUuaTdRAEjQ4Jfrgz + responses: + '202': + headers: + X-RateLimit-Limit: + schema: + type: integer + description: The request limit per minute. + X-RateLimit-Remaining: + schema: + type: integer + description: The number of available requests left for the current time window. + X-RateLimit-Retry-After: + schema: + type: integer + description: The number of seconds left until the current rate limit window resets. + description: Accepted + '404': + headers: + X-RateLimit-Limit: + schema: + type: integer + description: The request limit per minute. + X-RateLimit-Remaining: + schema: + type: integer + description: The number of available requests left for the current time window. + X-RateLimit-Retry-After: + schema: + type: integer + description: The number of seconds left until the current rate limit window resets. + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/not-found' + examples: + response: + value: + type: 'https://docs.api.video/reference/resource-not-found' + title: The requested resource was not found. + name: liveStreamId + status: 404 + '429': + headers: + X-RateLimit-Limit: + schema: + type: integer + description: The request limit per minute. + X-RateLimit-Remaining: + schema: + type: integer + description: The number of available requests left for the current time window. + X-RateLimit-Retry-After: + schema: + type: integer + description: The number of seconds left until the current rate limit window resets. + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/too-many-requests' + examples: + Too many requests: + value: + type: 'https://docs.api.video/reference/too-many-requests' + title: Too many requests. + status: 429 + security: + - apiKey: [] + x-client-action: complete + x-doctave: + code-samples: '/videos/{videoId}/captions/{language}': get: tags: @@ -7091,7 +7183,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -7397,7 +7489,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -7694,7 +7786,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -8003,7 +8095,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -8292,7 +8384,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -8554,7 +8646,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -8837,7 +8929,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -9093,7 +9185,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -9353,7 +9445,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -9689,7 +9781,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -9951,7 +10043,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -10275,7 +10367,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: playerId status: 404 @@ -10299,7 +10391,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -10512,7 +10604,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: playerId status: 404 @@ -10536,7 +10628,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -10774,7 +10866,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: playerId status: 404 @@ -10798,7 +10890,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -11097,7 +11189,7 @@ paths: response: value: status: 400 - type: 'https://docs.api.video/docs/fileextension' + type: 'https://docs.api.video/reference/uploaded-file-extension-invalid' title: 'Only [''jpg'', ''JPG'', ''jpeg'', ''JPEG'', ''png'', ''PNG''] extensions are supported.' name: file '404': @@ -11122,7 +11214,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: playerId status: 404 @@ -11146,7 +11238,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -11385,7 +11477,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: playerId status: 404 @@ -11409,7 +11501,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -11560,78 +11652,88 @@ paths: code: | // First install the api client: https://github.com/apivideo/api.video-swift-client#getting-started // Documentation: https://github.com/apivideo/api.video-swift-client/blob/main/docs/PlayerThemesAPI.md#deleteLogo - '/analytics/videos/plays': + '/data/metrics/{metric}/{aggregation}': get: tags: - Analytics - summary: Get play events for video - description: Retrieve filtered analytics about the number of plays for your - videos in a project. - operationId: GET_analytics-videos-plays + summary: Retrieve aggregated metrics + description: Retrieve time-based and countable metrics like average watch time or the number of impressions over a certain period of time. + operationId: GET_aggregated-metrics parameters: - - name: from - in: query + - name: metric + in: path description: | - Use this query parameter to set the start date for the time period that you want analytics for. - - The API returns analytics data including the day you set in `from`. - - The date you set must be **within the last 30 days**. - - The value you provide must follow the `YYYY-MM-DD` format. + Use this path parameter to select a metric that you want analytics for. + + - `play` is the number of times your content has been played. You can use the aggregations `count`, `rate`, and `total` with the `play` metric. + - `start` is the number of times playback was started. You can use the aggregation `count` with this metric. + - `end` is the number of times playback has ended with the content watch until the end. You can use the aggregation `count` with this metric. + - `impression` is the number of times your content has been loaded and was ready for playback. You can use the aggregation `count` with this metric. + - `impression-time` is the time in milliseconds that your content was loading for until the first video frame is displayed. You can use the aggregations `average` and `sum` with this metric. + - `watch-time` is the cumulative time in seconds that the user has spent watching your content. You can use the aggregations `average` and `sum` with this metric. required: true - style: form - explode: true + style: simple + explode: false schema: type: string - format: date - example: 2023-06-01 - - name: to - in: query + enum: + - play + - start + - end + - impression + - impression-time + - watch-time + - name: aggregation + in: path description: | - Use this optional query parameter to set the end date for the time period that you want analytics for. - - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - - The date you set must be **within the last 30 days**. - - The value you provide must follow the `YYYY-MM-DD` format. - required: false - style: form - explode: true + Use this path parameter to define a way of collecting data for the metric that you want analytics for. + + - `count` returns the overall number of events for the `play` metric. + - `rate` returns the ratio that calculates the number of plays your content receives divided by its impressions. This aggregation can be used only with the `play` metric. + - `total` calculates the total number of events for the `play` metric. + - `average` calculates an average value for the selected metric. + - `sum` adds up the total value of the select metric. + style: simple + explode: false + required: true schema: type: string - format: date - example: 2023-06-10 - - name: dimension + enum: + - count + - rate + - total + - average + - sum + - name: from in: query - description: |- - Use this query parameter to define the dimension that you want analytics for. - - `videoId`: Returns analytics based on the public video identifiers. - - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - - `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/). - - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. Possible response values are: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`. - - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. Response values include `windows`, `mac osx`, `android`, `ios`, `linux`. - - `browser`: Returns analytics based on the browser used by the viewers during the play event. Response values include `chrome`, `firefox`, `edge`, `opera`. - required: true + description: | + Use this query parameter to define the starting date-time of the period you want analytics for. + + - If you do not set a value for `from`, the default assigned value is 1 day ago, based on the `to` parameter. + - The maximum value is 30 days ago. + - The value you provide should follow the ATOM date-time format: `2024-02-05T00:00:00+01:00` + - The API ignores this parameter when you call `/data/metrics/play/total`. style: form explode: false schema: type: string - enum: - - videoId - - emittedAt - - country - - deviceType - - operatingSystem - - browser - example: browser - - name: filter + format: date-time + example: 2024-02-05T00:00:00+01:00 + - name: to in: query - description: Use this query parameter to filter your results to a specific - video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. - required: false + description: | + Use this query parameter to define the ending date-time of the period you want analytics for. + + - If you do not set a value for `to`, the default assigned value is `now`. + - The API ignores this parameter when you call `/data/metrics/play/total`. + - The value for `to` is a non-inclusive value: the API returns data **before** the date-time that you set. style: form explode: false schema: type: string - example: videoId:vi3q7HxhApxRF1c8F8r6VeaI - - $ref: '#/components/parameters/current-page' - - $ref: '#/components/parameters/page-size' + format: date-time + example: 2024-02-06T00:00:00+01:00 + - $ref: '#/components/parameters/filterBy' responses: '200': headers: @@ -11651,79 +11753,17 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/analytics-plays-response' + $ref: '#/components/schemas/analytics-aggregated-metrics-response' examples: - Dimension is videoId: - description: Breakdown video-plays by videoId for a project. - value: - data: - - value: vi3q7HxhApxRF1c8F8r6VeaI - plays: 100 - - value: vi3q7HxhApxRF1c8F8r6VeaF - plays: 10 - - value: vi3q7HxhApxRF1c8F8r6VeaH - plays: 1 - pagination: - currentPage: 1 - currentPageItems: 3 - pageSize: 25 - pagesTotal: 1 - itemsTotal: 3 - links: - - rel: self - uri: "/analytics/videos/plays?dimension=videoId¤tPage=1&pageSize=25" - - rel: first - uri: "/analytics/videos/plays?dimension=videoId¤tPage=1&pageSize=25" - - rel: last - uri: "/analytics/videos/plays?dimension=videoId¤tPage=1&pageSize=25" - Dimension is country: - description: Breakdown video-plays by country for a project, with pagination set. - value: - data: - - value: france - plays: 100 - - value: united states - plays: 10 - - value: spain - plays: 1 - pagination: - currentPage: 1 - currentPageItems: 2 - pageSize: 2 - pagesTotal: 2 - itemsTotal: 3 - links: - - rel: self - uri: "/analytics/videos/plays?dimension=country¤tPage=1&pageSize=2" - - rel: first - uri: "/analytics/videos/plays?dimension=country¤tPage=1&pageSize=2" - - rel: next - uri: "/analytics/videos/plays?dimension=country¤tPage=2&pageSize=1" - - rel: last - uri: "/analytics/videos/plays?dimension=country¤tPage=2&pageSize=1" - Dimension is emittedAt, filtered for a videoId: - description: Breakdown video-plays by the time of play events, for a specific video. - value: - data: - - value: 2023-06-10T10:00:00.000Z - plays: 100 - - value: 2023-06-10T11:00:00.000Z - plays: 10 - - value: 2023-06-10T12:00:00.000Z - plays: 1 - pagination: - currentPage: 1 - currentPageItems: 3 - pageSize: 25 - pagesTotal: 1 - itemsTotal: 3 - links: - - rel: self - uri: "/analytics/videos/plays?dimension=videoId&filter=videoId:vi3VooPMbQLWdPF26qfmNVX6¤tPage=1&pageSize=25" - - rel: first - uri: "/analytics/videos/plays?dimension=videoId&filter=videoId:vi3VooPMbQLWdPF26qfmNVX6¤tPage=1&pageSize=25" - - rel: last - uri: "/analytics/videos/plays?dimension=videoId&filter=videoId:vi3VooPMbQLWdPF26qfmNVX6¤tPage=1&pageSize=25" + Impression time from a certain date: + value: + context: + metric: impression + aggregation: count + timeframe: + from: 2024-05-28T11:15:07+00:00 + to: 2024-05-29T11:15:07+00:00 + data: 346.5 '400': headers: X-RateLimit-Limit: @@ -11745,79 +11785,22 @@ paths: title: Bad request error $ref: '#/components/schemas/analytics-plays-400-error' examples: - Missing parameter: - description: This error occurs when a required query-parameter is missing. + Invalid attribute: + description: This error occurs when a parameter you provided does not exist, or isn't correct for this endpoint, has an invalid value. value: type: https://docs.api.video/reference/invalid-attribute title: An attribute is invalid. status: 400 detail: This value must be of type string. - name: dimension - Invalid parameter: - description: This error occurs when a required query-parameter format is invalid. - value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be of the following structure(s): videoId:{videoId}' - name: filter - Dimension not allowed: - description: This error occurs when the dimension you requested is not allowed for the endpoint. For example, the dimension `videoId` is not allowed for the `/live-streams` endpoint. - value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be part of the following values: emittedAt,videoId,country,deviceType,operatingSystem,browser' - name: dimension - Dimension unknown: - description: This error occurs when the dimension you requested is unknown. - value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be part of the following values: emittedAt,videoId,country,deviceType,operatingSystem,browser' - name: dimension - Invalid filter: - description: This error occurs when the format of the filter you requested is invalid. - value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be of the following structure(s): videoId:{videoId}' - name: filter - Invalid videoId: - description: This error occurs when the videoId you requested does not refer to an existing video. + name: metric + Invalid query parameter: + description: This error occurs when a query parameter you provided does not exist, isn't correct for this endpoint, or has an invalid value. value: type: https://docs.api.video/reference/request-invalid-query-parameter title: A query parameter is invalid. status: 400 - detail: This value must refer to an existing video - name: filter - '403': - headers: - X-RateLimit-Limit: - schema: - type: integer - description: The request limit per minute. - X-RateLimit-Remaining: - schema: - type: integer - description: The number of available requests left for the current time window. - X-RateLimit-Retry-After: - schema: - type: integer - description: The number of seconds left until the current rate limit window resets. - description: Forbidden - Disabled Analytics - content: - application/json: - schema: - $ref: '#/components/schemas/403-error-schema' - examples: - Analytics is disabled: - value: - type: https://docs.api.video/reference/authorization-disabled-analytics - title: You cannot get analytics from this project. - status: 403 + detail: This field was not expected. + name: from:2024-05-20T09:15:05+02:00 '404': headers: X-RateLimit-Limit: @@ -11832,17 +11815,16 @@ paths: schema: type: integer description: The number of seconds left until the current rate limit window resets. - description: Not Found + description: Unrecognized request URL content: application/json: schema: - $ref: '#/components/schemas/not-found' + $ref: '#/components/schemas/unrecognized-request-url' examples: - Endpoint not found: + Unrecognized request URL: value: - type: - title: - name: + type: 'https://docs.api.video/reference/unrecognized-request-url' + title: Unrecognized request URL. status: 404 '429': headers: @@ -11864,294 +11846,165 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. status: 429 security: - apiKey: [] - x-client-action: getVideosPlays + x-client-action: getAggregatedMetrics x-group-parameters: true - x-client-paginated: true x-doctave: code-samples: - - language: go - code: | - package main - - import ( - "context" - "fmt" - "os" - "time" - apivideosdk "github.com/apivideo/api.video-go-client" - ) - - func main() { - client := apivideosdk.ClientBuilder("YOUR_API_KEY").Build() - // if you rather like to use the sandbox environment: - // client := apivideosdk.SandboxClientBuilder("YOU_SANDBOX_API_KEY").Build() - req := apivideosdk.AnalyticsApiGetVideosPlaysRequest{} - - req.From(time.Now()) // string | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - req.Dimension("browser") // string | Use this query parameter to define the dimension that you want analytics for. - `videoId`: Returns analytics based on the public video identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - req.To(time.Now()) // string | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - req.Filter("videoId:vi3q7HxhApxRF1c8F8r6VeaI") // string | Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. - req.CurrentPage(int32(2)) // int32 | Choose the number of search results to return per page. Minimum value: 1 (default to 1) - req.PageSize(int32(30)) // int32 | Results per page. Allowed values 1-100, default is 25. (default to 25) - - res, err := client.Analytics.GetVideosPlays(req) - - - if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `Analytics.GetVideosPlays``: %v\n", err) - } - // response from `GetVideosPlays`: AnalyticsPlaysResponse - fmt.Fprintf(os.Stdout, "Response from `Analytics.GetVideosPlays`: %v\n", res) - } - - language: node - code: |- - // First install the "@api.video/nodejs-client" npm package - // Documentation: https://github.com/apivideo/api.video-nodejs-client/blob/main/doc/api/AnalyticsApi.md#getVideosPlays - - const client = new ApiVideoClient({ apiKey: "YOUR_API_KEY" }); - - const from = "2023-06-01"; // Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - const dimension = "videoId"; // Use this query parameter to define the dimension that you want analytics for. - `videoId`: Returns analytics based on the public video identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - const to = "2023-06-10"; // Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - const filter = "videoId:vi3q7HxhApxRF1c8F8r6VeaI"; // Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. - const currentPage = 1; // Choose the number of search results to return per page. Minimum value: 1 - const pageSize = 25; // Results per page. Allowed values 1-100, default is 25. - - const videoPlays = await client.analytics.getVideosPlays({ - from, dimension, to, filter, currentPage, pageSize - }); - - language: python - code: | - import apivideo - from apivideo.api import analytics_api - from apivideo.model.analytics_plays_response import AnalyticsPlaysResponse - from apivideo.model.not_found import NotFound - from apivideo.model.analytics_plays400_error import AnalyticsPlays400Error - from pprint import pprint - - # Enter a context with an instance of the API client - with apivideo.AuthenticatedApiClient(__API_KEY__) as api_client: - # Create an instance of the API class - api_instance = analytics_api.AnalyticsApi(api_client) - _from = dateutil_parser('2023-06-01').date() # date | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - dimension = "browser" # str | Use this query parameter to define the dimension that you want analytics for. - `videoId`: Returns analytics based on the public video identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - to = dateutil_parser('2023-06-10').date() # date | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. (optional) - filter = "videoId:vi3q7HxhApxRF1c8F8r6VeaI" # str | Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. (optional) - current_page = 2 # int | Choose the number of search results to return per page. Minimum value: 1 (optional) if omitted the server will use the default value of 1 - page_size = 30 # int | Results per page. Allowed values 1-100, default is 25. (optional) if omitted the server will use the default value of 25 - - # example passing only required values which don't have defaults set - try: - # Get play events for video - api_response = api_instance.get_videos_plays(_from, dimension) - pprint(api_response) - except apivideo.ApiException as e: - print("Exception when calling AnalyticsApi->get_videos_plays: %s\n" % e) - - # example passing only required values which don't have defaults set - # and optional values - try: - # Get play events for video - api_response = api_instance.get_videos_plays(_from, dimension, to=to, filter=filter, current_page=current_page, page_size=page_size) - pprint(api_response) - except apivideo.ApiException as e: - print("Exception when calling AnalyticsApi->get_videos_plays: %s\n" % e) - - language: java - code: | - // Import classes: - import video.api.client.ApiVideoClient; - import video.api.client.api.ApiException; - import video.api.client.api.models.*; - import video.api.client.api.clients.AnalyticsApi; - import java.util.*; - - public class Example { - public static void main(String[] args) { - ApiVideoClient client = new ApiVideoClient("YOUR_API_KEY"); - // if you rather like to use the sandbox environment: - // ApiVideoClient client = new ApiVideoClient("YOUR_SANDBOX_API_KEY", Environment.SANDBOX); - - AnalyticsApi apiInstance = client.analytics(); - - LocalDate from = LocalDate.parse("2023-06-01"); // Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - String dimension = "videoId"; // Use this query parameter to define the dimension that you want analytics for. - `videoId`: Returns analytics based on the public video identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - LocalDate to = LocalDate.parse("2023-06-10"); // Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - String filter = "videoId:vi3q7HxhApxRF1c8F8r6VeaI"; // Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. - Integer currentPage = 1; // Choose the number of search results to return per page. Minimum value: 1 - Integer pageSize = 25; // Results per page. Allowed values 1-100, default is 25. - - try { - Page result = apiInstance.getVideosPlays(from, dimension) - .to(to) - .filter(filter) - .currentPage(currentPage) - .pageSize(pageSize) - .execute(); - System.out.println(result); - } catch (ApiException e) { - System.err.println("Exception when calling AnalyticsApi#getVideosPlays"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getMessage()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } - } - } - - language: csharp - code: | - using System.Diagnostics; - using ApiVideo.Client; - - namespace Example - { - public class getVideosPlaysExample - { - public static void Main() - { - var basePath = ApiVideoClient.Client.Environment.SANDBOX; - var apiKey = "YOUR_API_KEY"; - - var apiInstance = new ApiVideoClient(apiKey,basePath); - - var from = 2023-06-01; // DateTime | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - var dimension = browser; // string | Use this query parameter to define the dimension that you want analytics for. - `videoId`: Returns analytics based on the public video identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - var to = 2023-06-10; // DateTime? | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. (optional) - var filter = videoId:vi3q7HxhApxRF1c8F8r6VeaI; // string | Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. (optional) - var currentPage = 2; // int? | Choose the number of search results to return per page. Minimum value: 1 (optional) (default to 1) - var pageSize = 30; // int? | Results per page. Allowed values 1-100, default is 25. (optional) (default to 25) - var apiAnalyticsInstance = apiInstance.Analytics(); - try - { - // Get play events for video - AnalyticsPlaysResponse result = apiAnalyticsInstance.getVideosPlays(from, dimension, to, filter, currentPage, pageSize); - Debug.WriteLine(result); - } - catch (ApiException e) - { - Debug.Print("Exception when calling AnalyticsApi.getVideosPlays: " + e.Message ); - Debug.Print("Status Code: "+ e.ErrorCode); - Debug.Print(e.StackTrace); - } - } - } - } - - language: php - code: |- - analytics()->getVideosPlays($from, $dimension, array( - 'to' => new \DateTime('2023-06-10'), // Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - 'filter' => "videoId:vi3q7HxhApxRF1c8F8r6VeaI", // Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. - 'currentPage' => 2, // Choose the number of search results to return per page. Minimum ->setvalue(1) - 'pageSize' => 30 // Results per page. Allowed values 1-100, default is 25. - )); - - language: swift - code: | - // The following code samples are still beta. For any issue, please report via http://github.com/OpenAPITools/openapi-generator/issues/new - import ApiVideoClient - - let from = Date() // Date | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - let dimension = "dimension_example" // String | Use this query parameter to define the dimension that you want analytics for. - `videoId`: Returns analytics based on the public video identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - let to = Date() // Date | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. (optional) - let filter = "filter_example" // String | Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the `videoId:` prefix when specifying a video ID. (optional) - let currentPage = 987 // Int | Choose the number of search results to return per page. Minimum value: 1 (optional) (default to 1) - let pageSize = 987 // Int | Results per page. Allowed values 1-100, default is 25. (optional) (default to 25) - - // Get play events for video - AnalyticsAPI.getVideosPlays(from: from, dimension: dimension, to: to, filter: filter, currentPage: currentPage, pageSize: pageSize) { (response, error) in - guard error == nil else { - print(error) - return - } - - if (response) { - dump(response) - } - } - '/analytics/live-streams/plays': + AnalyticsAggregatedMetricsResponse res = apiClient.analytics().getAggregatedMetrics("play", "total").filterBy(filterBy).execute(); + System.out.println(res.getData()); + - language: node + code: | + const res = await client.analytics.getAggregatedMetrics({ + aggregation: 'total', + metric: 'play', + filterBy: { + browser: ['chrome', 'firefox'], + continent: ['EU', 'AF'], + tag: 'test', + }, + }); + console.log(res.data); + - language: csharp + code: | + FilterBy2 filterBy = new FilterBy2 + { + continent = new List { "EU", "US" }, + devicetype = new List { "phone" }, + tag = "test" + }; + AnalyticsAggregatedMetricsResponse res = apiClient.Analytics() + .getAggregatedMetrics("play", "total").From(new DateTime(2024, 7, 1)).FilterBy(filterBy).execute(); + - language: go + code: | + res, err := cl.Analytics.GetAggregatedMetrics("play", "day", AnalyticsApiGetAggregatedMetricsRequest{ + filterBy: &FilterBy2{ + Continent: &[]string{"EU", "AA"}, + DeviceType: &[]string{"computer", "phone"}, + Tag: PtrString("tag"), + }, + }) + - language: python + code: | + res = self.api.get_aggregated_metrics(metric='play', aggregation='count', filter_by=FilterBy2( + device_type=["computer", "phone"], + tag="test", + ), + ) + - language: swift + code: | + AnalyticsAPI.getAggregatedMetrics( + metric: .play, + aggregation: .total, + filterBy: FilterBy2( + continent: [.an, .eu], + tag: "test" + ) + ) { analytics, error in + // ... + } + '/data/buckets/{metric}/{breakdown}': get: tags: - Analytics - summary: Get play events for live stream - description: Retrieve filtered analytics about the number of plays for your - live streams in a project. - operationId: GET_analytics-live-streams-plays + summary: Retrieve metrics in a breakdown of dimensions + description: Retrieve detailed analytics play-rate and number of impressions segmented by dimensions like country or device type. + operationId: GET_metrics-breakdown parameters: - - name: from - in: query + - name: metric + in: path description: | - Use this query parameter to set the start date for the time period that you want analytics for. - - The API returns analytics data including the day you set in `from`. - - The date you set must be **within the last 30 days**. - - The value you provide must follow the `YYYY-MM-DD` format. + Use this path parameter to select a metric that you want analytics for. + + - `play` is the number of times your content has been played. + - `play-rate` is the ratio that calculates the number of plays your content receives divided by its impressions. + - `play-total` is the total number of times a specific content has been played. You can only use the `media-id` breakdown with this metric. + - `start` is the number of times playback was started. + - `end` is the number of times playback has ended with the content watch until the end. + - `impression` is the number of times your content has been loaded and was ready for playback. required: true - style: form - explode: true + style: simple + explode: false schema: type: string - format: date - example: 2023-06-01 - - name: to - in: query + enum: + - play + - play-rate + - play-total + - start + - end + - impression + - name: breakdown + in: path description: | - Use this optional query parameter to set the end date for the time period that you want analytics for. - - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - - The date you set must be **within the last 30 days**. - - The value you provide must follow the `YYYY-MM-DD` format. - required: false - style: form - explode: true - schema: - type: string - format: date - example: 2023-06-10 - - name: dimension - in: query - description: |- - Use this query parameter to define the dimension that you want analytics for. - - `liveStreamId`: Returns analytics based on the public live stream identifiers. - - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. + Use this path parameter to define a dimension for segmenting analytics data. You must use `kebab-case` for path parameters. + + These are the available dimensions: + + - `media-id`: Returns analytics based on the unique identifiers of a video or a live stream. + - `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/). - - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. Possible response values are: `computer`, `phone`, `tablet`, `tv`, `console`, `wearable`, `unknown`. - - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. Response values include `windows`, `mac osx`, `android`, `ios`, `linux`. - - `browser`: Returns analytics based on the browser used by the viewers during the play event. 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 + schema: + type: string + enum: + - media-id + - media-type + - continent + - country + - device-type + - operating-system + - browser + - name: from + in: query + description: | + Use this query parameter to define the starting date-time of the period you want analytics for. + + - If you do not set a value for `from`, the default assigned value is 1 day ago, based on the `to` parameter. + - The maximum value is 30 days ago. + - The value you provide should follow the ATOM date-time format: `2024-02-05T00:00:00+01:00` style: form explode: false schema: type: string - enum: - - liveStreamId - - emittedAt - - country - - deviceType - - operatingSystem - - browser - example: browser - - name: filter + format: date-time + example: 2024-02-05T00:00:00+01:00 + - name: to in: query - description: Use this query parameter to filter your results to a specific - live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. - required: false + description: | + Use this query parameter to define the ending date-time of the period you want analytics for. + + - If you do not set a value for `to`, the default assigned value is `now`. + - The value for `to` is a non-inclusive value: the API returns data **before** the date-time that you set. style: form explode: false schema: type: string - example: liveStreamId:li3q7HxhApxRF1c8F8r6VeaI + format: date-time + example: 2024-02-06T00:00:00+01:00 + - $ref: '#/components/parameters/filterBy' - $ref: '#/components/parameters/current-page' - $ref: '#/components/parameters/page-size' responses: @@ -12173,77 +12026,32 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/analytics-plays-response' + $ref: '#/components/schemas/analytics-metrics-breakdown-response' examples: - Dimension is liveStreamId: - description: Breakdown video-plays by liveStreamId for a project. - value: + Number of plays in a country over a specific timeframe: + value: + context: + metric: play + breakdown: country + timeframe: + from: '2024-04-28T07:15:05+00:00' + to: '2024-05-29T11:25:37+00:00' data: - - value: li3q7HxhApxRF1c8F8r6VeaI - plays: 100 - - value: li3q7HxhApxRF1c8F8r6VeaB - plays: 10 - - value: li3q7HxhApxRF1c8F8r6VeaD - plays: 1 + - metricValue: 7 + dimensionValue: FR pagination: currentPage: 1 - currentPageItems: 2 - pageSize: 2 - pagesTotal: 2 - itemsTotal: 3 - links: - - rel: self - uri: "/analytics/live-streams/plays?dimension=liveStreamId¤tPage=1&pageSize=2" - - rel: first - uri: "/analytics/live-streams/plays?dimension=liveStreamId¤tPage=2&pageSize=1" - - rel: last - uri: "/analytics/live-streams/plays?dimension=liveStreamId¤tPage=2&pageSize=1" - Dimension is country, paginated: - description: Breakdown video-plays by country for a project, with pagination. - value: - data: - - value: france - plays: 100 - - value: united states - plays: 10 - - value: spain - plays: 1 - pagination: - currentPage: 1 - currentPageItems: 2 - pageSize: 2 - pagesTotal: 2 - itemsTotal: 3 - links: - - rel: self - uri: "/analytics/live-streams/plays?dimension=country¤tPage=1&pageSize=2" - - rel: first - uri: "/analytics/live-streams/plays?dimension=country¤tPage=1&pageSize=2" - - rel: last - uri: "/analytics/live-streams/plays?dimension=country¤tPage=2&pageSize=1" - Dimension is emittedAt, filtered for a liveStreamId: - description: Breakdown video-plays by the time of play events, for a specific live stream. - value: - data: - - value: 2023-06-10T10:00:00.000Z - plays: 100 - - value: 2023-06-10T11:00:00.000Z - plays: 10 - - value: 2023-06-10T12:00:00.000Z - plays: 1 - pagination: - currentPage: 1 - currentPageItems: 3 + currentPageItems: 1 pageSize: 25 pagesTotal: 1 - itemsTotal: 3 + itemsTotal: 1 links: - rel: self - uri: "/analytics/live-streams/plays?dimension=videoId&filter=liveStreamId:li3VooPMbQLWdPF26qfmNVX6¤tPage=1&pageSize=25" + uri: "/data/buckets/play/country?from=2024-04-28T09%3A15%3A05%2B02%3A00¤tPage=1&pageSize=25" - rel: first - uri: "/analytics/live-streams/plays?dimension=videoId&filter=liveStreamId:li3VooPMbQLWdPF26qfmNVX6¤tPage=1&pageSize=25" + uri: "/data/buckets/play/country?from=2024-04-28T09%3A15%3A05%2B02%3A00¤tPage=1&pageSize=25" - rel: last - uri: "/analytics/live-streams/plays?dimension=videoId&filter=liveStreamId:li3VooPMbQLWdPF26qfmNVX6¤tPage=1&pageSize=25" + uri: "/data/buckets/play/country?from=2024-04-28T09%3A15%3A05%2B02%3A00¤tPage=1&pageSize=25" '400': headers: X-RateLimit-Limit: @@ -12265,55 +12073,265 @@ paths: title: Bad request error $ref: '#/components/schemas/analytics-plays-400-error' examples: - Missing parameter: - description: This error occurs when a required query-parameter is missing. + Invalid attribute: + description: This error occurs when a parameter you provided does not exist, or isn't correct for this endpoint, has an invalid value. value: type: https://docs.api.video/reference/invalid-attribute title: An attribute is invalid. status: 400 detail: This value must be of type string. - name: dimension - Invalid parameter: - description: This error occurs when a required query-parameter format is invalid. - value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be of the following structure(s): liveStreamId:{liveStreamId}' - name: filter - Dimension not allowed: - description: This error occurs when the dimension you requested is not allowed for the endpoint. For example, the dimension `videoId` is not allowed for the `/live-streams` endpoint. - value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be part of the following values: emittedAt,liveStreamId,country,deviceType,operatingSystem,browser' - name: dimension - Dimension unknown: - description: This error occurs when the dimension you requested is unknown. + name: metric + Invalid query parameter: + description: This error occurs when a query parameter you provided does not exist, isn't correct for this endpoint, or has an invalid value. value: type: https://docs.api.video/reference/request-invalid-query-parameter title: A query parameter is invalid. status: 400 - detail: 'This value must be part of the following values: emittedAt,liveStreamId,country,deviceType,operatingSystem,browser' - name: dimension - Invalid filter: - description: This error occurs when the format of the filter you requested is invalid. + detail: This field was not expected. + name: from:2024-05-20T09:15:05+02:00 + '404': + headers: + X-RateLimit-Limit: + schema: + type: integer + description: The request limit per minute. + X-RateLimit-Remaining: + schema: + type: integer + description: The number of available requests left for the current time window. + X-RateLimit-Retry-After: + schema: + type: integer + description: The number of seconds left until the current rate limit window resets. + description: Unrecognized request URL + content: + application/json: + schema: + $ref: '#/components/schemas/unrecognized-request-url' + examples: + Unrecognized request URL: value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: 'This value must be of the following structure(s): liveStreamId:{liveStreamId}' - name: filter - Invalid liveStreamId: - description: This error occurs when the liveStreamId you requested does not refer to an existing live stream. + type: 'https://docs.api.video/reference/unrecognized-request-url' + title: Unrecognized request URL. + status: 404 + '429': + headers: + X-RateLimit-Limit: + schema: + type: integer + description: The request limit per minute. + X-RateLimit-Remaining: + schema: + type: integer + description: The number of available requests left for the current time window. + X-RateLimit-Retry-After: + schema: + type: integer + description: The number of seconds left until the current rate limit window resets. + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/too-many-requests' + examples: + Too many requests: value: - type: https://docs.api.video/reference/request-invalid-query-parameter - title: A query parameter is invalid. - status: 400 - detail: This value must refer to an existing live stream - name: filter - '403': + type: 'https://docs.api.video/reference/too-many-requests' + title: Too many requests. + status: 429 + security: + - apiKey: [] + x-client-action: getMetricsBreakdown + x-group-parameters: true + x-client-paginated: true + x-doctave: + code-samples: + - language: java + code: | + FilterBy2 filterBy = new FilterBy2(); + + filterBy.setBrowser(Collections.singletonList("Chrome")); + filterBy.setContinent(Arrays.asList(FilterBy2.ContinentEnum.NA, FilterBy2.ContinentEnum.EU)); + filterBy.setMediaType(FilterBy2.MediaTypeEnum.VIDEO); + filterBy.setTag("test"); + + Page res = apiClient.analytics().getMetricsBreakdown("play", "media-id").filterBy(filterBy).pageSize(30).execute(); + for (AnalyticsMetricsBreakdownResponseData item : res.getItems()) { + System.out.println(item.getDimensionValue() + ": " + item.getMetricValue()); + } + - language: node + code: | + const res = await client.analytics.getMetricsBreakdown({ + breakdown: 'continent', + metric: 'play', + filterBy: { + browser: ['chrome', 'firefox'], + continent: ['EU', 'AF'], + tag: 'test', + }, + }); + console.log(res.data); + - language: csharp + code: | + FilterBy2 filterBy = new FilterBy2 + { + continent = new List { "EU", "US" }, + devicetype = new List { "phone" }, + tag = "test" + }; + Page 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{ + Continent: &[]string{"EU", "AA"}, + DeviceType: &[]string{"computer", "phone"}, + Tag: PtrString("tag"), + }, + }) + - language: python + code: | + res = self.api.get_metrics_breakdown(metric='play', breakdown='continent', filter_by=FilterBy2( + device_type=["computer", "phone"], + tag="test", + ), + ) + - language: swift + code: | + AnalyticsAPI.getMetricsBreakdown( + metric: .play, + breakdown: .browser, + filterBy: FilterBy2( + continent: [.an, .eu], + tag: "test" + ) + ) { analytics, error in + // ... + } + '/data/timeseries/{metric}': + get: + tags: + - Analytics + summary: Retrieve metrics over time + description: Retrieve countable metrics like the number of plays or impressions, grouped by the time at which they occurred + operationId: GET_metrics-over-time + parameters: + - name: metric + in: path + description: | + Use this path parameter to select a metric that you want analytics for. + + - `play` is the number of times your content has been played. + - `play-rate` is the ratio that calculates the number of plays your content receives divided by its impressions. + - `start` is the number of times playback was started. + - `end` is the number of times playback has ended with the content watch until the end. + - `impression` is the number of times your content has been loaded and was ready for playback. + required: true + style: simple + explode: false + schema: + type: string + enum: + - play + - play-rate + - start + - end + - impression + - name: from + in: query + description: | + Use this query parameter to define the starting date-time of the period you want analytics for. + + - If you do not set a value for `from`, the default assigned value is 1 day ago, based on the `to` parameter. + - The maximum value is 30 days ago. + - The value you provide should follow the ATOM date-time format: `2024-02-05T00:00:00+01:00` + style: form + explode: false + schema: + type: string + format: date-time + example: 2024-02-05T00:00:00+01:00 + - name: to + in: query + description: | + Use this query parameter to define the ending date-time of the period you want analytics for. + + - If you do not set a value for `to`, the default assigned value is `now`. + - The value for `to` is a non-inclusive value: the API returns data **before** the date-time that you set. + style: form + explode: false + schema: + type: string + format: date-time + example: 2024-02-06T00:00:00+01:00 + - name: interval + in: query + description: | + Use this query parameter to define how granularity of the data. Possible values: `hour`, `day`. + + - Default: If no interval specified and the period (different between from and to) ≤ 2 days then hour, otherwise day. + + - If you do not set a value for `interval`, and the period you set using the `from` and `to` parameters is less than or equals to 2 days, then the default assigned value is `hour`. Otherwise the API sets it to `day`. + style: form + explode: false + schema: + type: string + enum: [hour, day] + example: hour + - $ref: '#/components/parameters/filterBy' + - $ref: '#/components/parameters/current-page' + - $ref: '#/components/parameters/page-size' + responses: + '200': + headers: + X-RateLimit-Limit: + schema: + type: integer + description: The request limit per minute. + X-RateLimit-Remaining: + schema: + type: integer + description: The number of available requests left for the current time window. + X-RateLimit-Retry-After: + schema: + type: integer + description: The number of seconds left until the current rate limit window resets. + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/analytics-metrics-over-time-response' + examples: + Impression time from a certain date: + value: + context: + metric: play + interval: hour + timeframe: + from: '2024-05-28T11:08:39+00:00' + to: '2024-05-29T11:08:39+00:00' + data: + - emittedAt: '2024-05-29T07:00:00+00:00' + metricValue: 2 + - emittedAt: '2024-05-29T08:00:00+00:00' + metricValue: 1 + - emittedAt: '2024-05-29T09:00:00+00:00' + metricValue: 1 + pagination: + currentPage: 1 + currentPageItems: 3 + pageSize: 25 + pagesTotal: 1 + itemsTotal: 3 + links: + - rel: self + uri: "/data/timeseries/play?currentPage=1&pageSize=25" + - rel: first + uri: "/data/timeseries/play?currentPage=1&pageSize=25" + - rel: last + uri: "/data/timeseries/play?currentPage=1&pageSize=25" + '400': headers: X-RateLimit-Limit: schema: @@ -12327,17 +12345,29 @@ paths: schema: type: integer description: The number of seconds left until the current rate limit window resets. - description: Forbidden - Disabled Analytics + description: Bad request error content: application/json: schema: - $ref: '#/components/schemas/403-error-schema' + title: Bad request error + $ref: '#/components/schemas/analytics-plays-400-error' examples: - Analytics is disabled: + Invalid attribute: + description: This error occurs when a parameter you provided does not exist, or isn't correct for this endpoint, has an invalid value. value: - type: https://docs.api.video/reference/authorization-disabled-analytics - title: You cannot get analytics from this project. - status: 403 + type: https://docs.api.video/reference/invalid-attribute + title: An attribute is invalid. + status: 400 + detail: This value must be of type string. + name: metric + Invalid query parameter: + description: This error occurs when a query parameter you provided does not exist, isn't correct for this endpoint, or has an invalid value. + value: + type: https://docs.api.video/reference/request-invalid-query-parameter + title: A query parameter is invalid. + status: 400 + detail: This field was not expected. + name: from:2024-05-20T09:15:05+02:00 '404': headers: X-RateLimit-Limit: @@ -12352,17 +12382,16 @@ paths: schema: type: integer description: The number of seconds left until the current rate limit window resets. - description: Not Found + description: Unrecognized request URL content: application/json: schema: - $ref: '#/components/schemas/not-found' + $ref: '#/components/schemas/unrecognized-request-url' examples: - Endpoint not found: + Unrecognized request URL: value: - type: - title: - name: + type: 'https://docs.api.video/reference/unrecognized-request-url' + title: Unrecognized request URL. status: 404 '429': headers: @@ -12384,224 +12413,81 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. status: 429 security: - apiKey: [] - x-client-action: getLiveStreamsPlays + x-client-action: getMetricsOverTime x-group-parameters: true x-client-paginated: true x-doctave: code-samples: - - language: go - code: | - package main - - import ( - "context" - "fmt" - "os" - "time" - apivideosdk "github.com/apivideo/api.video-go-client" - ) - - func main() { - client := apivideosdk.ClientBuilder("YOUR_API_KEY").Build() - // if you rather like to use the sandbox environment: - // client := apivideosdk.SandboxClientBuilder("YOU_SANDBOX_API_KEY").Build() - req := apivideosdk.AnalyticsApiGetLiveStreamsPlaysRequest{} - - req.From(time.Now()) // string | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - req.Dimension("browser") // string | Use this query parameter to define the dimension that you want analytics for. - `liveStreamId`: Returns analytics based on the public live stream identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - req.To(time.Now()) // string | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - req.Filter("liveStreamId:li3q7HxhApxRF1c8F8r6VeaI") // string | Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. - req.CurrentPage(int32(2)) // int32 | Choose the number of search results to return per page. Minimum value: 1 (default to 1) - req.PageSize(int32(30)) // int32 | Results per page. Allowed values 1-100, default is 25. (default to 25) - - res, err := client.Analytics.GetLiveStreamsPlays(req) - - - if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `Analytics.GetLiveStreamsPlays``: %v\n", err) - } - // response from `GetLiveStreamsPlays`: AnalyticsPlaysResponse - fmt.Fprintf(os.Stdout, "Response from `Analytics.GetLiveStreamsPlays`: %v\n", res) - } - - language: node - code: |- - // First install the "@api.video/nodejs-client" npm package - // Documentation: https://github.com/apivideo/api.video-nodejs-client/blob/main/doc/api/AnalyticsApi.md#getLiveStreamsPlays - - const client = new ApiVideoClient({ apiKey: "YOUR_API_KEY" }); - - const from = "2023-06-01"; // Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - const dimension = "liveStreamId"; // Use this query parameter to define the dimension that you want analytics for. - `liveStreamId`: Returns analytics based on the public live stream identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - const to = "2023-06-10"; // Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - const filter = "liveStreamId:li3q7HxhApxRF1c8F8r6VeaI"; // Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. - const currentPage = 1; // Choose the number of search results to return per page. Minimum value: 1 - const pageSize = 25; // Results per page. Allowed values 1-100, default is 25. - - const liveStreamsPlays = await client.analytics.getLiveStreamsPlays({ - from, dimension, to, filter, currentPage, pageSize - }); - - language: python - code: | - import apivideo - from apivideo.api import analytics_api - from apivideo.model.analytics_plays_response import AnalyticsPlaysResponse - from apivideo.model.not_found import NotFound - from apivideo.model.analytics_plays400_error import AnalyticsPlays400Error - from pprint import pprint - - # Enter a context with an instance of the API client - with apivideo.AuthenticatedApiClient(__API_KEY__) as api_client: - # Create an instance of the API class - api_instance = analytics_api.AnalyticsApi(api_client) - _from = dateutil_parser('2023-06-01').date() # date | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - dimension = "browser" # str | Use this query parameter to define the dimension that you want analytics for. - `liveStreamId`: Returns analytics based on the public live stream identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - to = dateutil_parser('2023-06-10').date() # date | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. (optional) - filter = "liveStreamId:li3q7HxhApxRF1c8F8r6VeaI" # str | Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. (optional) - current_page = 2 # int | Choose the number of search results to return per page. Minimum value: 1 (optional) if omitted the server will use the default value of 1 - page_size = 30 # int | Results per page. Allowed values 1-100, default is 25. (optional) if omitted the server will use the default value of 25 - - # example passing only required values which don't have defaults set - try: - # Get play events for live stream - api_response = api_instance.get_live_streams_plays(_from, dimension) - pprint(api_response) - except apivideo.ApiException as e: - print("Exception when calling AnalyticsApi->get_live_streams_plays: %s\n" % e) - - # example passing only required values which don't have defaults set - # and optional values - try: - # Get play events for live stream - api_response = api_instance.get_live_streams_plays(_from, dimension, to=to, filter=filter, current_page=current_page, page_size=page_size) - pprint(api_response) - except apivideo.ApiException as e: - print("Exception when calling AnalyticsApi->get_live_streams_plays: %s\n" % e) - - language: java - code: | - // Import classes: - import video.api.client.ApiVideoClient; - import video.api.client.api.ApiException; - import video.api.client.api.models.*; - import video.api.client.api.clients.AnalyticsApi; - import java.util.*; - - public class Example { - public static void main(String[] args) { - ApiVideoClient client = new ApiVideoClient("YOUR_API_KEY"); - // if you rather like to use the sandbox environment: - // ApiVideoClient client = new ApiVideoClient("YOUR_SANDBOX_API_KEY", Environment.SANDBOX); - - AnalyticsApi apiInstance = client.analytics(); - - LocalDate from = LocalDate.parse("2023-06-01"); // Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - String dimension = "liveStreamId"; // Use this query parameter to define the dimension that you want analytics for. - `liveStreamId`: Returns analytics based on the public live stream identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - LocalDate to = LocalDate.parse("2023-06-10"); // Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - String filter = "liveStreamId:li3q7HxhApxRF1c8F8r6VeaI"; // Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. - Integer currentPage = 1; // Choose the number of search results to return per page. Minimum value: 1 - Integer pageSize = 25; // Results per page. Allowed values 1-100, default is 25. - - try { - Page result = apiInstance.getLiveStreamsPlays(from, dimension) - .to(to) - .filter(filter) - .currentPage(currentPage) - .pageSize(pageSize) - .execute(); - System.out.println(result); - } catch (ApiException e) { - System.err.println("Exception when calling AnalyticsApi#getLiveStreamsPlays"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getMessage()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } - } - } - - language: csharp - code: | - using System.Diagnostics; - using ApiVideo.Client; - - namespace Example - { - public class getLiveStreamsPlaysExample - { - public static void Main() - { - var basePath = ApiVideoClient.Client.Environment.SANDBOX; - var apiKey = "YOUR_API_KEY"; - - var apiInstance = new ApiVideoClient(apiKey,basePath); - - var from = 2023-06-01; // DateTime | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - var dimension = browser; // string | Use this query parameter to define the dimension that you want analytics for. - `liveStreamId`: Returns analytics based on the public live stream identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - var to = 2023-06-10; // DateTime? | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. (optional) - var filter = liveStreamId:li3q7HxhApxRF1c8F8r6VeaI; // string | Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. (optional) - var currentPage = 2; // int? | Choose the number of search results to return per page. Minimum value: 1 (optional) (default to 1) - var pageSize = 30; // int? | Results per page. Allowed values 1-100, default is 25. (optional) (default to 25) - var apiAnalyticsInstance = apiInstance.Analytics(); - try - { - // Get play events for live stream - AnalyticsPlaysResponse result = apiAnalyticsInstance.getLiveStreamsPlays(from, dimension, to, filter, currentPage, pageSize); - Debug.WriteLine(result); - } - catch (ApiException e) - { - Debug.Print("Exception when calling AnalyticsApi.getLiveStreamsPlays: " + e.Message ); - Debug.Print("Status Code: "+ e.ErrorCode); - Debug.Print(e.StackTrace); - } - } - } - } - - language: php - code: |- - analytics()->getLiveStreamsPlays($from, $dimension, array( - 'to' => new \DateTime('2023-06-10'), // Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - 'filter' => "liveStreamId:li3q7HxhApxRF1c8F8r6VeaI", // Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. - 'currentPage' => 2, // Choose the number of search results to return per page. Minimum value: 1 - 'pageSize' => 30 // Results per page. Allowed values 1-100, default is 25. - )); - - language: swift - code: | - // The following code samples are still beta. For any issue, please report via http://github.com/OpenAPITools/openapi-generator/issues/new - import ApiVideoClient - - let from = Date() // Date | Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in `from`. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. - let dimension = "dimension_example" // String | Use this query parameter to define the dimension that you want analytics for. - `liveStreamId`: Returns analytics based on the public live stream identifiers. - `emittedAt`: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in `from` and `to` is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - `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/). - `deviceType`: Returns analytics based on the type of device used by the viewers during the play event. - `operatingSystem`: Returns analytics based on the operating system used by the viewers during the play event. - `browser`: Returns analytics based on the browser used by the viewers during the play event. - let to = Date() // Date | Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a `to` date, the API returns analytics data starting from the `from` date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the `YYYY-MM-DD` format. (optional) - let filter = "filter_example" // String | Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the `liveStreamId:` prefix when specifying a live stream ID. (optional) - let currentPage = 987 // Int | Choose the number of search results to return per page. Minimum value: 1 (optional) (default to 1) - let pageSize = 987 // Int | Results per page. Allowed values 1-100, default is 25. (optional) (default to 25) - - // Get play events for live stream - AnalyticsAPI.getLiveStreamsPlays(from: from, dimension: dimension, to: to, filter: filter, currentPage: currentPage, pageSize: pageSize) { (response, error) in - guard error == nil else { - print(error) - return - } + - language: java + code: | + FilterBy2 filterBy = new FilterBy2(); + + filterBy.setBrowser(Collections.singletonList("Chrome")); + filterBy.setContinent(Arrays.asList(FilterBy2.ContinentEnum.NA, FilterBy2.ContinentEnum.EU)); + filterBy.setMediaType(FilterBy2.MediaTypeEnum.VIDEO); + filterBy.setTag("test"); + + Page res = apiClient.analytics().getMetricsOverTime("play").filterBy(filterBy).pageSize(30).execute(); + for (AnalyticsMetricsOverTimeResponseData item : res.getItems()) { + System.out.println(item.getEmittedAt() + ": " + item.getMetricValue()); + } + - language: node + code: | + const res = await client.analytics.getMetricsOverTime({ + metric: 'play', + filterBy: { + mediaType: 'video', + browser: ['chrome', 'firefox'], + continent: ['EU', 'AF'], + tag: "test", + }, + }); - if (response) { - dump(response) - } - } + console.log(res); + - language: csharp + code: | + FilterBy2 filterBy = new FilterBy2 + { + continent = new List { "EU", "US" }, + devicetype = new List { "phone" }, + tag = "test" + }; + Page res = apiClient.Analytics() + .getMetricsOverTime("play").From(new DateTime(2024, 7, 1)).FilterBy(filterBy).execute(); + - language: go + code: | + res, err := cl.Analytics.GetMetricsOverTime("play", AnalyticsApiGetMetricsOverTimeRequest{ + filterBy: &FilterBy2{ + Continent: &[]string{"EU", "AA"}, + DeviceType: &[]string{"computer", "phone"}, + Tag: PtrString("tag"), + }, + }) + - language: python + code: | + res = self.api.get_metrics_over_time(metric='play', filter_by=FilterBy2( + device_type=["computer", "phone"], + tag="test", + ), + ) + - language: swift + code: | + AnalyticsAPI.getMetricsOverTime( + metric: .play, + filterBy: FilterBy2( + continent: [.an, .eu], + tag: "test" + ) + ) { analytics, error in + // ... + } /webhooks: get: tags: @@ -12692,7 +12578,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -12939,18 +12825,18 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/attributerequired' + type: 'https://docs.api.video/reference/attribute-required' events: This attribute is required. name: events status: 400 problems: - - type: 'https://docs.api.video/docs/attributerequired' + - type: 'https://docs.api.video/reference/attribute-required' title: This attribute is required. name: events - - type: 'https://docs.api.video/docs/attributerequired' + - type: 'https://docs.api.video/reference/attribute-required' title: This attribute is required. name: url - - type: 'https://docs.api.video/docs/attributeinvalid' + - type: 'https://docs.api.video/reference/invalid-attribute' title: This attribute must be an array. name: events '429': @@ -12973,7 +12859,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -13218,7 +13104,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -13432,7 +13318,7 @@ paths: examples: response: value: - type: 'https://docs.api.video/docs/resourcenot_found' + type: 'https://docs.api.video/reference/resource-not-found' title: The requested resource was not found. name: webhookId status: 404 @@ -13456,7 +13342,7 @@ paths: schema: $ref: '#/components/schemas/too-many-requests' examples: - response: + Too many requests: value: type: 'https://docs.api.video/reference/too-many-requests' title: Too many requests. @@ -14159,47 +14045,7 @@ components: description: 'Whether you will have subtitles or not. True for yes you will have subtitles, false for no you will not have subtitles.' example: false default: false - video-session: - title: VideoSession - type: object - properties: - session: - $ref: '#/components/schemas/video-session-session' - location: - $ref: '#/components/schemas/video-session-location' - referrer: - $ref: '#/components/schemas/video-session-referrer' - device: - $ref: '#/components/schemas/video-session-device' - os: - $ref: '#/components/schemas/video-session-os' - client: - $ref: '#/components/schemas/video-session-client' - example: - session: - sessionId: psEmFwGQUAXR2lFHj5nDOpy - loadedAt: '2019-06-24T11:45:01.109Z' - endedAt: '2019-06-24T11:49:19.243Z' - location: - country: France - city: Paris - referrer: - url: 'https://api.video' - medium: organic - source: 'https://google.com' - searchTerm: video encoding hosting and delivery - device: - type: desktop - vendor: Dell - model: unknown - os: - name: Microsoft Windows - shortname: W10 - version: Windows10 - client: - type: browser - name: Firefox - version: '67.0' + live-stream: title: LiveStream type: object @@ -14218,7 +14064,7 @@ components: example: dw-dew8-q6w9-k67w-1ws8 public: type: boolean - description: 'Whether your video can be viewed by everyone, or requires authentication to see it. A setting of false will require a unique token for each view. Learn more about the Private Video feature [here](https://docs.api.video/delivery-analytics/video-privacy-access-management).' + description: 'Whether your video can be viewed by everyone, or requires authentication to see it. A setting of false will require a unique token for each view. Learn more about the Private Video feature [here](https://docs.api.video/delivery/video-privacy-access-management).' example: true assets: $ref: '#/components/schemas/live-stream-assets' @@ -14231,7 +14077,7 @@ components: description: 'Whether or not you are broadcasting the live video you recorded for others to see. True means you are broadcasting to viewers, false means you are not.' example: true restreams: - description: Returns the list of RTMP restream destinations. + description: Returns the list of restream destinations. type: array items: $ref: '#/components/schemas/restreams-response-object' @@ -14248,22 +14094,6 @@ components: required: - liveStreamId - restreams - live-stream-session: - title: LiveStreamSession - type: object - properties: - session: - $ref: '#/components/schemas/live-stream-session-session' - location: - $ref: '#/components/schemas/live-stream-session-location' - referrer: - $ref: '#/components/schemas/live-stream-session-referrer' - device: - $ref: '#/components/schemas/live-stream-session-device' - os: - $ref: '#/components/schemas/video-session-os' - client: - $ref: '#/components/schemas/live-stream-session-client' player-session-event: title: PlayerSessionEvent type: object @@ -14524,7 +14354,7 @@ components: example: 'https://www.myvideo.url.com/video.mp4 OR vi4k0jvEUuaTdRAEjQ4JfOyl' public: type: boolean - description: 'Default: True. If set to `false` the video will become private. More information on private videos can be found [here](https://docs.api.video/delivery-analytics/video-privacy-access-management)' + description: 'Default: True. If set to `false` the video will become private. More information on private videos can be found [here](https://docs.api.video/delivery/video-privacy-access-management)' example: true default: true panoramic: @@ -14722,13 +14552,13 @@ components: example: My Live Stream Video public: type: boolean - description: 'Whether your video can be viewed by everyone, or requires authentication to see it. A setting of false will require a unique token for each view. Learn more about the Private Video feature [here](https://docs.api.video/delivery-analytics/video-privacy-access-management).' + description: 'Whether your video can be viewed by everyone, or requires authentication to see it. A setting of false will require a unique token for each view. Learn more about the Private Video feature [here](https://docs.api.video/delivery/video-privacy-access-management).' playerId: type: string description: The unique identifier for the player. example: pl4f4ferf5erfr5zed4fsdd restreams: - description: Use this parameter to add, edit, or remove RTMP services where you want to restream a live stream. The list can only contain up to 5 destinations. + description: Use this parameter to add, edit, or remove `RTMPS` or `RTMP` services where you want to restream a live stream. The list can only contain up to 5 destinations. maxItems: 5 type: array items: @@ -14753,13 +14583,13 @@ components: example: My Live Stream Video public: type: boolean - description: 'Whether your video can be viewed by everyone, or requires authentication to see it. A setting of false will require a unique token for each view. Learn more about the Private Video feature [here](https://docs.api.video/delivery-analytics/video-privacy-access-management).' + description: 'Whether your video can be viewed by everyone, or requires authentication to see it. A setting of false will require a unique token for each view. Learn more about the Private Video feature [here](https://docs.api.video/delivery/video-privacy-access-management).' playerId: type: string description: The unique ID for the player associated with a live stream that you want to update. example: pl45KFKdlddgk654dspkze restreams: - description: Use this parameter to add, edit, or remove RTMP services where you want to restream a live stream. The list can only contain up to 5 destinations. This operation updates all restream destinations in the same request. If you do not want to modify an existing restream destination, you need to include it in your request, otherwise it is removed. + description: Use this parameter to add, edit, or remove `RTMPS` or `RTMP` services where you want to restream a live stream. The list can only contain up to 5 destinations. This operation updates all restream destinations in the same request. If you do not want to modify an existing restream destination, you need to include it in your request, otherwise it is removed. maxItems: 5 type: array items: @@ -14776,9 +14606,9 @@ components: name: description: Use this parameter to define a name for the restream destination. type: string - example: My RTMP server + example: My restream server serverUrl: - description: Use this parameter to set the RTMP URL of the restream destination. + description: Use this parameter to set the `RTMPS` or `RTMP` server URL of the restream destination. type: string example: rtmp://my.broadcast.example.com/app streamKey: @@ -14792,9 +14622,9 @@ components: name: description: Returns the name of a restream destination. type: string - example: My RTMP server + example: My restream server serverUrl: - description: Returns the RTMP URL of a restream destination. + description: Returns the server URL of a restream destination. type: string example: rtmp://my.broadcast.example.com/app streamKey: @@ -14938,6 +14768,187 @@ components: name: description: The name of the parameter that caused the error. type: string + analytics-aggregated-metrics-response: + title: Analytics response for aggregated metrics + type: object + properties: + context: + type: object + properties: + metric: + description: Returns the metric you selected. + type: string + enum: + - play + - start + - end + - impression + - impression-time + - watch-time + example: impression + aggregation: + description: Returns the aggregation you selected. + type: string + enum: + - count + - rate + - total + - average + - sum + example: count + timeframe: + description: Returns the starting and ending date-times of the period you want analytics for. + type: object + properties: + from: + description: Returns the starting date-time of the period you want analytics for in ATOM date-time format. + type: string + format: date-time + example: '2024-05-28T11:15:07+00:00' + to: + description: Returns the starting date-time of the period you want analytics for in ATOM date-time format. + type: string + format: date-time + example: '2024-05-29T11:15:07+00:00' + data: + type: number + format: float + example: '356.2' + required: + - context + - data + analytics-metrics-breakdown-response: + title: Analytics response for metrics breakdown by dimension + type: object + properties: + context: + type: object + properties: + metric: + description: Returns the metric you selected. + type: string + enum: + - play + - play-rate + - start + - end + - impression + example: impression + breakdown: + description: Returns the dimension you selected. + type: string + enum: + - media-id + - media-type + - continent + - country + - device-type + - operating-system + - browser + example: country + timeframe: + description: Returns the starting and ending date-times of the period you want analytics for. + type: object + properties: + from: + description: Returns the starting date-time of the period you want analytics for in ATOM date-time format. + type: string + format: date-time + example: '2024-05-28T11:15:07+00:00' + to: + description: Returns the starting date-time of the period you want analytics for in ATOM date-time format. + type: string + format: date-time + example: '2024-05-29T11:15:07+00:00' + data: + description: Returns an array of dimensions and their respective metrics. + type: array + items: + type: object + properties: + dimensionValue: + description: Returns a specific value for the dimension you selected, based on the data. For example if you select `continent` as a dimension, then `dimensionValue` returns values like `EU` or "AZ". + type: string + metricValue: + description: Returns the data for a specific dimension value. + type: number + format: float + pagination: + $ref: '#/components/schemas/pagination' + required: + - context + - data + - pagination + analytics-metrics-over-time-response: + title: Analytics response for metrics over time + type: object + properties: + context: + type: object + properties: + metric: + description: Returns the metric you selected. + type: string + enum: + - play + - play-rate + - start + - end + - impression + example: impression + interval: + description: Returns the interval you selected. + type: string + enum: + - hour + - day + example: day + timeframe: + description: Returns the starting and ending date-times of the period you want analytics for. + type: object + properties: + from: + description: Returns the starting date-time of the period you want analytics for in ATOM date-time format. + type: string + format: date-time + example: '2024-05-28T11:15:07+00:00' + to: + description: Returns the starting date-time of the period you want analytics for in ATOM date-time format. + type: string + format: date-time + example: '2024-05-29T11:15:07+00:00' + data: + description: Returns an array of metrics and the timestamps . + type: array + items: + type: object + properties: + emittedAt: + description: Returns the timestamp of the event that belongs to a specific metric in ATOM date-time format. For example, if you set `play` with an `hour` interval in your request, then `emittedAt` returns the hourly timestamps of every play event within the timeframe you defined. + type: string + metricValue: + description: Returns the data for a specific metric value. + type: number + format: float + pagination: + $ref: '#/components/schemas/pagination' + required: + - context + - data + - pagination + unrecognized-request-url: + title: Unrecognized request URL + type: object + properties: + type: + description: A link to the error documentation. + type: string + title: + description: A description of the error that occurred. + type: string + status: + description: The HTTP status code. + type: integer webhooks-list-response: title: Webhooks type: object @@ -15100,115 +15111,6 @@ components: format: uri example: 'https://cdn.api.video/vod/vi4k0jvEUuaTdRAEjQ4Jfrgz/token/8fd70443-d9f0-45d2-b01c-12c8cfc707c9/mp4/source.mp4' description: Collection of details about the video object that you can use to work with the video object. - video-session-session: - title: VideoSessionSession - type: object - properties: - sessionId: - type: string - description: The unique identifier for the session that you can use to track what happens during it. - example: psEmFwGQUAXR2lFHj5nDOpy - loadedAt: - type: string - description: 'When the video session started, presented in ISO-8601 format.' - format: date-time - example: '2019-06-24T11:45:01.109Z' - endedAt: - type: string - description: 'When the video session ended, presented in ISO-8601 format.' - format: date-time - example: '2019-06-24T12:45:01.109Z' - metadata: - type: array - description: 'A list of key value pairs that you use to provide metadata for your video. These pairs can be made dynamic, allowing you to segment your audience. You can also just use the pairs as another way to tag and categorize your videos.' - example: '[{"key": "Author", "value": "John Doe"}]' - items: - $ref: '#/components/schemas/metadata' - video-session-location: - title: VideoSessionLocation - type: object - properties: - country: - type: string - description: The country of the viewer. - example: France - city: - type: string - description: The city of the viewer. - example: Paris - nullable: true - description: The location of the viewer. - video-session-referrer: - title: VideoSessionReferrer - type: object - properties: - url: - type: string - description: The link the viewer used to reach the video session. - example: 'https://api.video' - nullable: true - medium: - type: string - description: 'How they arrived at the site, for example organic or paid. Organic meaning they found it themselves and paid meaning they followed a link from an advertisement.' - example: organic - source: - type: string - description: The source the referrer came from to the video session. For example if they searched through google to find the stream. - example: 'https://google.com' - searchTerm: - type: string - description: The search term they typed to arrive at the video session. - video-session-device: - title: VideoSessionDevice - type: object - properties: - type: - type: string - description: 'What the type is like desktop, laptop, mobile.' - example: desktop - vendor: - type: string - description: 'If known, what the brand of the device is, like Apple, Dell, etc.' - example: Dell - model: - type: string - description: 'The specific model of the device, if known.' - example: unknown - description: What type of device the user is on when in the video session. - video-session-os: - title: VideoSessionOs - type: object - properties: - name: - type: string - description: The name of the operating system. - example: Microsoft Windows - shortname: - type: string - description: 'The nickname for the operating system, often representing the version.' - example: W10 - version: - type: string - description: The version of the operating system. - example: Windows 10 - description: The operating system the viewer is on. - video-session-client: - title: VideoSessionClient - type: object - properties: - name: - type: string - description: The name of the browser used to view the video session. - example: Firefox - version: - type: string - description: The version of the browser used to view the video session. - example: '67.0' - type: - type: string - description: The type of client used to view the video session. - example: browser - description: What kind of browser the viewer is using for the video session. live-stream-assets: title: LiveStreamAssets type: object @@ -15232,90 +15134,6 @@ components: description: A link to the thumbnail for your video. format: uri example: 'https://live.api.video/li400mYKSgQ6xs7taUeSaEKr/thumbnail.jpg' - live-stream-session-session: - title: LiveStreamSessionSession - type: object - properties: - sessionId: - type: string - description: A unique identifier for your session. You can use this to track what happens during a specific session. - loadedAt: - type: string - description: 'When the session started, with the date and time presented in ISO-8601 format.' - format: date-time - example: '2019-06-24T11:45:01.109Z' - endedAt: - type: string - description: 'When the session ended, with the date and time presented in ISO-8601 format.' - format: date-time - example: '2019-06-24T12:45:01.109Z' - live-stream-session-location: - title: LiveStreamSessionLocation - type: object - properties: - country: - type: string - description: The country of the viewer of the live stream. - example: France - city: - type: string - description: The city of the viewer of the live stream. - example: Paris - description: The location of the viewer of the live stream. - live-stream-session-referrer: - title: LiveStreamSessionReferrer - type: object - properties: - url: - type: string - description: The website the viewer of the live stream was referred to in order to view the live stream. - example: 'https://api.video' - medium: - type: string - description: 'The type of search that brought the viewer to the live stream. Organic would be they found it on their own, paid would be they found it via an advertisement.' - example: organic - source: - type: string - description: Where the viewer came from to see the live stream (usually where they searched from). - example: 'https://google.com' - searchTerm: - type: string - description: What term they searched for that led them to the live stream. - example: video stream - live-stream-session-device: - title: LiveStreamSessionDevice - type: object - properties: - type: - type: string - description: 'What the type is like desktop, laptop, mobile.' - example: desktop - vendor: - type: string - description: 'If known, what the brand of the device is, like Apple, Dell, etc.' - example: Dell - model: - type: string - description: 'The specific model of the device, if known.' - example: unknown - description: What type of device the user is on when in the live stream session. - live-stream-session-client: - title: LiveStreamSessionClient - type: object - properties: - name: - type: string - description: The name of the browser used to view the live stream session. - example: Firefox - version: - type: string - description: The version of the browser used to view the live stream session. - example: '67.0' - type: - type: string - description: The type of client used to view the live stream session. - example: browser - description: What kind of browser the viewer is using for the live stream session. video-status-ingest: title: VideoStatusIngest type: object @@ -15416,6 +15234,79 @@ components: metadata: $ref: '#/components/schemas/video-status-encoding-metadata' parameters: + filterBy: + name: filterBy + description: | + 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` 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. 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: 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: + x-is-deep-object: true + type: object + properties: + mediaId: + type: array + items: + type: string + description: Returns analytics based on the unique identifiers of a video or a live stream. + example: ['vi4blUQJFrYWbaG44NChkH27'] + mediaType: + type: string + enum: [video, live-stream] + example: video + continent: + 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`. + 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: array + items: + type: string + example: ['FR'] + deviceType: + 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: 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 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). + example: Cool videos current-page: name: currentPage in: query diff --git a/templates/csharp/ApiClient.mustache b/templates/csharp/ApiClient.mustache index 4fa83096..2915ac3d 100644 --- a/templates/csharp/ApiClient.mustache +++ b/templates/csharp/ApiClient.mustache @@ -19,6 +19,7 @@ using RestSharp.Portable.HttpClient; {{^netStandard}} using RestSharp; {{/netStandard}} +using ApiVideo.Model; namespace {{packageName}}.Client { @@ -401,9 +402,9 @@ namespace {{packageName}}.Client public string ParameterToString(object obj) { if (obj is DateTime dateTime) - return dateTime.ToString(); + return dateTime.ToString("yyyy-MM-ddTHH:mm:ssKT"); else if (obj is DateTimeOffset dateTimeOffset) - return dateTimeOffset.ToString(); + return dateTimeOffset.ToString("yyyy-MM-ddTHH:mm:ssKT"); else if (obj is bool isTrue) return isTrue ? "true" : "false"; else if (obj is IList list) @@ -680,6 +681,25 @@ namespace {{packageName}}.Client parameters.Add(new KeyValuePair(name + "[" + entry.Key + "]", ParameterToString(entry.Value))); } } + else if (value is DeepObject) { + var dict2 = JsonConvert.DeserializeObject>(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(name + "[" + entry.Key + "][]", ParameterToString(item))); + } + } else { + parameters.Add(new KeyValuePair(name + "[" + entry.Key + "]", ParameterToString(entry.Value))); + } + } + } + } else { parameters.Add(new KeyValuePair(name, ParameterToString(value))); diff --git a/templates/csharp/api.mustache b/templates/csharp/api.mustache index 03b10d10..fba3aaf8 100644 --- a/templates/csharp/api.mustache +++ b/templates/csharp/api.mustache @@ -52,11 +52,11 @@ namespace {{packageName}}.{{apiPackage}} {{#isDeprecated}} [Obsolete] {{/isDeprecated}} - public {{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default{{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}},IUploadProgressListener progressListener = null{{/vendorExtensions.x-client-chunk-upload}}, CancellationToken cancellationToken = default) + public {{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task>{{/returnType}} {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default{{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}},IUploadProgressListener progressListener = null{{/vendorExtensions.x-client-chunk-upload}}, CancellationToken cancellationToken = default) { {{#returnType}}Task> localVarResponse = {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}},progressListener{{/vendorExtensions.x-client-chunk-upload}}, cancellationToken); return localVarResponse.ContinueWith((Task> task) => task.Result.Data ); - {{/returnType}}{{^returnType}}{{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, cancellationToken);{{/returnType}} + {{/returnType}}{{^returnType}}return {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, cancellationToken);{{/returnType}} } /// diff --git a/templates/csharp/model.mustache b/templates/csharp/model.mustache index 9c8b5b19..40cc2096 100644 --- a/templates/csharp/model.mustache +++ b/templates/csharp/model.mustache @@ -13,7 +13,7 @@ namespace {{packageName}}.Model { /// {{description}} /// [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}} /// /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}} diff --git a/templates/csharp/post-generate.sh b/templates/csharp/post-generate.sh index afff2132..0ff3beed 100755 --- a/templates/csharp/post-generate.sh +++ b/templates/csharp/post-generate.sh @@ -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 diff --git a/templates/csharp/statics/src/Model/DeepObject.cs b/templates/csharp/statics/src/Model/DeepObject.cs new file mode 100644 index 00000000..651fc627 --- /dev/null +++ b/templates/csharp/statics/src/Model/DeepObject.cs @@ -0,0 +1,4 @@ +namespace ApiVideo.Model { + public interface DeepObject { + } +} \ No newline at end of file diff --git a/templates/go/api.mustache b/templates/go/api.mustache index 3af307d0..3d638606 100644 --- a/templates/go/api.mustache +++ b/templates/go/api.mustache @@ -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}}")) diff --git a/templates/go/client.mustache b/templates/go/client.mustache index 8443cf54..6e5bfc85 100644 --- a/templates/go/client.mustache +++ b/templates/go/client.mustache @@ -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") + } +} \ No newline at end of file diff --git a/templates/go/statics/api_analytics_test.go b/templates/go/statics/api_analytics_test.go deleted file mode 100644 index f39216a6..00000000 --- a/templates/go/statics/api_analytics_test.go +++ /dev/null @@ -1,254 +0,0 @@ -package apivideosdk - -import ( - "fmt" - "net/http" - "reflect" - "testing" -) - -var liveStreamsPlaysResponses = []string{ - `{ - "data" : [ { - "value" : "li3q7HxhApxRF1c8F8r6VeaI", - "plays" : 100 - }, { - "value" : "li3q7HxhApxRF1c8F8r6VeaB", - "plays" : 10 - }, { - "value" : "li3q7HxhApxRF1c8F8r6VeaD", - "plays" : 1 - } - ] - }`, - `{ - "data" : [ { - "value" : "france", - "plays" : 100 - }, { - "value" : "united states", - "plays" : 10 - }, { - "value" : "spain", - "plays" : 1 - } - ] - }`, - `{ - "data" : [ { - "value" : "2023-06-10T10:00:00.000Z", - "plays" : 100 - }, { - "value" : "2023-06-10T11:00:00.000Z", - "plays" : 10 - }, { - "value" : "2023-06-10T12:00:00.000Z", - "plays" : 1 - } - ] - }`, -} - -var liveStreamsPlaysStructs = []AnalyticsPlaysResponse{ - { - Data: []AnalyticsData{ - {Value: "li3q7HxhApxRF1c8F8r6VeaI", Plays: 100}, - {Value: "li3q7HxhApxRF1c8F8r6VeaB", Plays: 10}, - {Value: "li3q7HxhApxRF1c8F8r6VeaD", Plays: 1}, - }, - }, - { - Data: []AnalyticsData{ - {Value: "france", Plays: 100}, - {Value: "united states", Plays: 10}, - {Value: "spain", Plays: 1}, - }, - }, - { - Data: []AnalyticsData{ - {Value: "2023-06-10T10:00:00.000Z", Plays: 100}, - {Value: "2023-06-10T11:00:00.000Z", Plays: 10}, - {Value: "2023-06-10T12:00:00.000Z", Plays: 1}, - }, - }, -} - -func TestAnalytics_GetLiveStreamsPlaysByLiveStreamId(t *testing.T) { - setup() - defer teardown() - mux.HandleFunc("/analytics/live-streams/plays", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, liveStreamsPlaysResponses[0]) - }) - - plays, err := client.Analytics.GetLiveStreamsPlays(AnalyticsApiGetLiveStreamsPlaysRequest{}.From("2023-04-01").Dimension("liveStreamId")) - if err != nil { - t.Errorf("Analytics.GetLiveStreamsPlays error: %v", err) - } - - expected := &liveStreamsPlaysStructs[0] - if !reflect.DeepEqual(plays, expected) { - t.Errorf("Analytics.GetLiveStreamsPlays\n got=%#v\nwant=%#v", plays, expected) - } -} - -func TestAnalytics_GetLiveStreamsPlaysByCountry(t *testing.T) { - setup() - defer teardown() - mux.HandleFunc("/analytics/live-streams/plays", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, liveStreamsPlaysResponses[1]) - }) - - plays, err := client.Analytics.GetLiveStreamsPlays(AnalyticsApiGetLiveStreamsPlaysRequest{}.From("2023-04-01").Dimension("country")) - if err != nil { - t.Errorf("Analytics.GetLiveStreamsPlays error: %v", err) - } - - expected := &liveStreamsPlaysStructs[1] - if !reflect.DeepEqual(plays, expected) { - t.Errorf("Analytics.GetLiveStreamsPlays\n got=%#v\nwant=%#v", plays, expected) - } -} - -func TestAnalytics_GetLiveStreamsPlaysByEmittedAt(t *testing.T) { - setup() - defer teardown() - mux.HandleFunc("/analytics/live-streams/plays", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, liveStreamsPlaysResponses[2]) - }) - - plays, err := client.Analytics.GetLiveStreamsPlays(AnalyticsApiGetLiveStreamsPlaysRequest{}.From("2023-04-01").Dimension("emittedAt")) - if err != nil { - t.Errorf("Analytics.GetLiveStreamsPlays error: %v", err) - } - - expected := &liveStreamsPlaysStructs[2] - if !reflect.DeepEqual(plays, expected) { - t.Errorf("Analytics.GetLiveStreamsPlays\n got=%#v\nwant=%#v", plays, expected) - } -} - -var videosPlaysResponses = []string{ - `{ - "data" : [ { - "value" : "vi3q7HxhApxRF1c8F8r6VeaI", - "plays" : 100 - }, { - "value" : "vi3q7HxhApxRF1c8F8r6VeaF", - "plays" : 10 - }, { - "value" : "vi3q7HxhApxRF1c8F8r6VeaH", - "plays" : 1 - } - ] - }`, - `{ - "data" : [ { - "value" : "france", - "plays" : 100 - }, { - "value" : "united states", - "plays" : 10 - }, { - "value" : "spain", - "plays" : 1 - } - ] - }`, - `{ - "data" : [ { - "value" : "2023-06-10T10:00:00.000Z", - "plays" : 100 - }, { - "value" : "2023-06-10T11:00:00.000Z", - "plays" : 10 - }, { - "value" : "2023-06-10T12:00:00.000Z", - "plays" : 1 - } - ] - }`, -} - -var videosPlaysStructs = []AnalyticsPlaysResponse{ - { - Data: []AnalyticsData{ - {Value: "vi3q7HxhApxRF1c8F8r6VeaI", Plays: 100}, - {Value: "vi3q7HxhApxRF1c8F8r6VeaF", Plays: 10}, - {Value: "vi3q7HxhApxRF1c8F8r6VeaH", Plays: 1}, - }, - }, - { - Data: []AnalyticsData{ - {Value: "france", Plays: 100}, - {Value: "united states", Plays: 10}, - {Value: "spain", Plays: 1}, - }, - }, - { - Data: []AnalyticsData{ - {Value: "2023-06-10T10:00:00.000Z", Plays: 100}, - {Value: "2023-06-10T11:00:00.000Z", Plays: 10}, - {Value: "2023-06-10T12:00:00.000Z", Plays: 1}, - }, - }, -} - -func TestAnalytics_GetVideosPlaysByVideoId(t *testing.T) { - setup() - defer teardown() - mux.HandleFunc("/analytics/videos/plays", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, videosPlaysResponses[0]) - }) - - plays, err := client.Analytics.GetVideosPlays(AnalyticsApiGetVideosPlaysRequest{}.From("2023-04-01").Dimension("videoId")) - if err != nil { - t.Errorf("Analytics.GetVideosPlays error: %v", err) - } - - expected := &videosPlaysStructs[0] - if !reflect.DeepEqual(plays, expected) { - t.Errorf("Analytics.GetVideosPlays\n got=%#v\nwant=%#v", plays, expected) - } -} - -func TestAnalytics_GetVideosPlaysByCountry(t *testing.T) { - setup() - defer teardown() - mux.HandleFunc("/analytics/videos/plays", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, videosPlaysResponses[1]) - }) - - plays, err := client.Analytics.GetVideosPlays(AnalyticsApiGetVideosPlaysRequest{}.From("2023-04-01").Dimension("country")) - if err != nil { - t.Errorf("Analytics.GetVideosPlays error: %v", err) - } - - expected := &videosPlaysStructs[1] - if !reflect.DeepEqual(plays, expected) { - t.Errorf("Analytics.GetVideosPlays\n got=%#v\nwant=%#v", plays, expected) - } -} - -func TestAnalytics_GetVideosPlaysByEmittedAt(t *testing.T) { - setup() - defer teardown() - mux.HandleFunc("/analytics/videos/plays", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, videosPlaysResponses[2]) - }) - - plays, err := client.Analytics.GetVideosPlays(AnalyticsApiGetVideosPlaysRequest{}.From("2023-04-01").Dimension("emittedAt")) - if err != nil { - t.Errorf("Analytics.GetVideosPlays error: %v", err) - } - - expected := &videosPlaysStructs[2] - if !reflect.DeepEqual(plays, expected) { - t.Errorf("Analytics.GetVideosPlays\n got=%#v\nwant=%#v", plays, expected) - } -} diff --git a/templates/java/libraries/okhttp-gson/ApiClient.mustache b/templates/java/libraries/okhttp-gson/ApiClient.mustache index bace44cf..51381e26 100644 --- a/templates/java/libraries/okhttp-gson/ApiClient.mustache +++ b/templates/java/libraries/okhttp-gson/ApiClient.mustache @@ -3,6 +3,7 @@ package {{invokerPackage}}; import {{invokerPackage}}.auth.ApiVideoAuthInterceptor; +import {{invokerPackage}}.models.DeepObject; {{#dynamicOperations}} import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; @@ -28,6 +29,7 @@ import org.joda.time.format.DateTimeFormatter; import org.threeten.bp.LocalDate; import org.threeten.bp.OffsetDateTime; import org.threeten.bp.format.DateTimeFormatter; +import org.threeten.bp.ZoneId; {{/threetenbp}} {{#hasOAuthMethods}} import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; @@ -461,9 +463,20 @@ public class ApiClient { if (param == null) { return ""; } else if (param instanceof Date {{#joda}}|| param instanceof DateTime || param instanceof LocalDate{{/joda}}{{#jsr310}}|| param instanceof OffsetDateTime || param instanceof LocalDate{{/jsr310}}) { - //Serialize to json string and remove the " enclosing characters - String jsonStr = json.serialize(param); - return jsonStr.substring(1, jsonStr.length() - 1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); + + String formattedDate; + {{^threetenbp}}{{#java8}}if (param instanceof Date) { + OffsetDateTime odt = ((Date) param).toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime(); + formattedDate = odt.format(formatter); + } else {{/java8}}{{/threetenbp}}if (param instanceof OffsetDateTime) { + formattedDate = ((OffsetDateTime) param).format(formatter); + } else { + OffsetDateTime odt = ((LocalDate) param).atStartOfDay(ZoneId.systemDefault()).toOffsetDateTime(); + formattedDate = odt.format(formatter); + } + + return formattedDate; } else if (param instanceof Collection) { StringBuilder b = new StringBuilder(); for (Object o : (Collection) param) { @@ -503,6 +516,25 @@ public class ApiClient { return params; } + + if (value instanceof DeepObject) { + Map map = json.deserialize(json.serialize(value), Map.class); + map.forEach((k, v) -> { + if (k != null) { + if(v instanceof List) { + List list = (List) v; + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + params.add(new Pair(name + "[" + k + "][" + i + "]", item == null ? null : item.toString())); + } + } else { + params.add(new Pair(name + "[" + k + "]", v == null ? null : v.toString())); + } + } + }); + return params; + } + params.add(new Pair(name, parameterToString(value))); return params; } diff --git a/templates/java/statics/android-uploader/src/main/java/video/api/client/api/models/DeepObject.java b/templates/java/statics/android-uploader/src/main/java/video/api/client/api/models/DeepObject.java new file mode 100644 index 00000000..eed8c2ce --- /dev/null +++ b/templates/java/statics/android-uploader/src/main/java/video/api/client/api/models/DeepObject.java @@ -0,0 +1,4 @@ +package video.api.client.api.models; + +public interface DeepObject { +} diff --git a/templates/java/statics/android/src/main/java/video/api/client/api/models/DeepObject.java b/templates/java/statics/android/src/main/java/video/api/client/api/models/DeepObject.java new file mode 100644 index 00000000..eed8c2ce --- /dev/null +++ b/templates/java/statics/android/src/main/java/video/api/client/api/models/DeepObject.java @@ -0,0 +1,4 @@ +package video.api.client.api.models; + +public interface DeepObject { +} diff --git a/templates/java/statics/android/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java b/templates/java/statics/android/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java index e2300753..fbf28848 100644 --- a/templates/java/statics/android/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java +++ b/templates/java/statics/android/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java @@ -33,189 +33,7 @@ @DisplayName("AnalyticsApi") public class AnalyticsApiTest extends AbstractApiTest { - private final AnalyticsApi api = new AnalyticsApi(apiClientMock.getHttpClient()); + private final AnalyticsApi api = apiClientMock.analytics(); - @Nested - @DisplayName("getLiveStreamsPlays") - class getLiveStreamsPlays { - private static final String PAYLOADS_PATH = "/payloads/analytics/getLiveStreamsPlays/"; - - @Test - @DisplayName("required parameters") - public void requiredParametersTest() { - answerOnAnyRequest(201, "{}"); - - assertThatThrownBy(() -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), null).execute()) - .isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'dimension' when calling getLiveStreamsPlays"); - assertThatThrownBy(() -> api.getLiveStreamsPlays(null, "liveStreamId").execute()) - .isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'from' when calling getLiveStreamsPlays"); - - assertThatNoException() - .isThrownBy(() -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "liveStreamId").execute()); - } - - @Test - @DisplayName("200 response by liveStreamId") - public void responseWithStatusByLiveStreamId200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-0.json")); - - Page res = api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "liveStreamId").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("li3q7HxhApxRF1c8F8r6VeaI"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("li3q7HxhApxRF1c8F8r6VeaB"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("li3q7HxhApxRF1c8F8r6VeaD"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } - - @Test - @DisplayName("200 response by country") - public void responseWithStatusByCountry200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-1.json")); - - Page res = api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "country").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("france"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("united states"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("spain"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } - - @Test - @DisplayName("200 response by emittedAt") - public void responseWithStatusByEmittedAt200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-2.json")); - - Page res = api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "emittedAt").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("2023-06-10T10:00:00.000Z"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("2023-06-10T11:00:00.000Z"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("2023-06-10T12:00:00.000Z"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } - - @Test - @DisplayName("400 response") - public void responseWithStatus400ForUnknownDimensionTest() throws ApiException { - answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-2.json")); - - ApiException e = assertThrows(ApiException.class, - () -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "unknownDimension").execute()); - Truth.assertThat(e.getCode()).isEqualTo(400); - Truth.assertThat(e).hasMessageThat().contains("A query parameter is invalid."); - } - - @Test - @DisplayName("404 response") - public void responseWithStatus404Test() throws ApiException { - answerOnAnyRequest(404, ""); - - ApiException e = assertThrows(ApiException.class, - () -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "country").execute()); - Truth.assertThat(e.getCode()).isEqualTo(404); - Truth.assertThat(e).hasMessageThat().contains(""); - } - } - - @Nested - @DisplayName("getVideosPlays") - class getVideosPlays { - private static final String PAYLOADS_PATH = "/payloads/analytics/getVideosPlays/"; - - @Test - @DisplayName("required parameters") - public void requiredParametersTest() { - answerOnAnyRequest(201, "{}"); - - assertThatThrownBy(() -> api.getVideosPlays(LocalDate.parse("2023-04-01"), null).execute()) - .isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'dimension' when calling getVideosPlays"); - assertThatThrownBy(() -> api.getVideosPlays(null, "videoId").execute()).isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'from' when calling getVideosPlays"); - - assertThatNoException() - .isThrownBy(() -> api.getVideosPlays(LocalDate.parse("2023-04-01"), "videoId").execute()); - } - - @Test - @DisplayName("200 response by videoId") - public void responseWithStatusByVideoId200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-0.json")); - - Page res = api.getVideosPlays(LocalDate.parse("2023-04-01"), "videoId").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("vi3q7HxhApxRF1c8F8r6VeaI"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("vi3q7HxhApxRF1c8F8r6VeaF"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("vi3q7HxhApxRF1c8F8r6VeaH"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } - - @Test - @DisplayName("200 response by country") - public void responseWithStatusByCountry200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-1.json")); - - Page res = api.getVideosPlays(LocalDate.parse("2023-04-01"), "country").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("france"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("united states"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("spain"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } - - @Test - @DisplayName("200 response by emittedAt") - public void responseWithStatusByEmittedAt200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-2.json")); - - Page res = api.getVideosPlays(LocalDate.parse("2023-04-01"), "emittedAt").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("2023-06-10T10:00:00.000Z"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("2023-06-10T11:00:00.000Z"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("2023-06-10T12:00:00.000Z"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } - - @Test - @DisplayName("400 response") - public void responseWithStatus400ForUnknownDimensionTest() throws ApiException { - answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-2.json")); - - ApiException e = assertThrows(ApiException.class, - () -> api.getVideosPlays(LocalDate.parse("2023-04-01"), "unknownDimension").execute()); - Truth.assertThat(e.getCode()).isEqualTo(400); - Truth.assertThat(e).hasMessageThat().contains("A query parameter is invalid."); - } - - @Test - @DisplayName("404 response") - public void responseWithStatus404Test() throws ApiException { - answerOnAnyRequest(404, ""); - - ApiException e = assertThrows(ApiException.class, - () -> api.getVideosPlays(LocalDate.parse("2023-04-01"), "country").execute()); - Truth.assertThat(e.getCode()).isEqualTo(404); - Truth.assertThat(e).hasMessageThat().contains(""); - } - } } diff --git a/templates/java/statics/android/src/test/java/video/api/client/api/clients/WebhooksApiTest.java b/templates/java/statics/android/src/test/java/video/api/client/api/clients/WebhooksApiTest.java index be82da8e..2fe5074d 100644 --- a/templates/java/statics/android/src/test/java/video/api/client/api/clients/WebhooksApiTest.java +++ b/templates/java/statics/android/src/test/java/video/api/client/api/clients/WebhooksApiTest.java @@ -176,11 +176,11 @@ public void responseWithStatus400Test() throws ApiException { ApiException e = assertThrows(ApiException.class, () -> api.create(new WebhooksCreationPayload().url("url"))); assertThat(e.getCode()).isEqualTo(400); assertThat(e.getProblems()).containsExactlyElementsIn(Arrays.asList( - new ApiException.ApiProblem("https://docs.api.video/docs/attributerequired", + new ApiException.ApiProblem("https://docs.api.video/reference/attribute-required", "This attribute is required.", "events"), - new ApiException.ApiProblem("https://docs.api.video/docs/attributerequired", + new ApiException.ApiProblem("https://docs.api.video/reference/attribute-required", "This attribute is required.", "url"), - new ApiException.ApiProblem("https://docs.api.video/docs/attributeinvalid", + new ApiException.ApiProblem("https://docs.api.video/reference/invalid-attribute", "This attribute must be an array.", "events"))) .inOrder(); assertThat(e).hasMessageThat().contains("This attribute is required."); diff --git a/templates/java/statics/java/src/main/java/video/api/client/api/models/DeepObject.java b/templates/java/statics/java/src/main/java/video/api/client/api/models/DeepObject.java new file mode 100644 index 00000000..eed8c2ce --- /dev/null +++ b/templates/java/statics/java/src/main/java/video/api/client/api/models/DeepObject.java @@ -0,0 +1,4 @@ +package video.api.client.api.models; + +public interface DeepObject { +} diff --git a/templates/java/statics/java/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java b/templates/java/statics/java/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java index 6baf8b62..3117fd7b 100644 --- a/templates/java/statics/java/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java +++ b/templates/java/statics/java/src/test/java/video/api/client/api/clients/AnalyticsApiTest.java @@ -11,22 +11,22 @@ package video.api.client.api.clients; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertThrows; +import video.api.client.api.ApiException; +import video.api.client.api.models.*; -import com.google.common.truth.Truth; +import java.time.OffsetDateTime; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; - -import java.time.LocalDate; - -import video.api.client.api.ApiException; -import video.api.client.api.models.AnalyticsData; -import video.api.client.api.models.Page; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * API tests for AnalyticsApi @@ -34,188 +34,250 @@ @DisplayName("AnalyticsApi") public class AnalyticsApiTest extends AbstractApiTest { - private final AnalyticsApi api = new AnalyticsApi(apiClientMock.getHttpClient()); + private final AnalyticsApi api = apiClientMock.analytics(); @Nested - @DisplayName("getLiveStreamsPlays") - class getLiveStreamsPlays { - private static final String PAYLOADS_PATH = "/payloads/analytics/getLiveStreamsPlays/"; + @DisplayName("getAggregatedMetrics") + class getAggregatedMetrics { + private static final String PAYLOADS_PATH = "/payloads/analytics/getAggregatedMetrics/"; @Test @DisplayName("required parameters") public void requiredParametersTest() { answerOnAnyRequest(201, "{}"); - assertThatThrownBy(() -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), null).execute()) - .isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'dimension' when calling getLiveStreamsPlays"); - assertThatThrownBy(() -> api.getLiveStreamsPlays(null, "liveStreamId").execute()) - .isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'from' when calling getLiveStreamsPlays"); - - assertThatNoException() - .isThrownBy(() -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "liveStreamId").execute()); + assertThatNoException().isThrownBy(() -> api.getAggregatedMetrics("play", "count").execute()); + // String metric, String aggregation, OffsetDateTime from, OffsetDateTime to, String filterBy, Integer + // currentPage, Integer pageSize } @Test - @DisplayName("200 response by liveStreamId") - public void responseWithStatusByLiveStreamId200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-0.json")); - - Page res = api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "liveStreamId").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("li3q7HxhApxRF1c8F8r6VeaI"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("li3q7HxhApxRF1c8F8r6VeaB"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("li3q7HxhApxRF1c8F8r6VeaD"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } + @DisplayName("200 response") + public void responseWithStatus200Test() throws ApiException { + answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200.json")); - @Test - @DisplayName("200 response by country") - public void responseWithStatusByCountry200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-1.json")); - - Page res = api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "country").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("france"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("united states"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("spain"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); - } + AnalyticsAggregatedMetricsResponse res = api.getAggregatedMetrics("play", "count").execute(); - @Test - @DisplayName("200 response by emittedAt") - public void responseWithStatusByEmittedAt200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-2.json")); - - Page res = api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "emittedAt").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("2023-06-10T10:00:00.000Z"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("2023-06-10T11:00:00.000Z"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("2023-06-10T12:00:00.000Z"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); + /* + * sample response: { "context" : { "metric" : "impression", "aggregation" : "count", "timeframe" : { "from" + * : "2024-05-28T11:15:07+00:00", "to" : "2024-05-29T11:15:07+00:00" } }, "data" : 346.5 } + */ } @Test @DisplayName("400 response") - public void responseWithStatus400ForUnknownDimensionTest() throws ApiException { - answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-2.json")); + public void responseWithStatus400Test() throws ApiException { + answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-0.json")); - ApiException e = assertThrows(ApiException.class, - () -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "unknownDimension").execute()); - Truth.assertThat(e.getCode()).isEqualTo(400); - Truth.assertThat(e).hasMessageThat().contains("A query parameter is invalid."); + assertThatThrownBy(() -> api.getAggregatedMetrics("play", "count").execute()) + .isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(400)) + .hasMessage("An attribute is invalid."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/request-invalid-query-parameter", "title" : + * "A query parameter is invalid.", "status" : 400, "detail" : "This field was not expected.", "name" : + * "from:2024-05-20T09:15:05+02:00" } + */ } @Test @DisplayName("404 response") public void responseWithStatus404Test() throws ApiException { - answerOnAnyRequest(404, ""); + answerOnAnyRequest(404, readResourceFile(PAYLOADS_PATH + "responses/404.json")); + + assertThatThrownBy(() -> api.getAggregatedMetrics("play", "count").execute()) + .isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(404)) + .hasMessage("Unrecognized request URL."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/unrecognized-request-url", "title" : + * "Unrecognized request URL.", "status" : 404 } + */ + } + + @Test + @DisplayName("429 response") + public void responseWithStatus429Test() throws ApiException { + answerOnAnyRequest(429, readResourceFile(PAYLOADS_PATH + "responses/429.json")); - ApiException e = assertThrows(ApiException.class, - () -> api.getLiveStreamsPlays(LocalDate.parse("2023-04-01"), "country").execute()); - Truth.assertThat(e.getCode()).isEqualTo(404); - Truth.assertThat(e).hasMessageThat().contains(""); + assertThatThrownBy(() -> api.getAggregatedMetrics("play", "count").execute()) + .isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(429)) + .hasMessage("Too many requests."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/too-many-requests", "title" : + * "Too many requests.", "status" : 429 } + */ } } @Nested - @DisplayName("getVideosPlays") - class getVideosPlays { - private static final String PAYLOADS_PATH = "/payloads/analytics/getVideosPlays/"; + @DisplayName("getMetricsBreakdown") + class getMetricsBreakdown { + private static final String PAYLOADS_PATH = "/payloads/analytics/getMetricsBreakdown/"; @Test @DisplayName("required parameters") public void requiredParametersTest() { answerOnAnyRequest(201, "{}"); - assertThatThrownBy(() -> api.getVideosPlays(LocalDate.parse("2023-04-01"), null).execute()) + assertThatNoException().isThrownBy(() -> api.getMetricsBreakdown("play", "media-id").execute()); + // String metric, String breakdown, OffsetDateTime from, OffsetDateTime to, String filterBy, Integer + // currentPage, Integer pageSize + } + + @Test + @DisplayName("200 response") + public void responseWithStatus200Test() throws ApiException { + answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200.json")); + + Page res = api.getMetricsBreakdown("play", "media-id").execute(); + + /* + * sample response: { "context" : { "metric" : "play", "breakdown" : "country", "timeframe" : { "from" : + * "2024-04-28T07:15:05+00:00", "to" : "2024-05-29T11:25:37+00:00" } }, "data" : [ { "metricValue" : 7, + * "dimensionValue" : "FR" } ], "pagination" : { "currentPage" : 1, "currentPageItems" : 1, "pageSize" : 25, + * "pagesTotal" : 1, "itemsTotal" : 1, "links" : [ { "rel" : "self", "uri" : + * "/data/buckets/play/country?from=2024-04-28T09%3A15%3A05%2B02%3A00&currentPage=1&pageSize=25" }, + * { "rel" : "first", "uri" : + * "/data/buckets/play/country?from=2024-04-28T09%3A15%3A05%2B02%3A00&currentPage=1&pageSize=25" }, + * { "rel" : "last", "uri" : + * "/data/buckets/play/country?from=2024-04-28T09%3A15%3A05%2B02%3A00&currentPage=1&pageSize=25" } ] + * } } + */ + } + + @Test + @DisplayName("400 response") + public void responseWithStatus400Test() throws ApiException { + answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-0.json")); + + assertThatThrownBy(() -> api.getMetricsBreakdown("play", "media-id").execute()) + .isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(400)) + .hasMessage("An attribute is invalid."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/request-invalid-query-parameter", "title" : + * "A query parameter is invalid.", "status" : 400, "detail" : "This field was not expected.", "name" : + * "from:2024-05-20T09:15:05+02:00" } + */ + } + + @Test + @DisplayName("404 response") + public void responseWithStatus404Test() throws ApiException { + answerOnAnyRequest(404, readResourceFile(PAYLOADS_PATH + "responses/404.json")); + + assertThatThrownBy(() -> api.getMetricsBreakdown("play", "media-id").execute()) .isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'dimension' when calling getVideosPlays"); - assertThatThrownBy(() -> api.getVideosPlays(null, "videoId").execute()).isInstanceOf(ApiException.class) - .hasMessage("Missing the required parameter 'from' when calling getVideosPlays"); + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(404)) + .hasMessage("Unrecognized request URL."); - assertThatNoException() - .isThrownBy(() -> api.getVideosPlays(LocalDate.parse("2023-04-01"), "videoId").execute()); + /* + * sample response: { "type" : "https://docs.api.video/reference/unrecognized-request-url", "title" : + * "Unrecognized request URL.", "status" : 404 } + */ } @Test - @DisplayName("200 response by videoId") - public void responseWithStatusByVideoId200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-0.json")); - - Page res = api.getVideosPlays(LocalDate.parse("2023-04-01"), "videoId").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("vi3q7HxhApxRF1c8F8r6VeaI"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("vi3q7HxhApxRF1c8F8r6VeaF"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("vi3q7HxhApxRF1c8F8r6VeaH"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); + @DisplayName("429 response") + public void responseWithStatus429Test() throws ApiException { + answerOnAnyRequest(429, readResourceFile(PAYLOADS_PATH + "responses/429.json")); + + assertThatThrownBy(() -> api.getMetricsBreakdown("play", "media-id").execute()) + .isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(429)) + .hasMessage("Too many requests."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/too-many-requests", "title" : + * "Too many requests.", "status" : 429 } + */ } + } + + @Nested + @DisplayName("getMetricsOverTime") + class getMetricsOverTime { + private static final String PAYLOADS_PATH = "/payloads/analytics/getMetricsOverTime/"; @Test - @DisplayName("200 response by country") - public void responseWithStatusByCountry200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-1.json")); - - Page res = api.getVideosPlays(LocalDate.parse("2023-04-01"), "country").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("france"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("united states"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("spain"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); + @DisplayName("required parameters") + public void requiredParametersTest() { + answerOnAnyRequest(201, "{}"); + + assertThatNoException().isThrownBy(() -> api.getMetricsOverTime("play").execute()); + // String metric, OffsetDateTime from, OffsetDateTime to, OffsetDateTime interval, String filterBy, Integer + // currentPage, Integer pageSize } @Test - @DisplayName("200 response by emittedAt") - public void responseWithStatusByEmittedAt200Test() throws ApiException { - answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200-2.json")); - - Page res = api.getVideosPlays(LocalDate.parse("2023-04-01"), "emittedAt").execute(); - - AnalyticsData expected1 = new AnalyticsData().value("2023-06-10T10:00:00.000Z"); - expected1.setPlays(100); - AnalyticsData expected2 = new AnalyticsData().value("2023-06-10T11:00:00.000Z"); - expected2.setPlays(10); - AnalyticsData expected3 = new AnalyticsData().value("2023-06-10T12:00:00.000Z"); - expected3.setPlays(1); - assertThat(res.getItems()).containsExactlyInAnyOrder(expected1, expected2, expected3); + @DisplayName("200 response") + public void responseWithStatus200Test() throws ApiException { + answerOnAnyRequest(200, readResourceFile(PAYLOADS_PATH + "responses/200.json")); + + Page res = api.getMetricsOverTime("play").execute(); + + /* + * sample response: { "context" : { "metric" : "play", "interval" : "hour", "timeframe" : { "from" : + * "2024-05-28T11:08:39+00:00", "to" : "2024-05-29T11:08:39+00:00" } }, "data" : [ { "emittedAt" : + * "2024-05-29T07:00:00+00:00", "metricValue" : 2 }, { "emittedAt" : "2024-05-29T08:00:00+00:00", + * "metricValue" : 1 }, { "emittedAt" : "2024-05-29T09:00:00+00:00", "metricValue" : 1 } ], "pagination" : { + * "currentPage" : 1, "currentPageItems" : 3, "pageSize" : 25, "pagesTotal" : 1, "itemsTotal" : 3, "links" : + * [ { "rel" : "self", "uri" : "/data/timeseries/play?currentPage=1&pageSize=25" }, { "rel" : "first", + * "uri" : "/data/timeseries/play?currentPage=1&pageSize=25" }, { "rel" : "last", "uri" : + * "/data/timeseries/play?currentPage=1&pageSize=25" } ] } } + */ } @Test @DisplayName("400 response") - public void responseWithStatus400ForUnknownDimensionTest() throws ApiException { - answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-2.json")); - - ApiException e = assertThrows(ApiException.class, - () -> api.getVideosPlays(LocalDate.parse("2023-04-01"), "unknownDimension").execute()); - Truth.assertThat(e.getCode()).isEqualTo(400); - Truth.assertThat(e).hasMessageThat().contains("A query parameter is invalid."); + public void responseWithStatus400Test() throws ApiException { + answerOnAnyRequest(400, readResourceFile(PAYLOADS_PATH + "responses/400-0.json")); + + assertThatThrownBy(() -> api.getMetricsOverTime("play").execute()).isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(400)) + .hasMessage("An attribute is invalid."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/request-invalid-query-parameter", "title" : + * "A query parameter is invalid.", "status" : 400, "detail" : "This field was not expected.", "name" : + * "from:2024-05-20T09:15:05+02:00" } + */ } @Test @DisplayName("404 response") public void responseWithStatus404Test() throws ApiException { - answerOnAnyRequest(404, ""); + answerOnAnyRequest(404, readResourceFile(PAYLOADS_PATH + "responses/404.json")); + + assertThatThrownBy(() -> api.getMetricsOverTime("play").execute()).isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(404)) + .hasMessage("Unrecognized request URL."); - ApiException e = assertThrows(ApiException.class, - () -> api.getVideosPlays(LocalDate.parse("2023-04-01"), "country").execute()); - Truth.assertThat(e.getCode()).isEqualTo(404); - Truth.assertThat(e).hasMessageThat().contains(""); + /* + * sample response: { "type" : "https://docs.api.video/reference/unrecognized-request-url", "title" : + * "Unrecognized request URL.", "status" : 404 } + */ + } + + @Test + @DisplayName("429 response") + public void responseWithStatus429Test() throws ApiException { + answerOnAnyRequest(429, readResourceFile(PAYLOADS_PATH + "responses/429.json")); + + assertThatThrownBy(() -> api.getMetricsOverTime("play").execute()).isInstanceOf(ApiException.class) + .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(429)) + .hasMessage("Too many requests."); + + /* + * sample response: { "type" : "https://docs.api.video/reference/too-many-requests", "title" : + * "Too many requests.", "status" : 429 } + */ } } diff --git a/templates/java/statics/java/src/test/java/video/api/client/api/clients/WebhooksApiTest.java b/templates/java/statics/java/src/test/java/video/api/client/api/clients/WebhooksApiTest.java index b675bede..8deead53 100644 --- a/templates/java/statics/java/src/test/java/video/api/client/api/clients/WebhooksApiTest.java +++ b/templates/java/statics/java/src/test/java/video/api/client/api/clients/WebhooksApiTest.java @@ -174,11 +174,11 @@ public void responseWithStatus400Test() throws ApiException { .satisfies(e -> assertThat(((ApiException) e).getCode()).isEqualTo(400)).satisfies(e -> { ApiException apiException = (ApiException) e; assertThat(apiException.getProblems()).containsExactlyInAnyOrder( - new ApiException.ApiProblem("https://docs.api.video/docs/attributerequired", + new ApiException.ApiProblem("https://docs.api.video/reference/attribute-required", "This attribute is required.", "events"), - new ApiException.ApiProblem("https://docs.api.video/docs/attributerequired", + new ApiException.ApiProblem("https://docs.api.video/reference/attribute-required", "This attribute is required.", "url"), - new ApiException.ApiProblem("https://docs.api.video/docs/attributeinvalid", + new ApiException.ApiProblem("https://docs.api.video/reference/invalid-attribute", "This attribute must be an array.", "events")); }).hasMessage("This attribute is required."); } diff --git a/templates/nodejs/src/ObjectSerializer.ts.mustache b/templates/nodejs/src/ObjectSerializer.ts.mustache index 670ee3e2..46a4c9f4 100644 --- a/templates/nodejs/src/ObjectSerializer.ts.mustache +++ b/templates/nodejs/src/ObjectSerializer.ts.mustache @@ -94,7 +94,7 @@ export default class ObjectSerializer { return data.getFullYear() + "-" + month + "-" + day; } else { - return data.toISOString(); + return data.toISOString().split('.')[0] + 'Z'; } } else { if (enumsMap.has(type)) { diff --git a/templates/nodejs/src/api/api.ts.mustache b/templates/nodejs/src/api/api.ts.mustache index fe32802d..9d85a329 100644 --- a/templates/nodejs/src/api/api.ts.mustache +++ b/templates/nodejs/src/api/api.ts.mustache @@ -314,7 +314,18 @@ export default class {{classname}} { if (typeof {{paramName}} !== 'object') { throw new Error(`${ {{paramName}} } is not an object`); } - Object.keys({{paramName}}).forEach((k) => urlSearchParams.append("{{baseName}}["+k+"]", ObjectSerializer.serialize({{paramName}}[k], "string", ""))); + Object.keys({{paramName}}).forEach((k) => { + if(({{paramName}} as any)[k] instanceof Object) { + Object.keys(({{paramName}} as any)[k]).forEach((key) => { + urlSearchParams.append( + `{{baseName}}[${k}][${key}]`, + ObjectSerializer.serialize(({{baseName}} as any)[k][key], 'string', '') + ); + }); + } else { + urlSearchParams.append("{{baseName}}["+k+"]", ObjectSerializer.serialize(({{paramName}} as any)[k], "string", "")) + } + }); {{/ vendorExtensions.x-is-deep-object }} {{^ vendorExtensions.x-is-deep-object }} {{#isArray}} diff --git a/templates/python/api_client.mustache b/templates/python/api_client.mustache index 6a1348e4..2060a11d 100644 --- a/templates/python/api_client.mustache +++ b/templates/python/api_client.mustache @@ -527,7 +527,11 @@ class ApiClient(object): if k in collection_formats: collection_format = collection_formats[k] if collection_format == 'deepObject': - new_params.extend((k+'['+value+']', v[value]) for value in v) + for value in v: + if isinstance(v[value], list): + new_params.extend((k + '[' + value + '][]', item) for item in v[value]) + else: + new_params.append((k+'['+value+']', v[value])) elif collection_format == 'multi': new_params.extend((k, value) for value in v) else: diff --git a/templates/swift5/APIHelper.mustache b/templates/swift5/APIHelper.mustache index 78c98979..ca647781 100644 --- a/templates/swift5/APIHelper.mustache +++ b/templates/swift5/APIHelper.mustache @@ -78,10 +78,6 @@ import Vapor{{/useVapor}} result.append(URLQueryItem(name: item.key, value: "\(value)")) } } - - if destination.isEmpty { - return nil - } return destination } } diff --git a/templates/swift5/OpenISO8601DateFormatter.mustache b/templates/swift5/OpenISO8601DateFormatter.mustache index 29c28dac..eaa09aca 100644 --- a/templates/swift5/OpenISO8601DateFormatter.mustache +++ b/templates/swift5/OpenISO8601DateFormatter.mustache @@ -35,6 +35,10 @@ import Foundation setup() } + override {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func string(from date: Date) -> String { + return OpenISO8601DateFormatter.withoutSeconds.string(from: date) + } + override {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func date(from string: String) -> Date? { if let result = super.date(from: string) { return result diff --git a/templates/swift5/api.mustache b/templates/swift5/api.mustache index 1871ab7a..e4327d6b 100644 --- a/templates/swift5/api.mustache +++ b/templates/swift5/api.mustache @@ -555,11 +555,15 @@ extension {{projectName}}API { {{/hasFormParams}} {{/bodyParam}}{{#hasQueryParams}} var localVariableUrlComponents = URLComponents(string: localVariableURLString) - localVariableUrlComponents?.queryItems = APIHelper.mapValuesToQueryItems([{{^queryParams}}:{{/queryParams}} - {{#queryParams}} - {{> _param}}, - {{/queryParams}} - ]){{/hasQueryParams}}{{^hasQueryParams}} + localVariableUrlComponents?.queryItems = APIHelper.mapValuesToQueryItems([{{^queryParams}}:{{/queryParams}}{{#queryParams}}{{^vendorExtensions.x-is-deep-object}} + {{> _param}},{{/vendorExtensions.x-is-deep-object}}{{/queryParams}} + ]) + {{#queryParams}} +{{#vendorExtensions.x-is-deep-object}} localVariableUrlComponents?.queryItems?.append(contentsOf: {{paramName}}?.encodeToQueryParams() ?? []) + + {{/vendorExtensions.x-is-deep-object}} + {{/queryParams}} + {{/hasQueryParams}}{{^hasQueryParams}} let localVariableUrlComponents = URLComponents(string: localVariableURLString){{/hasQueryParams}} {{#vendorExtensions.x-client-chunk-upload}}var{{/vendorExtensions.x-client-chunk-upload}}{{^vendorExtensions.x-client-chunk-upload}}let{{/vendorExtensions.x-client-chunk-upload}} localVariableNillableHeaders: [String: Any?] = [{{^headerParams}}{{^hasFormParams}} @@ -637,7 +641,14 @@ extension {{projectName}}API { {{#queryParams}} {{> _param}}, {{/queryParams}} - ]){{/hasQueryParams}}{{^hasQueryParams}} + ]) + {{#queryParams}} + + {{#vendorExtensions.x-is-deep-object}} + localVariableUrlComponents?.queryItems?.append(contentsOf: filterBy?.encodeToQueryParams() ?? []) + {{/vendorExtensions.x-is-deep-object}} + {{/queryParams}} + {{/hasQueryParams}}{{^hasQueryParams}} let localVariableUrlComponents = URLComponents(string: localVariableURLString){{/hasQueryParams}} var localVariableNillableHeaders: [String: Any?] = [{{^headerParams}}{{^hasFormParams}} diff --git a/templates/swift5/modelObject.mustache b/templates/swift5/modelObject.mustache index d3add802..2d1bd1e7 100644 --- a/templates/swift5/modelObject.mustache +++ b/templates/swift5/modelObject.mustache @@ -58,6 +58,28 @@ } }{{/additionalPropertiesType}}{{/generateModelAdditionalProperties}} +{{#vendorExtensions.x-is-deep-object}} + public func encodeToQueryParams() -> [URLQueryItem] { + var queryItems = [URLQueryItem]() + + {{#allVars}} + {{^isArray}} + if let {{{name}}} = {{{name}}} { + queryItems.append(URLQueryItem(name: "{{{ vendorExtensions.x-model-name }}}[{{{name}}}]", value: {{{name}}}{{#isEnum}}.rawValue{{/isEnum}})) + } + {{/isArray}} + {{#isArray}} + if let {{{name}}} = {{{name}}}, !{{{name}}}.isEmpty { + for (index, val) in {{{name}}}.enumerated() { + queryItems.append(URLQueryItem(name: "{{{ vendorExtensions.x-model-name }}}[{{{name}}}][\(index)]", value: val{{#isEnum}}.rawValue{{/isEnum}})) + } + } + {{/isArray}} + {{/allVars}} + + return queryItems + } +{{/vendorExtensions.x-is-deep-object}} // Encodable protocol methods {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func encode(to encoder: Encoder) throws {