diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data.meta
new file mode 100644
index 0000000..ef95eb9
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b3b6a48119d19c14fb2918549caf5d70
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations.meta
new file mode 100644
index 0000000..64005fc
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: eb98eb895d641f942bba5bd709cf1704
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic.meta
new file mode 100644
index 0000000..c34d587
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5b711277178b07b4d89e1d348b373e9e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic/Operation.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic/Operation.cs
new file mode 100644
index 0000000..37dad52
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic/Operation.cs
@@ -0,0 +1,50 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Uralstech.UCloud.Operations.Generic
+{
+ ///
+ /// This resource represents a long-running operation that is the result of a network API call.
+ ///
+ /// The type of the operation's metadata.
+ /// The type of the operation's response.
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class Operation
+ {
+ ///
+ /// The server-assigned name, which is only unique within the same service that originally returns it.
+ ///
+ ///
+ /// If you use the default HTTP mapping, the name should be a resource name ending with operations/{unique_id}.
+ ///
+ public string Name;
+
+ ///
+ /// Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time.
+ ///
+ ///
+ /// Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time.
+ ///
+ public TMetadata Metadata = default;
+
+ ///
+ /// If the value is false, it means the operation is still in progress.
+ /// If true, the operation is completed, and either or is available.
+ ///
+ public bool Done;
+
+ ///
+ /// The error result of the operation in case of failure or cancellation.
+ ///
+ public OperationStatus Error = null;
+
+ ///
+ /// The normal response of the operation in case of success. If the original method returns no data on success, such as Delete, the response is google.protobuf.Empty.
+ ///
+ ///
+ /// If the original method is standard Get/Create/Update, the response should be the resource. For other methods, the response should have the type XxxResponse,
+ /// where Xxx is the original method name. For example, if the original method name is TakeSnapshot(), the inferred response type is TakeSnapshotResponse.
+ ///
+ public TResponse Response = default;
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic/Operation.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic/Operation.cs.meta
new file mode 100644
index 0000000..7cba5ac
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Generic/Operation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 22ae15dfa5ce1f74f9eb6b51c99cd023
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get.meta
new file mode 100644
index 0000000..0ff2db3
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 208010de384041349a38a033553d3735
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get/OperationGetRequest.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get/OperationGetRequest.cs
new file mode 100644
index 0000000..40a22b4
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get/OperationGetRequest.cs
@@ -0,0 +1,31 @@
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// Gets the latest state of a long-running operation. Clients can use this method to poll the operation result at intervals as recommended by the API service.
+ ///
+ ///
+ /// Return type is or .
+ ///
+ public class OperationGetRequest : IOperationsGetRequest
+ {
+ ///
+ /// The resource name of the operation to get.
+ ///
+ public string OperationName;
+
+ ///
+ public string GetEndpointUri()
+ {
+ return $"https://servicemanagement.googleapis.com/v1/{OperationName}";
+ }
+
+ ///
+ /// Creates a new .
+ ///
+ /// The resource name of the operation to get.
+ public OperationGetRequest(string operationName)
+ {
+ OperationName = operationName;
+ }
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get/OperationGetRequest.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get/OperationGetRequest.cs.meta
new file mode 100644
index 0000000..cabd150
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Get/OperationGetRequest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 60e436535af8a8e45a866551949215c0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List.meta
new file mode 100644
index 0000000..66aaa29
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 88a62a38b97e61a479ed5320d73d4b2f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic.meta
new file mode 100644
index 0000000..c99e7b2
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8b6b80600298d2143923e40ffe4bdc50
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic/OperationsListResponse.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic/OperationsListResponse.cs
new file mode 100644
index 0000000..0d61ffa
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic/OperationsListResponse.cs
@@ -0,0 +1,24 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Uralstech.UCloud.Operations.Generic
+{
+ ///
+ /// The generic response for an call.
+ ///
+ /// The type of the operation's metadata.
+ /// The type of the operation's response.
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class OperationsListResponse
+ {
+ ///
+ /// A list of operations that matches the specified filter in the request.
+ ///
+ public Operation[] Operations;
+
+ ///
+ /// A token that can be sent as a into a subsequent call.
+ ///
+ public string NextPageToken;
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic/OperationsListResponse.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic/OperationsListResponse.cs.meta
new file mode 100644
index 0000000..89a664e
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/Generic/OperationsListResponse.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5908e9c8b6e6fa1448b9eb982da7d1fe
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListRequest.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListRequest.cs
new file mode 100644
index 0000000..175164d
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListRequest.cs
@@ -0,0 +1,29 @@
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// Requests metadata for an operation. Return type is or .
+ ///
+ public class OperationsListRequest : IOperationsGetRequest
+ {
+ ///
+ /// The maximum number of s to return (per page).
+ ///
+ ///
+ /// This method returns at most 100 operations per page.
+ ///
+ public int MaxResponseOperations = 50;
+
+ ///
+ /// A page token from a previous call.
+ ///
+ public string PageToken = string.Empty;
+
+ ///
+ public string GetEndpointUri()
+ {
+ return string.IsNullOrEmpty(PageToken)
+ ? $"https://servicemanagement.googleapis.com/v1/operations?pageSize={MaxResponseOperations}"
+ : $"https://servicemanagement.googleapis.com/v1/operations?pageSize={MaxResponseOperations}&pageToken={PageToken}";
+ }
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListRequest.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListRequest.cs.meta
new file mode 100644
index 0000000..c5f6679
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListRequest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a57869d2e40b2b3438664e039476f00f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListResponse.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListResponse.cs
new file mode 100644
index 0000000..fb05cc9
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListResponse.cs
@@ -0,0 +1,22 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// The response for an call.
+ ///
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class OperationsListResponse
+ {
+ ///
+ /// A list of operations that matches the specified filter in the request.
+ ///
+ public Operation[] Operations;
+
+ ///
+ /// A token that can be sent as a into a subsequent call.
+ ///
+ public string NextPageToken;
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListResponse.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListResponse.cs.meta
new file mode 100644
index 0000000..4b665f1
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/List/OperationsListResponse.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7487355e672e58b4593589438f96d9da
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Operation.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Operation.cs
new file mode 100644
index 0000000..51621ac
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Operation.cs
@@ -0,0 +1,11 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// This resource represents a long-running operation that is the result of a network API call.
+ ///
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class Operation : Generic.Operation { }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Operation.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Operation.cs.meta
new file mode 100644
index 0000000..51b6c3a
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/Operation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9e68e10188de5404ea2b0484cedcf6a1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/OperationStatus.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/OperationStatus.cs
new file mode 100644
index 0000000..e229fb4
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/OperationStatus.cs
@@ -0,0 +1,33 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// The type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by gRPC.
+ ///
+ ///
+ /// Each message contains three pieces of data: error code, error message, and error details.
+ ///
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class OperationStatus
+ {
+ ///
+ /// The status code, which should be an enum value of google.rpc.Code.
+ ///
+ public int Code;
+
+ ///
+ /// A developer-facing error message, which should be in English.
+ ///
+ ///
+ /// Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.
+ ///
+ public string Message;
+
+ ///
+ /// A list of messages that carry the error details. There is a common set of message types for APIs to use.
+ ///
+ public ProtobufObject[] Details;
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/OperationStatus.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/OperationStatus.cs.meta
new file mode 100644
index 0000000..f0aa7f6
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Operations/OperationStatus.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1ffe0bc5f2ceeca439e85b0d26a83f50
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/ProtobufObject.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/ProtobufObject.cs
new file mode 100644
index 0000000..5c0db68
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/ProtobufObject.cs
@@ -0,0 +1,26 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+using System.Collections.Generic;
+
+namespace Uralstech.UCloud
+{
+ ///
+ /// An object containing fields of an arbitrary type.
+ ///
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class ProtobufObject
+ {
+ ///
+ /// Contains a URI identifying the type.
+ ///
+ [JsonProperty("@type")]
+ public string Type;
+
+ ///
+ /// The actual details of the object.
+ ///
+ [JsonExtensionData]
+ public Dictionary Data;
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/ProtobufObject.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/ProtobufObject.cs.meta
new file mode 100644
index 0000000..9a01efa
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/ProtobufObject.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 43b4c0c2af276834a96214579bac7777
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests.meta
new file mode 100644
index 0000000..ca0b37f
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8dc8b33668a3be9429f2ec6d24fbbb14
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsGetRequest.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsGetRequest.cs
new file mode 100644
index 0000000..41166e0
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsGetRequest.cs
@@ -0,0 +1,7 @@
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// All google.longrunning API GET requests must inherit from this interface.
+ ///
+ public interface IOperationsGetRequest : IOperationsRequest { }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsGetRequest.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsGetRequest.cs.meta
new file mode 100644
index 0000000..6498382
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsGetRequest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 92ea8bd3833865548befb1ddaa8c89fb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsRequest.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsRequest.cs
new file mode 100644
index 0000000..967914b
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsRequest.cs
@@ -0,0 +1,14 @@
+namespace Uralstech.UCloud.Operations
+{
+ ///
+ /// All google.longrunning API requests must inherit from this interface.
+ ///
+ public interface IOperationsRequest
+ {
+ ///
+ /// Gets the URI to the API endpoint.
+ ///
+ /// The URI.
+ public string GetEndpointUri();
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsRequest.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsRequest.cs.meta
new file mode 100644
index 0000000..5066b11
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Data/Requests/IOperationsRequest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a96adb00487d31c46a98c979c59f79f7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions.meta
new file mode 100644
index 0000000..dce087e
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6ec85db5ba02f5e4f849ee0870181b9c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationOAuthException.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationOAuthException.cs
new file mode 100644
index 0000000..7f3c4f1
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationOAuthException.cs
@@ -0,0 +1,34 @@
+using System;
+using UnityEngine.Networking;
+
+namespace Uralstech.UCloud.Operations.Exceptions
+{
+ ///
+ /// Thrown when an exception related to OAuth authentication is raised.
+ ///
+ public class OperationOAuthException : Exception
+ {
+ ///
+ /// The endpoint of the request.
+ ///
+ public Uri RequestEndpoint;
+
+ ///
+ /// The reason for the exception.
+ ///
+ public string Reason;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The request that caused the exception.
+ internal OperationOAuthException(UnityWebRequest webRequest, string reason)
+ : base($"Failed to authenticate google.longrunning API request: " +
+ $"Request Endpoint: {webRequest.uri.AbsolutePath} | " +
+ $"Reason:\n{reason}")
+ {
+ RequestEndpoint = webRequest.uri;
+ Reason = reason;
+ }
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationOAuthException.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationOAuthException.cs.meta
new file mode 100644
index 0000000..cff81ac
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationOAuthException.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6ca8f2f04b97c81498e0f4ba2dbbfdb9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationRequestException.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationRequestException.cs
new file mode 100644
index 0000000..2a39bfb
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationRequestException.cs
@@ -0,0 +1,49 @@
+using System;
+using UnityEngine.Networking;
+
+namespace Uralstech.UCloud.Operations.Exceptions
+{
+ ///
+ /// Thrown if a google.longrunning API request fails.
+ ///
+ public class OperationRequestException : Exception
+ {
+ ///
+ /// The endpoint of the failed request.
+ ///
+ public Uri RequestEndpoint;
+
+ ///
+ /// The response code returned by the request.
+ ///
+ public long RequestErrorCode;
+
+ ///
+ /// The name of the request's error.
+ ///
+ public string RequestError;
+
+ ///
+ /// The request's error message.
+ ///
+ public string RequestErrorMessage;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The request that caused the exception.
+ internal OperationRequestException(UnityWebRequest webRequest)
+ : base($"Failed google.longrunning API request: " +
+ $"Request Endpoint: {webRequest.uri.AbsolutePath} | " +
+ $"Request Error Code: {webRequest.responseCode} | " +
+ $"Request Error: {webRequest.error} | " +
+ $"Details:\n{webRequest.downloadHandler?.text}")
+ {
+ RequestEndpoint = webRequest.uri;
+
+ RequestError = webRequest.error;
+ RequestErrorCode = webRequest.responseCode;
+ RequestErrorMessage = webRequest.downloadHandler?.text;
+ }
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationRequestException.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationRequestException.cs.meta
new file mode 100644
index 0000000..18f43f6
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationRequestException.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: df20b815f7886a74b91844ebbefd3167
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationResponseParsingException.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationResponseParsingException.cs
new file mode 100644
index 0000000..e2878ae
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationResponseParsingException.cs
@@ -0,0 +1,35 @@
+using System;
+using UnityEngine.Networking;
+
+namespace Uralstech.UCloud.Operations.Exceptions
+{
+ ///
+ /// Thrown if the response of a google.longrunning API request could not be parsed.
+ ///
+ public class OperationResponseParsingException : Exception
+ {
+ ///
+ /// The endpoint of the request.
+ ///
+ public Uri RequestEndpoint;
+
+ ///
+ /// The content downloaded from the request.
+ ///
+ public string DownloadedText;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The request that caused the exception.
+ /// The inner exception that caused this one.
+ internal OperationResponseParsingException(UnityWebRequest webRequest, Exception innerException)
+ : base($"Failed to parse google.longrunning API response: " +
+ $"Request Endpoint: {webRequest.uri.AbsolutePath} | " +
+ $"Downloaded Text:\n{webRequest.downloadHandler?.text}", innerException)
+ {
+ RequestEndpoint = webRequest.uri;
+ DownloadedText = webRequest.downloadHandler?.text;
+ }
+ }
+}
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationResponseParsingException.cs.meta b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationResponseParsingException.cs.meta
new file mode 100644
index 0000000..ce266a6
--- /dev/null
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Exceptions/OperationResponseParsingException.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 162d2b0833827f34587333abe445cfe8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Managers/OperationsManager.cs b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Managers/OperationsManager.cs
index e1eeb5d..028be97 100644
--- a/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Managers/OperationsManager.cs
+++ b/UCloud.Operations/Packages/com.uralstech.ucloud.operations/Runtime/Scripts/Managers/OperationsManager.cs
@@ -1,10 +1,110 @@
+using Newtonsoft.Json;
+using System;
+using System.Threading.Tasks;
using UnityEngine;
+using UnityEngine.Networking;
+using Uralstech.UCloud.Operations.Exceptions;
using Uralstech.Utils.Singleton;
namespace Uralstech.UCloud.Operations
{
+ ///
+ /// The class for accessing the google.longrunning API!
+ ///
+ [AddComponentMenu("Uralstech/UCloud/Operations/Operations Manager")]
public class OperationsManager : Singleton
{
+ ///
+ /// Computes a GET request on the google.longrunning API.
+ ///
+ ///
+ ///
+ /// The response type. For example, a request of type corresponds
+ /// to a response type of or .
+ ///
+ ///
+ /// The OAuth Access Token to use for authentication.
+ /// The request object.
+ /// The computed response.
+ /// Thrown if the request could not be authenticated.
+ /// Thrown if the API request fails.
+ /// Thrown if the response could not be parsed.
+ public async Task Request(string accessToken, IOperationsGetRequest request)
+ {
+ string requestEndpoint = request.GetEndpointUri();
+
+ using UnityWebRequest webRequest = UnityWebRequest.Get(requestEndpoint);
+ await ComputeRequest(accessToken, webRequest);
+
+ return ConfirmResponse(webRequest);
+ }
+
+ ///
+ /// Sets up, sends and verifies a .
+ ///
+ /// The OAuth Access Token to use for authentication.
+ /// The to compute.
+ /// Thrown if the request could not be authenticated.
+ /// Thrown if the request was not successful.
+ private async Task ComputeRequest(string accessToken, UnityWebRequest webRequest)
+ {
+ SetupWebRequest(accessToken, webRequest);
+
+ UnityWebRequestAsyncOperation operation = webRequest.SendWebRequest();
+ while (!operation.isDone)
+ await Task.Yield();
+
+ CheckWebRequest(webRequest);
+ }
+
+ ///
+ /// Sets up the with API keys and disposal settings.
+ ///
+ /// The OAuth Access Token to use for authentication.
+ /// The request to set up.
+ /// Thrown if the request could not be authenticated.
+ private void SetupWebRequest(string accessToken, UnityWebRequest webRequest)
+ {
+ if (string.IsNullOrWhiteSpace(accessToken))
+ throw new OperationOAuthException(webRequest, "The provided access token was empty!");
+
+ webRequest.SetRequestHeader("Authorization", $"Bearer {accessToken}");
+
+ webRequest.disposeUploadHandlerOnDispose = true;
+ webRequest.disposeDownloadHandlerOnDispose = true;
+ }
+
+ ///
+ /// Checks the given for errors.
+ ///
+ /// The request to check.
+ /// Thrown if the request was not successful.
+ private void CheckWebRequest(UnityWebRequest webRequest)
+ {
+ if (webRequest.result != UnityWebRequest.Result.Success)
+ throw new OperationRequestException(webRequest);
+
+ Debug.Log("Operations API computation succeeded.");
+ }
+
+ ///
+ /// Checks if the downloaded response was correct.
+ ///
+ /// The expected response type.
+ /// The web request.
+ /// Thrown if the response could not be parsed.
+ private TResponse ConfirmResponse(UnityWebRequest request)
+ {
+ try
+ {
+ return JsonConvert.DeserializeObject(request.downloadHandler?.text);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"Failed to confirm successful API response:\n{e}");
+ throw new OperationResponseParsingException(request, e);
+ }
+ }
}
}