diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7a29e50 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,901 @@ +[*.cs] + +# CA1001: Types that own disposable fields should be disposable +dotnet_diagnostic.CA1001.severity = warning + +# CA1000: Do not declare static members on generic types +dotnet_diagnostic.CA1000.severity = warning + +# CA1002: Do not expose generic lists +dotnet_diagnostic.CA1002.severity = warning + +# CA1003: Use generic event handler instances +dotnet_diagnostic.CA1003.severity = warning + +# CA1005: Avoid excessive parameters on generic types +dotnet_diagnostic.CA1005.severity = warning + +# CA1008: Enums should have zero value +dotnet_diagnostic.CA1008.severity = warning + +# CA1010: Generic interface should also be implemented +dotnet_diagnostic.CA1010.severity = warning + +# CA1012: Abstract types should not have public constructors +dotnet_diagnostic.CA1012.severity = warning + +# CA1014: Mark assemblies with CLSCompliant +dotnet_diagnostic.CA1014.severity = warning + +# CA1016: Mark assemblies with assembly version +dotnet_diagnostic.CA1016.severity = warning + +# CA1017: Mark assemblies with ComVisible +dotnet_diagnostic.CA1017.severity = warning + +# CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1018.severity = warning + +# CA1019: Define accessors for attribute arguments +dotnet_diagnostic.CA1019.severity = warning + +# CA1021: Avoid out parameters +dotnet_diagnostic.CA1021.severity = warning + +# CA1024: Use properties where appropriate +dotnet_diagnostic.CA1024.severity = warning + +# CA1027: Mark enums with FlagsAttribute +dotnet_diagnostic.CA1027.severity = warning + +# CA1028: Enum Storage should be Int32 +dotnet_diagnostic.CA1028.severity = warning + +# CA1030: Use events where appropriate +dotnet_diagnostic.CA1030.severity = warning + +# CA1031: Do not catch general exception types +dotnet_diagnostic.CA1031.severity = warning + +# CA1033: Interface methods should be callable by child types +dotnet_diagnostic.CA1033.severity = warning + +# CA1034: Nested types should not be visible +dotnet_diagnostic.CA1034.severity = warning + +# CA1036: Override methods on comparable types +dotnet_diagnostic.CA1036.severity = warning + +# CA1040: Avoid empty interfaces +dotnet_diagnostic.CA1040.severity = warning + +# CA1041: Provide ObsoleteAttribute message +dotnet_diagnostic.CA1041.severity = warning + +# CA1043: Use Integral Or String Argument For Indexers +dotnet_diagnostic.CA1043.severity = warning + +# CA1044: Properties should not be write only +dotnet_diagnostic.CA1044.severity = warning + +# CA1045: Do not pass types by reference +dotnet_diagnostic.CA1045.severity = warning + +# CA1046: Do not overload equality operator on reference types +dotnet_diagnostic.CA1046.severity = warning + +# CA1050: Declare types in namespaces +dotnet_diagnostic.CA1050.severity = warning + +# CA1051: Do not declare visible instance fields +dotnet_diagnostic.CA1051.severity = warning + +# CA1052: Static holder types should be Static or NotInheritable +dotnet_diagnostic.CA1052.severity = warning + +# CA1054: URI-like parameters should not be strings +dotnet_diagnostic.CA1054.severity = warning + +# CA1055: URI-like return values should not be strings +dotnet_diagnostic.CA1055.severity = warning + +# CA1056: URI-like properties should not be strings +dotnet_diagnostic.CA1056.severity = warning + +# CA1058: Types should not extend certain base types +dotnet_diagnostic.CA1058.severity = warning + +# CA1060: Move pinvokes to native methods class +dotnet_diagnostic.CA1060.severity = warning + +# CA1061: Do not hide base class methods +dotnet_diagnostic.CA1061.severity = warning + +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = warning + +# CA1063: Implement IDisposable Correctly +dotnet_diagnostic.CA1063.severity = warning + +# CA1064: Exceptions should be public +dotnet_diagnostic.CA1064.severity = warning + +# CA1065: Do not raise exceptions in unexpected locations +dotnet_diagnostic.CA1065.severity = warning + +# CA1066: Implement IEquatable when overriding Object.Equals +dotnet_diagnostic.CA1066.severity = warning + +# CA1067: Override Object.Equals(object) when implementing IEquatable +dotnet_diagnostic.CA1067.severity = warning + +# CA1068: CancellationToken parameters must come last +dotnet_diagnostic.CA1068.severity = warning + +# CA1069: Enums values should not be duplicated +dotnet_diagnostic.CA1069.severity = warning + +# CA1070: Do not declare event fields as virtual +dotnet_diagnostic.CA1070.severity = warning + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = warning + +# CA1304: Specify CultureInfo +dotnet_diagnostic.CA1304.severity = warning + +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = warning + +# CA1307: Specify StringComparison for clarity +dotnet_diagnostic.CA1307.severity = warning + +# CA1308: Normalize strings to uppercase +dotnet_diagnostic.CA1308.severity = warning + +# CA1310: Specify StringComparison for correctness +dotnet_diagnostic.CA1310.severity = warning + +# CA1401: P/Invokes should not be visible +dotnet_diagnostic.CA1401.severity = warning + +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = warning + +# CA1417: Do not use 'OutAttribute' on string parameters for P/Invokes +dotnet_diagnostic.CA1417.severity = warning + +# CA1418: Use valid platform string +dotnet_diagnostic.CA1418.severity = warning + +# CA1419: Provide a parameterless constructor that is as visible as the containing type for concrete types derived from 'System.Runtime.InteropServices.SafeHandle' +dotnet_diagnostic.CA1419.severity = warning + +# CA1420: Property, type, or attribute requires runtime marshalling +dotnet_diagnostic.CA1420.severity = warning + +# CA1421: This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied +dotnet_diagnostic.CA1421.severity = warning + +# CA1422: Validate platform compatibility +dotnet_diagnostic.CA1422.severity = warning + +# CA1501: Avoid excessive inheritance +dotnet_diagnostic.CA1501.severity = warning + +# CA1502: Avoid excessive complexity +dotnet_diagnostic.CA1502.severity = warning + +# CA1505: Avoid unmaintainable code +dotnet_diagnostic.CA1505.severity = warning + +# CA1506: Avoid excessive class coupling +# dotnet_diagnostic.CA1506.severity = warning + +# CA1509: Invalid entry in code metrics rule specification file +dotnet_diagnostic.CA1509.severity = warning + +# CA1510: Use ArgumentNullException throw helper +dotnet_diagnostic.CA1510.severity = warning + +# CA1511: Use ArgumentException throw helper +dotnet_diagnostic.CA1511.severity = warning + +# CA1512: Use ArgumentOutOfRangeException throw helper +dotnet_diagnostic.CA1512.severity = warning + +# CA1513: Use ObjectDisposedException throw helper +dotnet_diagnostic.CA1513.severity = warning + +# CA1700: Do not name enum values 'Reserved' +dotnet_diagnostic.CA1700.severity = warning + +# CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1707.severity = warning + +# CA1708: Identifiers should differ by more than case +dotnet_diagnostic.CA1708.severity = warning + +# CA1710: Identifiers should have correct suffix +dotnet_diagnostic.CA1710.severity = warning + +# CA1711: Identifiers should not have incorrect suffix +dotnet_diagnostic.CA1711.severity = warning + +# CA1712: Do not prefix enum values with type name +dotnet_diagnostic.CA1712.severity = warning + +# CA1713: Events should not have 'Before' or 'After' prefix +dotnet_diagnostic.CA1713.severity = warning + +# CA1715: Identifiers should have correct prefix +dotnet_diagnostic.CA1715.severity = warning + +# CA1716: Identifiers should not match keywords +dotnet_diagnostic.CA1716.severity = warning + +# CA1720: Identifier contains type name +dotnet_diagnostic.CA1720.severity = warning + +# CA1721: Property names should not match get methods +dotnet_diagnostic.CA1721.severity = warning + +# CA1724: Type names should not match namespaces +dotnet_diagnostic.CA1724.severity = warning + +# CA1725: Parameter names should match base declaration +dotnet_diagnostic.CA1725.severity = warning + +# CA1727: Use PascalCase for named placeholders +dotnet_diagnostic.CA1727.severity = warning + +# CA1806: Do not ignore method results +dotnet_diagnostic.CA1806.severity = warning + +# CA1810: Initialize reference type static fields inline +dotnet_diagnostic.CA1810.severity = warning + +# CA1813: Avoid unsealed attributes +dotnet_diagnostic.CA1813.severity = warning + +# CA1814: Prefer jagged arrays over multidimensional +dotnet_diagnostic.CA1814.severity = warning + +# CA1815: Override equals and operator equals on value types +dotnet_diagnostic.CA1815.severity = warning + +# CA1816: Dispose methods should call SuppressFinalize +dotnet_diagnostic.CA1816.severity = warning + +# CA1819: Properties should not return arrays +# dotnet_diagnostic.CA1819.severity = warning + +# CA1820: Test for empty strings using string length +dotnet_diagnostic.CA1820.severity = warning + +# CA1821: Remove empty Finalizers +dotnet_diagnostic.CA1821.severity = warning + +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = warning + +# CA1823: Avoid unused private fields +dotnet_diagnostic.CA1823.severity = warning + +# CA1826: Do not use Enumerable methods on indexable collections +dotnet_diagnostic.CA1826.severity = warning + +# CA1827: Do not use Count() or LongCount() when Any() can be used +dotnet_diagnostic.CA1827.severity = warning + +# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used +dotnet_diagnostic.CA1828.severity = warning + +# CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1829.severity = warning + +# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder +dotnet_diagnostic.CA1830.severity = warning + +# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1831.severity = warning + +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1832.severity = warning + +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1833.severity = warning + +# CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1834.severity = warning + +# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' +dotnet_diagnostic.CA1835.severity = warning + +# CA1836: Prefer IsEmpty over Count +dotnet_diagnostic.CA1836.severity = warning + +# CA1837: Use 'Environment.ProcessId' +dotnet_diagnostic.CA1837.severity = warning + +# CA1838: Avoid 'StringBuilder' parameters for P/Invokes +dotnet_diagnostic.CA1838.severity = warning + +# CA1839: Use 'Environment.ProcessPath' +dotnet_diagnostic.CA1839.severity = warning + +# CA1840: Use 'Environment.CurrentManagedThreadId' +dotnet_diagnostic.CA1840.severity = warning + +# CA1842: Do not use 'WhenAll' with a single task +dotnet_diagnostic.CA1842.severity = warning + +# CA1843: Do not use 'WaitAll' with a single task +dotnet_diagnostic.CA1843.severity = warning + +# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream' +dotnet_diagnostic.CA1844.severity = warning + +# CA1846: Prefer 'AsSpan' over 'Substring' +dotnet_diagnostic.CA1846.severity = warning + +# CA1847: Use char literal for a single character lookup +dotnet_diagnostic.CA1847.severity = warning + +# CA1848: Use the LoggerMessage delegates +dotnet_diagnostic.CA1848.severity = warning + +# CA1849: Call async methods when in an async method +dotnet_diagnostic.CA1849.severity = warning + +# CA1850: Prefer static 'HashData' method over 'ComputeHash' +dotnet_diagnostic.CA1850.severity = warning + +# CA1852: Seal internal types +dotnet_diagnostic.CA1852.severity = warning + +# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)' +dotnet_diagnostic.CA1853.severity = warning + +# CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method +dotnet_diagnostic.CA1854.severity = warning + +# CA1858: Use 'StartsWith' instead of 'IndexOf' +dotnet_diagnostic.CA1858.severity = warning + +# CA1859: Use concrete types when possible for improved performance +dotnet_diagnostic.CA1859.severity = warning + +# CA1860: Avoid using 'Enumerable.Any()' extension method +dotnet_diagnostic.CA1860.severity = warning + +# CA1861: Avoid constant arrays as arguments +dotnet_diagnostic.CA1861.severity = warning + +# CA1862: Use the 'StringComparison' method overloads to perform case-insensitive string comparisons +dotnet_diagnostic.CA1862.severity = warning + +# CA1863: Use 'CompositeFormat' +dotnet_diagnostic.CA1863.severity = warning + +# CA1864: Prefer the 'IDictionary.TryAdd(TKey, TValue)' method +dotnet_diagnostic.CA1864.severity = warning + +# CA1868: Unnecessary call to 'Contains(item)' +dotnet_diagnostic.CA1868.severity = warning + +# CA1869: Cache and reuse 'JsonSerializerOptions' instances +dotnet_diagnostic.CA1869.severity = warning + +# CA2000: Dispose objects before losing scope +dotnet_diagnostic.CA2000.severity = warning + +# CA2002: Do not lock on objects with weak identity +dotnet_diagnostic.CA2002.severity = warning + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = warning + +# CA2008: Do not create tasks without passing a TaskScheduler +dotnet_diagnostic.CA2008.severity = warning + +# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value +dotnet_diagnostic.CA2009.severity = warning + +# CA2011: Avoid infinite recursion +dotnet_diagnostic.CA2011.severity = warning + +# CA2012: Use ValueTasks correctly +dotnet_diagnostic.CA2012.severity = warning + +# CA2013: Do not use ReferenceEquals with value types +dotnet_diagnostic.CA2013.severity = warning + +# CA2015: Do not define finalizers for types derived from MemoryManager +dotnet_diagnostic.CA2015.severity = warning + +# CA2017: Parameter count mismatch +dotnet_diagnostic.CA2017.severity = warning + +# CA2018: 'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument +dotnet_diagnostic.CA2018.severity = warning + +# CA2019: Improper 'ThreadStatic' field initialization +dotnet_diagnostic.CA2019.severity = warning + +# CA2021: Do not call Enumerable.Cast or Enumerable.OfType with incompatible types +dotnet_diagnostic.CA2021.severity = warning + +# CA2100: Review SQL queries for security vulnerabilities +dotnet_diagnostic.CA2100.severity = warning + +# CA2101: Specify marshaling for P/Invoke string arguments +dotnet_diagnostic.CA2101.severity = warning + +# CA2119: Seal methods that satisfy private interfaces +dotnet_diagnostic.CA2119.severity = warning + +# CA2153: Do Not Catch Corrupted State Exceptions +dotnet_diagnostic.CA2153.severity = warning + +# CA2200: Rethrow to preserve stack details +dotnet_diagnostic.CA2200.severity = warning + +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA2201.severity = warning + +# CA2207: Initialize value type static fields inline +dotnet_diagnostic.CA2207.severity = warning + +# CA2208: Instantiate argument exceptions correctly +dotnet_diagnostic.CA2208.severity = warning + +# CA2211: Non-constant fields should not be visible +dotnet_diagnostic.CA2211.severity = warning + +# CA2213: Disposable fields should be disposed +dotnet_diagnostic.CA2213.severity = warning + +# CA2214: Do not call overridable methods in constructors +dotnet_diagnostic.CA2214.severity = warning + +# CA2215: Dispose methods should call base class dispose +dotnet_diagnostic.CA2215.severity = warning + +# CA2216: Disposable types should declare finalizer +dotnet_diagnostic.CA2216.severity = warning + +# CA2217: Do not mark enums with FlagsAttribute +dotnet_diagnostic.CA2217.severity = warning + +# CA2219: Do not raise exceptions in finally clauses +dotnet_diagnostic.CA2219.severity = warning + +# CA2225: Operator overloads have named alternates +dotnet_diagnostic.CA2225.severity = warning + +# CA2226: Operators should have symmetrical overloads +dotnet_diagnostic.CA2226.severity = warning + +# CA2227: Collection properties should be read only +dotnet_diagnostic.CA2227.severity = warning + +# CA2231: Overload operator equals on overriding value type Equals +dotnet_diagnostic.CA2231.severity = warning + +# CA2235: Mark all non-serializable fields +dotnet_diagnostic.CA2235.severity = warning + +# CA2237: Mark ISerializable types with serializable +dotnet_diagnostic.CA2237.severity = warning + +# CA2241: Provide correct arguments to formatting methods +dotnet_diagnostic.CA2241.severity = warning + +# CA2242: Test for NaN correctly +dotnet_diagnostic.CA2242.severity = warning + +# CA2243: Attribute string literals should parse correctly +dotnet_diagnostic.CA2243.severity = warning + +# CA2244: Do not duplicate indexed element initializations +dotnet_diagnostic.CA2244.severity = warning + +# CA2245: Do not assign a property to itself +dotnet_diagnostic.CA2245.severity = warning + +# CA2246: Assigning symbol and its member in the same statement +dotnet_diagnostic.CA2246.severity = warning + +# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum +dotnet_diagnostic.CA2247.severity = warning + +# CA2248: Provide correct 'enum' argument to 'Enum.HasFlag' +dotnet_diagnostic.CA2248.severity = warning + +# CA2249: Consider using 'string.Contains' instead of 'string.IndexOf' +dotnet_diagnostic.CA2249.severity = warning + +# CA2250: Use 'ThrowIfCancellationRequested' +dotnet_diagnostic.CA2250.severity = warning + +# CA2251: Use 'string.Equals' +dotnet_diagnostic.CA2251.severity = warning + +# CA2253: Named placeholders should not be numeric values +dotnet_diagnostic.CA2253.severity = warning + +# CA2254: Template should be a static expression +dotnet_diagnostic.CA2254.severity = warning + +# CA2255: The 'ModuleInitializer' attribute should not be used in libraries +dotnet_diagnostic.CA2255.severity = warning + +# CA2256: All members declared in parent interfaces must have an implementation in a DynamicInterfaceCastableImplementation-attributed interface +dotnet_diagnostic.CA2256.severity = warning + +# CA2257: Members defined on an interface with the 'DynamicInterfaceCastableImplementationAttribute' should be 'static' +dotnet_diagnostic.CA2257.severity = warning + +# CA2258: Providing a 'DynamicInterfaceCastableImplementation' interface in Visual Basic is unsupported +dotnet_diagnostic.CA2258.severity = warning + +# CA2259: 'ThreadStatic' only affects static fields +dotnet_diagnostic.CA2259.severity = warning + +# CA2261: Do not use ConfigureAwaitOptions.SuppressThrowing with Task +dotnet_diagnostic.CA2261.severity = warning + +# CA2300: Do not use insecure deserializer BinaryFormatter +dotnet_diagnostic.CA2300.severity = warning + +# CA2301: Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder +dotnet_diagnostic.CA2301.severity = warning + +# CA2302: Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize +dotnet_diagnostic.CA2302.severity = warning + +# CA2305: Do not use insecure deserializer LosFormatter +dotnet_diagnostic.CA2305.severity = warning + +# CA2310: Do not use insecure deserializer NetDataContractSerializer +dotnet_diagnostic.CA2310.severity = warning + +# CA2311: Do not deserialize without first setting NetDataContractSerializer.Binder +dotnet_diagnostic.CA2311.severity = warning + +# CA2312: Ensure NetDataContractSerializer.Binder is set before deserializing +dotnet_diagnostic.CA2312.severity = warning + +# CA2315: Do not use insecure deserializer ObjectStateFormatter +dotnet_diagnostic.CA2315.severity = warning + +# CA2321: Do not deserialize with JavaScriptSerializer using a SimpleTypeResolver +dotnet_diagnostic.CA2321.severity = warning + +# CA2322: Ensure JavaScriptSerializer is not initialized with SimpleTypeResolver before deserializing +dotnet_diagnostic.CA2322.severity = warning + +# CA2326: Do not use TypeNameHandling values other than None +dotnet_diagnostic.CA2326.severity = warning + +# CA2327: Do not use insecure JsonSerializerSettings +dotnet_diagnostic.CA2327.severity = warning + +# CA2328: Ensure that JsonSerializerSettings are secure +dotnet_diagnostic.CA2328.severity = warning + +# CA2329: Do not deserialize with JsonSerializer using an insecure configuration +dotnet_diagnostic.CA2329.severity = warning + +# CA2330: Ensure that JsonSerializer has a secure configuration when deserializing +dotnet_diagnostic.CA2330.severity = warning + +# CA2350: Do not use DataTable.ReadXml() with untrusted data +dotnet_diagnostic.CA2350.severity = warning + +# CA2351: Do not use DataSet.ReadXml() with untrusted data +dotnet_diagnostic.CA2351.severity = warning + +# CA2361: Ensure auto-generated class containing DataSet.ReadXml() is not used with untrusted data +dotnet_diagnostic.CA2361.severity = warning + +# CA3001: Review code for SQL injection vulnerabilities +dotnet_diagnostic.CA3001.severity = warning + +# CA3002: Review code for XSS vulnerabilities +dotnet_diagnostic.CA3002.severity = warning + +# CA3003: Review code for file path injection vulnerabilities +dotnet_diagnostic.CA3003.severity = warning + +# CA3004: Review code for information disclosure vulnerabilities +dotnet_diagnostic.CA3004.severity = warning + +# CA3005: Review code for LDAP injection vulnerabilities +dotnet_diagnostic.CA3005.severity = warning + +# CA3006: Review code for process command injection vulnerabilities +dotnet_diagnostic.CA3006.severity = warning + +# CA3007: Review code for open redirect vulnerabilities +dotnet_diagnostic.CA3007.severity = warning + +# CA3008: Review code for XPath injection vulnerabilities +dotnet_diagnostic.CA3008.severity = warning + +# CA3009: Review code for XML injection vulnerabilities +dotnet_diagnostic.CA3009.severity = warning + +# CA3010: Review code for XAML injection vulnerabilities +dotnet_diagnostic.CA3010.severity = warning + +# CA3011: Review code for DLL injection vulnerabilities +dotnet_diagnostic.CA3011.severity = warning + +# CA3012: Review code for regex injection vulnerabilities +dotnet_diagnostic.CA3012.severity = warning + +# CA3061: Do Not Add Schema By URL +dotnet_diagnostic.CA3061.severity = warning + +# CA3075: Insecure DTD processing in XML +dotnet_diagnostic.CA3075.severity = warning + +# CA3076: Insecure XSLT script processing +dotnet_diagnostic.CA3076.severity = warning + +# CA3077: Insecure Processing in API Design, XmlDocument and XmlTextReader +dotnet_diagnostic.CA3077.severity = warning + +# CA3147: Mark Verb Handlers With Validate Antiforgery Token +dotnet_diagnostic.CA3147.severity = warning + +# CA5350: Do Not Use Weak Cryptographic Algorithms +dotnet_diagnostic.CA5350.severity = warning + +# CA5351: Do Not Use Broken Cryptographic Algorithms +dotnet_diagnostic.CA5351.severity = warning + +# CA5358: Review cipher mode usage with cryptography experts +dotnet_diagnostic.CA5358.severity = warning + +# CA5359: Do Not Disable Certificate Validation +dotnet_diagnostic.CA5359.severity = warning + +# CA5360: Do Not Call Dangerous Methods In Deserialization +dotnet_diagnostic.CA5360.severity = warning + +# CA5361: Do Not Disable SChannel Use of Strong Crypto +dotnet_diagnostic.CA5361.severity = warning + +# CA5362: Potential reference cycle in deserialized object graph +dotnet_diagnostic.CA5362.severity = warning + +# CA5363: Do Not Disable Request Validation +dotnet_diagnostic.CA5363.severity = warning + +# CA5364: Do Not Use Deprecated Security Protocols +dotnet_diagnostic.CA5364.severity = warning + +# CA5365: Do Not Disable HTTP Header Checking +dotnet_diagnostic.CA5365.severity = warning + +# CA5366: Use XmlReader for 'DataSet.ReadXml()' +dotnet_diagnostic.CA5366.severity = warning + +# CA5367: Do Not Serialize Types With Pointer Fields +dotnet_diagnostic.CA5367.severity = warning + +# CA5368: Set ViewStateUserKey For Classes Derived From Page +dotnet_diagnostic.CA5368.severity = warning + +# CA5369: Use XmlReader for 'XmlSerializer.Deserialize()' +dotnet_diagnostic.CA5369.severity = warning + +# CA5370: Use XmlReader for XmlValidatingReader constructor +dotnet_diagnostic.CA5370.severity = warning + +# CA5371: Use XmlReader for 'XmlSchema.Read()' +dotnet_diagnostic.CA5371.severity = warning + +# CA5372: Use XmlReader for XPathDocument constructor +dotnet_diagnostic.CA5372.severity = warning + +# CA5373: Do not use obsolete key derivation function +dotnet_diagnostic.CA5373.severity = warning + +# CA5374: Do Not Use XslTransform +dotnet_diagnostic.CA5374.severity = warning + +# CA5375: Do Not Use Account Shared Access Signature +dotnet_diagnostic.CA5375.severity = warning + +# CA5376: Use SharedAccessProtocol HttpsOnly +dotnet_diagnostic.CA5376.severity = warning + +# CA5377: Use Container Level Access Policy +dotnet_diagnostic.CA5377.severity = warning + +# CA5378: Do not disable ServicePointManagerSecurityProtocols +dotnet_diagnostic.CA5378.severity = warning + +# CA5379: Ensure Key Derivation Function algorithm is sufficiently strong +dotnet_diagnostic.CA5379.severity = warning + +# CA5380: Do Not Add Certificates To Root Store +dotnet_diagnostic.CA5380.severity = warning + +# CA5381: Ensure Certificates Are Not Added To Root Store +dotnet_diagnostic.CA5381.severity = warning + +# CA5382: Use Secure Cookies In ASP.NET Core +dotnet_diagnostic.CA5382.severity = warning + +# CA5383: Ensure Use Secure Cookies In ASP.NET Core +dotnet_diagnostic.CA5383.severity = warning + +# CA5384: Do Not Use Digital Signature Algorithm (DSA) +dotnet_diagnostic.CA5384.severity = warning + +# CA5385: Use Rivest-Shamir-Adleman (RSA) Algorithm With Sufficient Key Size +dotnet_diagnostic.CA5385.severity = warning + +# CA5386: Avoid hardcoding SecurityProtocolType value +dotnet_diagnostic.CA5386.severity = warning + +# CA5387: Do Not Use Weak Key Derivation Function With Insufficient Iteration Count +dotnet_diagnostic.CA5387.severity = warning + +# CA5388: Ensure Sufficient Iteration Count When Using Weak Key Derivation Function +dotnet_diagnostic.CA5388.severity = warning + +# CA5389: Do Not Add Archive Item's Path To The Target File System Path +dotnet_diagnostic.CA5389.severity = warning + +# CA5390: Do not hard-code encryption key +dotnet_diagnostic.CA5390.severity = warning + +# CA5391: Use antiforgery tokens in ASP.NET Core MVC controllers +dotnet_diagnostic.CA5391.severity = warning + +# CA5392: Use DefaultDllImportSearchPaths attribute for P/Invokes +dotnet_diagnostic.CA5392.severity = warning + +# CA5393: Do not use unsafe DllImportSearchPath value +dotnet_diagnostic.CA5393.severity = warning + +# CA5394: Do not use insecure randomness +dotnet_diagnostic.CA5394.severity = warning + +# CA5395: Miss HttpVerb attribute for action methods +dotnet_diagnostic.CA5395.severity = warning + +# CA5396: Set HttpOnly to true for HttpCookie +dotnet_diagnostic.CA5396.severity = warning + +# CA5397: Do not use deprecated SslProtocols values +dotnet_diagnostic.CA5397.severity = warning + +# CA5398: Avoid hardcoded SslProtocols values +dotnet_diagnostic.CA5398.severity = warning + +# CA5399: HttpClients should enable certificate revocation list checks +dotnet_diagnostic.CA5399.severity = warning + +# CA5400: Ensure HttpClient certificate revocation list check is not disabled +dotnet_diagnostic.CA5400.severity = warning + +# CA5401: Do not use CreateEncryptor with non-default IV +dotnet_diagnostic.CA5401.severity = warning + +# CA5402: Use CreateEncryptor with the default IV +dotnet_diagnostic.CA5402.severity = warning + +# CA5403: Do not hard-code certificate +dotnet_diagnostic.CA5403.severity = warning + +# CA5404: Do not disable token validation checks +dotnet_diagnostic.CA5404.severity = warning + +# CA5405: Do not always skip token validation in delegates +dotnet_diagnostic.CA5405.severity = warning + +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = warning + +# CA1200: Avoid using cref tags with a prefix +dotnet_diagnostic.CA1200.severity = warning + +# CA1309: Use ordinal string comparison +dotnet_diagnostic.CA1309.severity = warning + +# CA1311: Specify a culture or use an invariant version +dotnet_diagnostic.CA1311.severity = warning + +# CA1507: Use nameof to express symbol names +dotnet_diagnostic.CA1507.severity = warning + +# CA1508: Avoid dead conditional code +dotnet_diagnostic.CA1508.severity = warning + +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = warning + +# CA1805: Do not initialize unnecessarily +dotnet_diagnostic.CA1805.severity = warning + +# CA1812: Avoid uninstantiated internal classes +dotnet_diagnostic.CA1812.severity = warning + +# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute +dotnet_diagnostic.CA1824.severity = warning + +# CA1825: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = warning + +# CA1841: Prefer Dictionary.Contains methods +dotnet_diagnostic.CA1841.severity = warning + +# CA1845: Use span-based 'string.Concat' +dotnet_diagnostic.CA1845.severity = warning + +# CA1851: Possible multiple enumerations of 'IEnumerable' collection +dotnet_diagnostic.CA1851.severity = warning + +# CA1855: Prefer 'Clear' over 'Fill' +dotnet_diagnostic.CA1855.severity = warning + +# CA1856: Incorrect usage of ConstantExpected attribute +dotnet_diagnostic.CA1856.severity = warning + +# CA1857: A constant is expected for the parameter +dotnet_diagnostic.CA1857.severity = warning + +# CA1865: Use char overload +dotnet_diagnostic.CA1865.severity = warning + +# CA1866: Use char overload +dotnet_diagnostic.CA1866.severity = warning + +# CA1867: Use char overload +dotnet_diagnostic.CA1867.severity = warning + +# CA1870: Use a cached 'SearchValues' instance +dotnet_diagnostic.CA1870.severity = warning + +# CA2014: Do not use stackalloc in loops +dotnet_diagnostic.CA2014.severity = warning + +# CA2016: Forward the 'CancellationToken' parameter to methods +dotnet_diagnostic.CA2016.severity = warning + +# CA2020: Prevent behavioral change +dotnet_diagnostic.CA2020.severity = warning + +# CA2234: Pass system uri objects instead of strings +dotnet_diagnostic.CA2234.severity = warning + +# CA2252: This API requires opting into preview features +dotnet_diagnostic.CA2252.severity = warning + +# CA2260: Use correct type parameter +dotnet_diagnostic.CA2260.severity = warning + +# CA2352: Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks +dotnet_diagnostic.CA2352.severity = warning + +# CA2353: Unsafe DataSet or DataTable in serializable type +dotnet_diagnostic.CA2353.severity = warning + +# CA2354: Unsafe DataSet or DataTable in deserialized object graph can be vulnerable to remote code execution attacks +dotnet_diagnostic.CA2354.severity = warning + +# CA2355: Unsafe DataSet or DataTable type found in deserializable object graph +dotnet_diagnostic.CA2355.severity = warning + +# CA2356: Unsafe DataSet or DataTable type in web deserializable object graph +dotnet_diagnostic.CA2356.severity = warning + +# CA2362: Unsafe DataSet or DataTable in auto-generated serializable type can be vulnerable to remote code execution attacks +dotnet_diagnostic.CA2362.severity = warning diff --git a/FrostFS.SDK.sln b/FrostFS.SDK.sln index f0c0d87..9d9fbbe 100644 --- a/FrostFS.SDK.sln +++ b/FrostFS.SDK.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}" @@ -9,6 +11,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F030ACD-F87C-4E83-9A68-4CC5DF03AD90}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,4 +39,7 @@ Global {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection EndGlobal diff --git a/src/FrostFS.SDK.ClientV2/Exceptions/FrostFsException.cs b/src/FrostFS.SDK.ClientV2/Exceptions/FrostFsException.cs new file mode 100644 index 0000000..ba31b9d --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Exceptions/FrostFsException.cs @@ -0,0 +1,18 @@ +using System; + +namespace FrostFS.SDK.ClientV2; + +public class FrostFsException : Exception +{ + public FrostFsException() + { + } + + public FrostFsException(string message) : base(message) + { + } + + public FrostFsException(string message, Exception innerException) : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Exceptions/InvalidObjectException.cs b/src/FrostFS.SDK.ClientV2/Exceptions/InvalidObjectException.cs index f2a8ea7..c15e591 100644 --- a/src/FrostFS.SDK.ClientV2/Exceptions/InvalidObjectException.cs +++ b/src/FrostFS.SDK.ClientV2/Exceptions/InvalidObjectException.cs @@ -2,6 +2,17 @@ using System; namespace FrostFS.SDK.ClientV2; -public class InvalidObjectException() : Exception() +public class InvalidObjectException : Exception { + public InvalidObjectException() + { + } + + public InvalidObjectException(string message) : base(message) + { + } + + public InvalidObjectException(string message, Exception innerException) : base(message, innerException) + { + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs b/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs index ad3c63e..ce7f19a 100644 --- a/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs +++ b/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs @@ -2,7 +2,24 @@ using System; namespace FrostFS.SDK.ClientV2; -public class ResponseException(FrostFsResponseStatus status) : Exception() +public class ResponseException : Exception { - public FrostFsResponseStatus Status { get; set; } = status; + public FrostFsResponseStatus? Status { get; private set; } + + public ResponseException() + { + } + + public ResponseException(FrostFsResponseStatus status) + { + Status = status; + } + + public ResponseException(string message) : base(message) + { + } + + public ResponseException(string message, Exception innerException) : base(message, innerException) + { + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj index 5f423dd..b16e7e7 100644 --- a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj +++ b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj @@ -4,14 +4,23 @@ netstandard2.0 12.0 enable + AllEnabledByDefault - - true - + + true + + + + true + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/FrostFSClient.cs similarity index 79% rename from src/FrostFS.SDK.ClientV2/Client.cs rename to src/FrostFS.SDK.ClientV2/FrostFSClient.cs index 8209571..4a680de 100644 --- a/src/FrostFS.SDK.ClientV2/Client.cs +++ b/src/FrostFS.SDK.ClientV2/FrostFSClient.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; +using Frostfs.V2.Ape; +using Frostfs.V2.Apemanager; + using FrostFS.Container; using FrostFS.Netmap; using FrostFS.Object; @@ -15,35 +18,33 @@ using Grpc.Core.Interceptors; using Grpc.Net.Client; using Microsoft.Extensions.Options; -using Frostfs.V2.Apemanager; -using Frostfs.V2.Ape; namespace FrostFS.SDK.ClientV2; -public class Client : IFrostFSClient +public class FrostFSClient : IFrostFSClient { private bool isDisposed; - + internal ContainerService.ContainerServiceClient? ContainerServiceClient { get; set; } - + internal NetmapService.NetmapServiceClient? NetmapServiceClient { get; set; } internal APEManagerService.APEManagerServiceClient? ApeManagerServiceClient { get; set; } internal SessionService.SessionServiceClient? SessionServiceClient { get; set; } - + internal ObjectService.ObjectServiceClient? ObjectServiceClient { get; set; } internal ClientEnvironment ClientCtx { get; set; } public static IFrostFSClient GetInstance(IOptions clientOptions, GrpcChannelOptions? channelOptions = null) { - return new Client(clientOptions, channelOptions); + return new FrostFSClient(clientOptions, channelOptions); } public static IFrostFSClient GetSingleOwnerInstance(IOptions clientOptions, GrpcChannelOptions? channelOptions = null) { - return new Client(clientOptions, channelOptions); + return new FrostFSClient(clientOptions, channelOptions); } /// @@ -64,10 +65,15 @@ public class Client : IFrostFSClient ContainerService.ContainerServiceClient containerService, ObjectService.ObjectServiceClient objectService) { - return new Client(clientOptions, channelOptions, containerService, netmapService, sessionService, objectService); + if (clientOptions is null) + { + throw new ArgumentNullException(nameof(clientOptions)); + } + + return new FrostFSClient(clientOptions, channelOptions, containerService, netmapService, sessionService, objectService); } - private Client( + private FrostFSClient( IOptions settings, GrpcChannelOptions? channelOptions, ContainerService.ContainerServiceClient containerService, @@ -75,6 +81,11 @@ public class Client : IFrostFSClient SessionService.SessionServiceClient sessionService, ObjectService.ObjectServiceClient objectService) { + if (settings is null) + { + throw new ArgumentNullException(nameof(settings)); + } + var ecdsaKey = settings.Value.Key.LoadWif(); FrostFsOwner.FromKey(ecdsaKey); @@ -85,13 +96,13 @@ public class Client : IFrostFSClient channel: InitGrpcChannel(settings.Value.Host, channelOptions), version: new FrostFsVersion(2, 13)); - ContainerServiceClient = containerService; - NetmapServiceClient = netmapService; - SessionServiceClient = sessionService; - ObjectServiceClient = objectService; + ContainerServiceClient = containerService ?? throw new ArgumentNullException(nameof(containerService)); + NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService)); + SessionServiceClient = sessionService ?? throw new ArgumentNullException(nameof(sessionService)); + ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService)); } - private Client(IOptions options, GrpcChannelOptions? channelOptions) + private FrostFSClient(IOptions options, GrpcChannelOptions? channelOptions) { var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized"); @@ -110,14 +121,14 @@ public class Client : IFrostFSClient // CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) }); } - private Client(IOptions options, GrpcChannelOptions? channelOptions) + private FrostFSClient(IOptions options, GrpcChannelOptions? channelOptions) { var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized"); clientSettings.Validate(); var ecdsaKey = clientSettings.Key.LoadWif(); - + var channel = InitGrpcChannel(clientSettings.Host, channelOptions); ClientCtx = new ClientEnvironment( @@ -128,7 +139,7 @@ public class Client : IFrostFSClient version: new FrostFsVersion(2, 13)); // TODO: define timeout logic - CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20)}); + // CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) }); } public void Dispose() @@ -149,18 +160,31 @@ public class Client : IFrostFSClient #region ApeManagerImplementation public Task AddChainAsync(PrmApeChainAdd args) { + if (args is null) + { + throw new ArgumentNullException(nameof(args)); + } + var service = GetApeManagerService(args); return service.AddChainAsync(args); } public Task RemoveChainAsync(PrmApeChainRemove args) { + if (args is null) + { + throw new ArgumentNullException(nameof(args)); + } + var service = GetApeManagerService(args); return service.RemoveChainAsync(args); } public Task ListChainAsync(PrmApeChainList args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetApeManagerService(args); return service.ListChainAsync(args); } @@ -169,6 +193,9 @@ public class Client : IFrostFSClient #region ContainerImplementation public Task GetContainerAsync(PrmContainerGet args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetContainerService(args); return service.GetContainerAsync(args); } @@ -176,18 +203,24 @@ public class Client : IFrostFSClient public IAsyncEnumerable ListContainersAsync(PrmContainerGetAll? args = null) { args ??= new PrmContainerGetAll(); - var service = GetContainerService(args); + var service = GetContainerService(args); return service.ListContainersAsync(args); } public Task CreateContainerAsync(PrmContainerCreate args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetContainerService(args); return service.CreateContainerAsync(args); } public Task DeleteContainerAsync(PrmContainerDelete args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetContainerService(args); return service.DeleteContainerAsync(args); } @@ -219,36 +252,54 @@ public class Client : IFrostFSClient #region ObjectImplementation public Task GetObjectHeadAsync(PrmObjectHeadGet args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetObjectService(args); return service.GetObjectHeadAsync(args); } public Task GetObjectAsync(PrmObjectGet args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetObjectService(args); return service.GetObjectAsync(args); } public Task PutObjectAsync(PrmObjectPut args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetObjectService(args); return service.PutObjectAsync(args); } public Task PutSingleObjectAsync(PrmSingleObjectPut args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetObjectService(args); return service.PutSingleObjectAsync(args); } public Task DeleteObjectAsync(PrmObjectDelete args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetObjectService(args); return service.DeleteObjectAsync(args); } public IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetObjectService(args); return service.SearchObjectsAsync(args); } @@ -257,17 +308,23 @@ public class Client : IFrostFSClient #region SessionImplementation public async Task CreateSessionAsync(PrmSessionCreate args) { - var session = await CreateSessionInternalAsync(args); + if (args is null) + throw new ArgumentNullException(nameof(args)); + + var session = await CreateSessionInternalAsync(args).ConfigureAwait(false); var token = session.Serialize(); - + return new FrostFsSessionToken(token); } - internal Task CreateSessionInternalAsync(PrmSessionCreate args) + internal Task CreateSessionInternalAsync(PrmSessionCreate args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + var service = GetSessionService(args); return service.CreateSessionAsync(args); - } + } #endregion #region ToolsImplementation @@ -283,20 +340,24 @@ public class Client : IFrostFSClient private async void CheckFrostFsVersionSupport(Context? ctx = default) { var args = new PrmNodeInfo { Context = ctx }; + + if (ctx?.Version == null) + throw new InvalidObjectException(nameof(ctx.Version)); + var service = GetNetmapService(args); - var localNodeInfo = await service.GetLocalNodeInfoAsync(args); - - if (!localNodeInfo.Version.IsSupported(args.Context!.Version)) + var localNodeInfo = await service.GetLocalNodeInfoAsync(args).ConfigureAwait(false); + + if (!localNodeInfo.Version.IsSupported(ctx.Version)) { var msg = $"FrostFS {localNodeInfo.Version} is not supported."; - throw new ApplicationException(msg); + throw new FrostFsException(msg); } } private CallInvoker? SetupEnvironment(IContext ctx) { if (isDisposed) - throw new Exception("Client is disposed."); + throw new InvalidObjectException("Client is disposed."); ctx.Context ??= new Context(); @@ -304,14 +365,14 @@ public class Client : IFrostFSClient { if (ClientCtx.Key == null) { - throw new Exception("Key is not initialized."); + throw new InvalidObjectException("Key is not initialized."); } ctx.Context.Key = ClientCtx.Key.ECDsaKey; } if (ctx.Context.OwnerId == null) - { + { ctx.Context.OwnerId = ClientCtx.Owner ?? FrostFsOwner.FromKey(ctx.Context.Key); } @@ -319,7 +380,7 @@ public class Client : IFrostFSClient { if (ClientCtx.Version == null) { - throw new Exception("Version is not initialized."); + throw new InvalidObjectException("Version is not initialized."); } ctx.Context.Version = ClientCtx.Version; @@ -336,7 +397,7 @@ public class Client : IFrostFSClient if (ctx.Context.Callback != null) callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Context.Callback)); - + return callInvoker; CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor) @@ -382,7 +443,7 @@ public class Client : IFrostFSClient private ContainerServiceProvider GetContainerService(IContext ctx) { - var callInvoker = SetupEnvironment(ctx); + var callInvoker = SetupEnvironment(ctx); var client = ContainerServiceClient ?? (callInvoker != null ? new ContainerService.ContainerServiceClient(callInvoker) : new ContainerService.ContainerServiceClient(ClientCtx.Channel)); @@ -408,7 +469,7 @@ public class Client : IFrostFSClient if (channelOptions != null) return GrpcChannel.ForAddress(uri, channelOptions); - + return GrpcChannel.ForAddress(uri, new GrpcChannelOptions { HttpHandler = new HttpClientHandler() diff --git a/src/FrostFS.SDK.ClientV2/Interceptors/MetricsInterceptor.cs b/src/FrostFS.SDK.ClientV2/Interceptors/MetricsInterceptor.cs index dc439f9..9b3199c 100644 --- a/src/FrostFS.SDK.ClientV2/Interceptors/MetricsInterceptor.cs +++ b/src/FrostFS.SDK.ClientV2/Interceptors/MetricsInterceptor.cs @@ -14,6 +14,11 @@ public class MetricsInterceptor(Action callback) : Interceptor ClientInterceptorContext context, AsyncUnaryCallContinuation continuation) { + if (continuation is null) + { + throw new ArgumentNullException(nameof(continuation)); + } + var call = continuation(request, context); return new AsyncUnaryCall( @@ -28,7 +33,10 @@ public class MetricsInterceptor(Action callback) : Interceptor ClientInterceptorContext context, AsyncClientStreamingCallContinuation continuation) { - var call = continuation(context); + if (continuation is null) + throw new ArgumentNullException(nameof(continuation)); + + var call = continuation(context); return new AsyncClientStreamingCall( call.RequestStream, @@ -43,31 +51,31 @@ public class MetricsInterceptor(Action callback) : Interceptor { var watch = new Stopwatch(); watch.Start(); - - var response = await call.ResponseAsync; + + var response = await call.ResponseAsync.ConfigureAwait(false); watch.Stop(); - var elapsed = watch.ElapsedTicks * 1_000_000/Stopwatch.Frequency; + var elapsed = watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency; callback(new CallStatistics { MethodName = call.ToString(), ElapsedMicroSeconds = elapsed }); - return response; + return response; } private async Task HandleStreamResponse(AsyncClientStreamingCall call) { var watch = new Stopwatch(); watch.Start(); - - var response = await call.ResponseAsync; + + var response = await call.ResponseAsync.ConfigureAwait(false); watch.Stop(); - var elapsed = watch.ElapsedTicks * 1_000_000/Stopwatch.Frequency; + var elapsed = watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency; callback(new CallStatistics { MethodName = call.ToString(), ElapsedMicroSeconds = elapsed }); - return response; + return response; } } diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs index a580149..3af0c0b 100644 --- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs @@ -36,21 +36,21 @@ public interface IFrostFSClient : IDisposable Task DeleteContainerAsync(PrmContainerDelete args); #endregion - + #region Object Task GetObjectHeadAsync(PrmObjectHeadGet args); Task GetObjectAsync(PrmObjectGet args); Task PutObjectAsync(PrmObjectPut args); - + Task PutSingleObjectAsync(PrmSingleObjectPut args); Task DeleteObjectAsync(PrmObjectDelete args); IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args); #endregion - + #region Tools FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, Context ctx); #endregion diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Container.cs b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs index 181de40..e631472 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Container.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs @@ -9,14 +9,12 @@ public static class ContainerMapper { public static FrostFsContainerInfo ToModel(this Container.Container container) { - if (!Enum.IsDefined(typeof(BasicAcl),(int)container.BasicAcl)) - throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'."); - - BasicAcl acl = (BasicAcl)container.BasicAcl; + if (container == null) + throw new ArgumentNullException(nameof(container)); - return new FrostFsContainerInfo(acl, + return new FrostFsContainerInfo( container.PlacementPolicy.ToModel(), - container.Attributes?.Select(a => new FrostFsAttribute(a.Key, a.Value)).ToList(), + container.Attributes?.Select(a => new FrostFsAttributePair(a.Key, a.Value)).ToArray(), container.Version?.ToModel(), container.OwnerId?.ToModel(), container.Nonce?.ToUuid()); diff --git a/src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs index f5feccc..86449e2 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs @@ -17,19 +17,23 @@ public static class ContainerIdMapper public static ContainerID ToMessage(this FrostFsContainerId model) { - if (model.Value == null) + if (model is null) + { throw new ArgumentNullException(nameof(model)); - - if (!Cache.Containers.TryGetValue(model.Value, out ContainerID? message)) + } + + var containerId = model.GetValue() ?? throw new ArgumentNullException(nameof(model)); + + if (!Cache.Containers.TryGetValue(containerId, out ContainerID? message)) { message = new ContainerID { - Value = ByteString.CopyFrom(Base58.Decode(model.Value)) + Value = ByteString.CopyFrom(Base58.Decode(containerId)) }; - Cache.Containers.Set(model.Value, message, _oneHourExpiration); + Cache.Containers.Set(containerId, message, _oneHourExpiration); } - return message!; + return message!; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs b/src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs index 6b4901c..5bf994e 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs @@ -1,3 +1,5 @@ +using System; + using FrostFS.Session; namespace FrostFS.SDK.ClientV2.Mappers.GRPC; @@ -6,11 +8,16 @@ public static class MetaHeaderMapper { public static RequestMetaHeader ToMessage(this MetaHeader metaHeader) { + if (metaHeader is null) + { + throw new ArgumentNullException(nameof(metaHeader)); + } + return new RequestMetaHeader { Version = metaHeader.Version.ToMessage(), Epoch = (uint)metaHeader.Epoch, Ttl = (uint)metaHeader.Ttl }; - } + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs index 31a8a0f..6729501 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using FrostFS.Netmap; @@ -8,6 +9,11 @@ public static class NetmapMapper { public static FrostFsNetmapSnapshot ToModel(this NetmapSnapshotResponse netmap) { + if (netmap is null) + { + throw new ArgumentNullException(nameof(netmap)); + } + return new FrostFsNetmapSnapshot( netmap.Body.Netmap.Epoch, netmap.Body.Netmap.Nodes diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs index 86e5ff0..d882127 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs @@ -10,11 +10,21 @@ public static class NodeInfoMapper { public static FrostFsNodeInfo ToModel(this LocalNodeInfoResponse.Types.Body node) { + if (node is null) + { + throw new ArgumentNullException(nameof(node)); + } + return node.NodeInfo.ToModel(node.Version); } public static FrostFsNodeInfo ToModel(this NodeInfo nodeInfo, Refs.Version version) { + if (nodeInfo is null) + { + throw new ArgumentNullException(nameof(nodeInfo)); + } + NodeState state = nodeInfo.State switch { NodeInfo.Types.State.Unspecified => NodeState.Unspecified, diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs index 2f66ed8..c44a397 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using FrostFS.Netmap; @@ -6,26 +7,13 @@ namespace FrostFS.SDK.ClientV2; public static class PlacementPolicyMapper { - public static PlacementPolicy ToMessage(this FrostFsPlacementPolicy placementPolicy) - { - var pp = new PlacementPolicy - { - Filters = { }, - Selectors = { }, - Replicas = { }, - Unique = placementPolicy.Unique - }; - - foreach (var replica in placementPolicy.Replicas) - { - pp.Replicas.Add(replica.ToMessage()); - } - - return pp; - } - public static FrostFsPlacementPolicy ToModel(this PlacementPolicy placementPolicy) { + if (placementPolicy is null) + { + throw new ArgumentNullException(nameof(placementPolicy)); + } + return new FrostFsPlacementPolicy( placementPolicy.Unique, placementPolicy.Replicas.Select(replica => replica.ToModel()).ToArray() diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs index 422891d..d9ea0cd 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs @@ -1,3 +1,5 @@ +using System; + using FrostFS.Netmap; namespace FrostFS.SDK.ClientV2; @@ -15,6 +17,11 @@ public static class ReplicaMapper public static FrostFsReplica ToModel(this Replica replica) { + if (replica is null) + { + throw new ArgumentNullException(nameof(replica)); + } + return new FrostFsReplica((int)replica.Count, replica.Selector); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs index 0e10ffe..d9f5d13 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs @@ -8,5 +8,5 @@ internal static class ObjectMapper { ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.ToByteArray()) }; - } + } } diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs index de596ad..85c7c5a 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs @@ -1,11 +1,18 @@ +using System; + using FrostFS.Object; namespace FrostFS.SDK.ClientV2.Mappers.GRPC; public static class ObjectAttributeMapper { - public static Header.Types.Attribute ToMessage(this FrostFsAttribute attribute) + public static Header.Types.Attribute ToMessage(this FrostFsAttributePair attribute) { + if (attribute is null) + { + throw new ArgumentNullException(nameof(attribute)); + } + return new Header.Types.Attribute { Key = attribute.Key, @@ -13,8 +20,13 @@ public static class ObjectAttributeMapper }; } - public static FrostFsAttribute ToModel(this Header.Types.Attribute attribute) + public static FrostFsAttributePair ToModel(this Header.Types.Attribute attribute) { - return new FrostFsAttribute(attribute.Key, attribute.Value); + if (attribute is null) + { + throw new ArgumentNullException(nameof(attribute)); + } + + return new FrostFsAttributePair(attribute.Key, attribute.Value); } } diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs index 0303213..76d1c92 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs @@ -8,6 +8,11 @@ public static class ObjectFilterMapper { public static SearchRequest.Types.Body.Types.Filter ToMessage(this IObjectFilter filter) { + if (filter is null) + { + throw new ArgumentNullException(nameof(filter)); + } + var objMatchTypeName = filter.MatchType switch { FrostFsMatchType.Unspecified => MatchType.Unspecified, @@ -25,5 +30,5 @@ public static class ObjectFilterMapper Key = filter.Key, Value = filter.GetSerializedValue() }; - } + } } diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs index fb4c77d..bdeb862 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.ObjectModel; using System.Linq; using FrostFS.Object; @@ -10,6 +11,11 @@ public static class ObjectHeaderMapper { public static FrostFsObjectHeader ToModel(this Header header) { + if (header is null) + { + throw new ArgumentNullException(nameof(header)); + } + var objTypeName = header.ObjectType switch { ObjectType.Regular => FrostFsObjectType.Regular, @@ -22,15 +28,15 @@ public static class ObjectHeaderMapper if (header.Split != null) { - split = new FrostFsSplit(new SplitId(header.Split.SplitId.ToUuid())) - { - Parent = header.Split.Parent?.ToModel(), - ParentHeader = header.Split.ParentHeader?.ToModel(), - Previous = header.Split.Previous?.ToModel() - }; + var children = header.Split.Children.Count != 0 ? new ReadOnlyCollection( + header.Split.Children.Select(x => x.ToModel()).ToList()) : null; - if (header.Split.Children.Count != 0) - split.Children.AddRange(header.Split.Children.Select(x => x.ToModel())); + split = new FrostFsSplit(new SplitId(header.Split.SplitId.ToUuid()), + header.Split.Previous?.ToModel(), + header.Split.Parent?.ToModel(), + header.Split.ParentHeader?.ToModel(), + null, + children); } var model = new FrostFsObjectHeader( @@ -40,10 +46,10 @@ public static class ObjectHeaderMapper split, header.OwnerId.ToModel(), header.Version.ToModel()) - { + { PayloadLength = header.PayloadLength, }; return model; - } + } } diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs index d20a915..10f8409 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs @@ -1,3 +1,5 @@ +using System; + using FrostFS.Refs; using Google.Protobuf; @@ -8,6 +10,11 @@ public static class ObjectIdMapper { public static ObjectID ToMessage(this FrostFsObjectId objectId) { + if (objectId is null) + { + throw new ArgumentNullException(nameof(objectId)); + } + return new ObjectID { Value = ByteString.CopyFrom(objectId.ToHash()) @@ -16,6 +23,11 @@ public static class ObjectIdMapper public static FrostFsObjectId ToModel(this ObjectID objectId) { + if (objectId is null) + { + throw new ArgumentNullException(nameof(objectId)); + } + return FrostFsObjectId.FromHash(objectId.Value.ToByteArray()); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs index ff81321..6f3276d 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs @@ -17,6 +17,11 @@ public static class OwnerIdMapper public static OwnerID ToMessage(this FrostFsOwner model) { + if (model is null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!Cache.Owners.TryGetValue(model, out OwnerID? message)) { message = new OwnerID @@ -32,10 +37,15 @@ public static class OwnerIdMapper public static FrostFsOwner ToModel(this OwnerID message) { + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + if (!Cache.Owners.TryGetValue(message, out FrostFsOwner? model)) { model = new FrostFsOwner(Base58.Encode(message.Value.ToByteArray())); - + Cache.Owners.Set(message, model, _oneHourExpiration); } diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs index 97b1c69..9f4a36f 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs @@ -1,3 +1,5 @@ +using System; + using Google.Protobuf; namespace FrostFS.SDK.ClientV2; @@ -6,8 +8,13 @@ public static class SessionMapper { public static byte[] Serialize(this Session.SessionToken token) { + if (token is null) + { + throw new ArgumentNullException(nameof(token)); + } + byte[] bytes = new byte[token.CalculateSize()]; - CodedOutputStream stream = new(bytes); + using CodedOutputStream stream = new(bytes); token.WriteTo(stream); return bytes; diff --git a/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs index 0b280cf..d3bf988 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs @@ -8,13 +8,17 @@ public static class SignatureMapper { public static Refs.Signature ToMessage(this FrostFsSignature signature) { + if (signature is null) + { + throw new ArgumentNullException(nameof(signature)); + } + var scheme = signature.Scheme switch { SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256, SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect, SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512, - _ => throw new ArgumentException(message: $"Unexpected enum value: {signature.Scheme}", - paramName: nameof(signature.Scheme)) + _ => throw new ArgumentException(nameof(signature.Scheme), $"Unexpected enum value: {signature.Scheme}") }; return new Refs.Signature diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Status.cs b/src/FrostFS.SDK.ClientV2/Mappers/Status.cs index d3555ac..ad9ea7d 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Status.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Status.cs @@ -6,7 +6,7 @@ public static class StatusMapper { public static FrostFsResponseStatus ToModel(this Status.Status status) { - if (status is null) + if (status is null) return new FrostFsResponseStatus(FrostFsStatusCode.Success); var codeName = Enum.GetName(typeof(FrostFsStatusCode), status.Code); diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Version.cs b/src/FrostFS.SDK.ClientV2/Mappers/Version.cs index f17509a..d4cc254 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Version.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Version.cs @@ -9,10 +9,15 @@ public static class VersionMapper { private static readonly Hashtable _cacheMessages = []; private static readonly Hashtable _cacheModels = []; - private static SpinLock _spinlock = new(); + private static SpinLock _spinlock; public static Version ToMessage(this FrostFsVersion model) { + if (model is null) + { + throw new System.ArgumentNullException(nameof(model)); + } + var key = model.Major << 16 + model.Minor; if (!_cacheMessages.ContainsKey(key)) @@ -46,6 +51,11 @@ public static class VersionMapper public static FrostFsVersion ToModel(this Version message) { + if (message is null) + { + throw new System.ArgumentNullException(nameof(message)); + } + var key = (int)message.Major << 16 + (int)message.Minor; if (!_cacheModels.ContainsKey(key)) diff --git a/src/FrostFS.SDK.ClientV2/Models/Chain/ChainTarget.cs b/src/FrostFS.SDK.ClientV2/Models/Chain/ChainTarget.cs index 0d47993..e9ce527 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Chain/ChainTarget.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Chain/ChainTarget.cs @@ -4,7 +4,7 @@ using Frostfs.V2.Ape; namespace FrostFS.SDK.ClientV2 { - public struct FrostFsChainTarget(FrostFsTargetType type, string name) + public struct FrostFsChainTarget(FrostFsTargetType type, string name) : IEquatable { private ChainTarget? chainTarget; @@ -33,5 +33,31 @@ namespace FrostFS.SDK.ClientV2 _ => throw new ArgumentException("Unexpected value for TargetType", nameof(type)), }; } + + public override readonly bool Equals(object obj) + { + var target = (FrostFsChainTarget)obj; + return Equals(target); + } + + public override readonly int GetHashCode() + { + return $"{Name}{Type}".GetHashCode(); + } + + public static bool operator ==(FrostFsChainTarget left, FrostFsChainTarget right) + { + return left.Equals(right); + } + + public static bool operator !=(FrostFsChainTarget left, FrostFsChainTarget right) + { + return !(left == right); + } + + public readonly bool Equals(FrostFsChainTarget other) + { + return Type == other.Type && Name.Equals(other.Name, StringComparison.Ordinal); + } } } diff --git a/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsChain.cs b/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsChain.cs index 0c21521..d6b4b1d 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsChain.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsChain.cs @@ -2,7 +2,7 @@ namespace FrostFS.SDK.ClientV2 { - public struct FrostFsChain (byte[] raw) + public struct FrostFsChain(byte[] raw) : System.IEquatable { private ByteString? grpcRaw; @@ -12,5 +12,31 @@ namespace FrostFS.SDK.ClientV2 { return grpcRaw ??= ByteString.CopyFrom(Raw); } + + public override readonly bool Equals(object obj) + { + var chain = (FrostFsChain)obj; + return Equals(chain); + } + + public override readonly int GetHashCode() + { + return Raw.GetHashCode(); + } + + public static bool operator ==(FrostFsChain left, FrostFsChain right) + { + return left.Equals(right); + } + + public static bool operator !=(FrostFsChain left, FrostFsChain right) + { + return !(left == right); + } + + public readonly bool Equals(FrostFsChain other) + { + return Raw == other.Raw; + } } } diff --git a/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsTargetType.cs b/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsTargetType.cs index 56617d3..03b1521 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsTargetType.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Chain/FrostFsTargetType.cs @@ -1,6 +1,6 @@ namespace FrostFS.SDK.ClientV2 { - public enum FrostFsTargetType + public enum FrostFsTargetType { Undefined = 0, Namespace, diff --git a/src/FrostFS.SDK.ClientV2/Models/Client/ClientSettings.cs b/src/FrostFS.SDK.ClientV2/Models/Client/ClientSettings.cs index 650c7d6..b1faf79 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Client/ClientSettings.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Client/ClientSettings.cs @@ -1,5 +1,6 @@ using System; -using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; using System.Text; namespace FrostFS.SDK; @@ -17,18 +18,24 @@ public class ClientSettings ThrowException(errors); } - protected List? CheckFields() + protected Collection? CheckFields() { - List? errors = null; - if (string.IsNullOrWhiteSpace(Host)) - (errors ??= []).Add(string.Format(errorTemplate, nameof(Host))); + { + var error = string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Host)); + return new Collection([error]); + } - return errors; + return null; } - protected static void ThrowException(List errors) + protected static void ThrowException(Collection errors) { + if (errors is null) + { + throw new ArgumentNullException(nameof(errors)); + } + StringBuilder messages = new(); foreach (var error in errors) @@ -51,12 +58,12 @@ public class SingleOwnerClientSettings : ClientSettings ThrowException(errors); } - protected new List? CheckFields() + protected new Collection? CheckFields() { - List? errors = base.CheckFields(); + Collection? errors = base.CheckFields(); if (string.IsNullOrWhiteSpace(Key)) - (errors ??= []).Add(string.Format(errorTemplate, nameof(Key))); + (errors ??= []).Add(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Key))); return errors; } diff --git a/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerId.cs b/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerId.cs index 895f25a..1ebbe04 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerId.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerId.cs @@ -1,8 +1,7 @@ using FrostFS.Refs; -using FrostFS.SDK.Cryptography; - -using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; namespace FrostFS.SDK; @@ -21,21 +20,18 @@ public class FrostFsContainerId this.containerID = id; } - public string Value + public string GetValue() { - get + if (this.modelId != null) + return this.modelId; + + if (containerID != null) { - if (this.modelId != null) - return this.modelId; - - if (containerID != null) - { - this.modelId = Base58.Encode(containerID.Value.ToByteArray()); - return this.modelId; - } - - throw new InvalidObjectException(); + this.modelId = Base58.Encode(containerID.Value.ToByteArray()); + return this.modelId; } + + throw new InvalidObjectException(); } internal ContainerID ContainerID @@ -57,6 +53,6 @@ public class FrostFsContainerId public override string ToString() { - return Value; + return GetValue(); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerInfo.cs b/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerInfo.cs index 74d0e41..e7336b2 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerInfo.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Containers/FrostFsContainerInfo.cs @@ -1,10 +1,10 @@ using System; -using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using FrostFS.SDK.ClientV2; -using FrostFS.SDK.Cryptography; using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; using Google.Protobuf; @@ -12,61 +12,59 @@ namespace FrostFS.SDK; public class FrostFsContainerInfo { - private FrostFS.Container.Container.Types.Attribute[]? grpsAttributes; - private List? attributes; + private Container.Container.Types.Attribute[]? grpsAttributes; + private ReadOnlyCollection? attributes; private FrostFsPlacementPolicy? placementPolicy; private Guid? nonce; - private Container.Container container; + private Container.Container? container; public FrostFsContainerInfo( - BasicAcl basicAcl, FrostFsPlacementPolicy placementPolicy, - List? attributes = null, + FrostFsAttributePair[]? attributes = null, FrostFsVersion? version = null, FrostFsOwner? owner = null, Guid? nonce = null) { - BasicAcl = basicAcl; this.placementPolicy = placementPolicy; - this.attributes = attributes; Version = version; Owner = owner; this.nonce = nonce; + + if (attributes != null) + this.attributes = new ReadOnlyCollection(attributes); } internal FrostFsContainerInfo(Container.Container container) { this.container = container; } - - public Guid Nonce + + public Guid Nonce { get { nonce ??= container?.Nonce != null ? container.Nonce.ToUuid() : Guid.NewGuid(); return nonce.Value; } - } - - public BasicAcl BasicAcl { get; private set; } + } public FrostFsPlacementPolicy? PlacementPolicy { get { - placementPolicy ??= container.PlacementPolicy?.ToModel(); + placementPolicy ??= container?.PlacementPolicy?.ToModel(); return placementPolicy; } } - public List? Attributes + public ReadOnlyCollection? Attributes { get { if (attributes == null && grpsAttributes != null) - attributes = grpsAttributes.Select(a => new FrostFsAttribute(a.Key, a.Value)).ToList(); - + attributes = new ReadOnlyCollection(grpsAttributes.Select(a => new FrostFsAttributePair(a.Key, a.Value)).ToList()); + return attributes; } } @@ -74,8 +72,8 @@ public class FrostFsContainerInfo public FrostFsVersion? Version { get; private set; } public FrostFsOwner? Owner { get; private set; } - - internal Container.Container.Types.Attribute[]? GetGrpsAttributes() + + internal Container.Container.Types.Attribute[]? GetGrpsAttributes() { grpsAttributes ??= Attributes? .Select(a => new Container.Container.Types.Attribute { Key = a.Key, Value = a.Value }) @@ -88,10 +86,14 @@ public class FrostFsContainerInfo { if (this.container == null) { + if (PlacementPolicy == null) + { + throw new InvalidObjectException("PlacementPolicy is null"); + } + this.container = new Container.Container() { - BasicAcl = (uint)BasicAcl, - PlacementPolicy = PlacementPolicy.ToMessage(), + PlacementPolicy = PlacementPolicy.Value.GetPolicy(), Nonce = ByteString.CopyFrom(Nonce.ToBytes()), OwnerId = Owner?.OwnerID, Version = Version?.Version diff --git a/src/FrostFS.SDK.ClientV2/Models/Enums/BasicAcl.cs b/src/FrostFS.SDK.ClientV2/Models/Enums/BasicAcl.cs deleted file mode 100644 index dd3252b..0000000 --- a/src/FrostFS.SDK.ClientV2/Models/Enums/BasicAcl.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel; - -namespace FrostFS.SDK; - -public enum BasicAcl -{ - [Description("Not defined ACL")] - NotDefined = 0x00000000, - - [Description("Basic ACL for private container")] - Private = 0x1C8C8CCC, - - [Description("Basic ACL for public RO container")] - PublicRO = 0x1FBF8CFF, - - [Description("Basic ACL for public RW container")] - PublicRW = 0x1FBFBFFF, - - [Description("Basic ACL for public append container")] - PublicAppend = 0x1FBF9FFF, -} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Models/Misc/CheckSum.cs b/src/FrostFS.SDK.ClientV2/Models/Misc/CheckSum.cs index 82017f9..fdeac99 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Misc/CheckSum.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Misc/CheckSum.cs @@ -1,20 +1,21 @@ -using FrostFS.SDK.Cryptography; using System; +using FrostFS.SDK.Cryptography; + namespace FrostFS.SDK; public class CheckSum { - // type is always Sha256 - public byte[]? Hash { get; set; } + private byte[]? hash; + private string? text; public static CheckSum CreateCheckSum(byte[] content) { - return new CheckSum { Hash = content.Sha256() }; + return new CheckSum { hash = content.Sha256() }; } public override string ToString() { - return BitConverter.ToString(Hash).Replace("-", ""); + return text ??= BitConverter.ToString(hash).Replace("-", ""); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Models/Misc/Constants.cs b/src/FrostFS.SDK.ClientV2/Models/Misc/Constants.cs index 332a3e3..28c8cf0 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Misc/Constants.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Misc/Constants.cs @@ -1,8 +1,8 @@ namespace FrostFS.SDK; -public class Constants +public static class Constants { - public const int ObjectChunkSize = 3 * (1 << 20); + public const int ObjectChunkSize = 3 * (1 << 20); public const int Sha256HashLength = 32; // HeaderPrefix is a prefix of key to object header value or property. diff --git a/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsPlacementPolicy.cs b/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsPlacementPolicy.cs index 410598c..62f7fb2 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsPlacementPolicy.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsPlacementPolicy.cs @@ -1,28 +1,89 @@ + using System; using System.Linq; +using FrostFS.Netmap; +using FrostFS.SDK.ClientV2; + namespace FrostFS.SDK; -public class FrostFsPlacementPolicy(bool unique, params FrostFsReplica[] replicas) : IComparable +public struct FrostFsPlacementPolicy(bool unique, params FrostFsReplica[] replicas) + : IEquatable { + private PlacementPolicy policy; + public FrostFsReplica[] Replicas { get; private set; } = replicas; public bool Unique { get; private set; } = unique; - public int CompareTo(FrostFsPlacementPolicy other) + public override readonly bool Equals(object obj) { - var notEqual = other == null - || Unique != other.Unique - || Replicas.Length != other.Replicas.Length; + if (obj is null) + return false; + + var other = (FrostFsPlacementPolicy)obj; + + return Equals(other); + } + + public PlacementPolicy GetPolicy() + { + if (policy == null) + { + policy = new PlacementPolicy + { + Filters = { }, + Selectors = { }, + Replicas = { }, + Unique = Unique + }; + + foreach (var replica in Replicas) + { + policy.Replicas.Add(replica.ToMessage()); + } + } + + return policy; + } + + //public static FrostFsPlacementPolicy ToModel(placementPolicy) + //{ + // return new FrostFsPlacementPolicy( + // placementPolicy.Unique, + // placementPolicy.Replicas.Select(replica => replica.ToModel()).ToArray() + // ); + //} + + + public override readonly int GetHashCode() + { + return Unique ? 17 : 0 + Replicas.GetHashCode(); + } + + public static bool operator ==(FrostFsPlacementPolicy left, FrostFsPlacementPolicy right) + { + return left.Equals(right); + } + + public static bool operator !=(FrostFsPlacementPolicy left, FrostFsPlacementPolicy right) + { + return !(left == right); + } + + public readonly bool Equals(FrostFsPlacementPolicy other) + { + var notEqual = Unique != other.Unique + || Replicas.Length != other.Replicas.Length; if (notEqual) - return 1; + return false; foreach (var replica in Replicas) { - if (!other!.Replicas.Any(r => r.Count == replica.Count && r.Selector == replica.Selector)) - return 1; + if (!other.Replicas.Any(r => r.Equals(replica))) + return false; } - return 0; + return true; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsReplica.cs b/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsReplica.cs index 8fd1bd9..fa2f8db 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsReplica.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsReplica.cs @@ -1,6 +1,8 @@ +using System; + namespace FrostFS.SDK; -public class FrostFsReplica +public struct FrostFsReplica : IEquatable { public int Count { get; set; } public string Selector { get; set; } @@ -12,4 +14,34 @@ public class FrostFsReplica Count = count; Selector = selector; } + + public override readonly bool Equals(object obj) + { + if (obj is null) + return false; + + var other = (FrostFsReplica)obj; + + return Count == other.Count && Selector == other.Selector; + } + + public override readonly int GetHashCode() + { + return Count + Selector.GetHashCode(); + } + + public static bool operator ==(FrostFsReplica left, FrostFsReplica right) + { + return left.Equals(right); + } + + public static bool operator !=(FrostFsReplica left, FrostFsReplica right) + { + return !(left == right); + } + + public readonly bool Equals(FrostFsReplica other) + { + return Count == other.Count && Selector == other.Selector; + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsVersion.cs b/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsVersion.cs index da66403..e63d9ac 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsVersion.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Netmap/FrostFsVersion.cs @@ -5,7 +5,7 @@ namespace FrostFS.SDK; public class FrostFsVersion(int major, int minor) { - public Version version; + private Version? version; public int Major { get; set; } = major; public int Minor { get; set; } = minor; @@ -21,6 +21,11 @@ public class FrostFsVersion(int major, int minor) public bool IsSupported(FrostFsVersion version) { + if (version is null) + { + throw new System.ArgumentNullException(nameof(version)); + } + return Major == version.Major; } diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsAttribute.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsAttributePair.cs similarity index 66% rename from src/FrostFS.SDK.ClientV2/Models/Object/FrostFsAttribute.cs rename to src/FrostFS.SDK.ClientV2/Models/Object/FrostFsAttributePair.cs index 8a69fd4..64d865f 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsAttribute.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsAttributePair.cs @@ -1,6 +1,6 @@ namespace FrostFS.SDK; -public class FrostFsAttribute(string key, string value) +public class FrostFsAttributePair(string key, string value) { public string Key { get; set; } = key; diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsLinkObject.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsLinkObject.cs index 5b9e349..f43c6d0 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsLinkObject.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsLinkObject.cs @@ -1,13 +1,21 @@ -namespace FrostFS.SDK; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace FrostFS.SDK; public class FrostFsLinkObject : FrostFsObject { - public FrostFsLinkObject(FrostFsContainerId containerId, SplitId splitId, FrostFsObjectHeader largeObjectHeader) + public FrostFsLinkObject(FrostFsContainerId containerId, + SplitId splitId, + FrostFsObjectHeader largeObjectHeader, + IList children) : base(containerId) { - Header!.Split = new FrostFsSplit(splitId) - { - ParentHeader = largeObjectHeader - }; + Header!.Split = new FrostFsSplit(splitId, + null, + null, + largeObjectHeader, + null, + new ReadOnlyCollection(children)); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObject.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObject.cs index 5ee8b8e..a6c82db 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObject.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObject.cs @@ -1,9 +1,11 @@ -using System; +using FrostFS.SDK.ClientV2; namespace FrostFS.SDK; public class FrostFsObject { + private byte[]? bytes; + /// /// Creates new instance from ObjectHeader /// @@ -37,19 +39,27 @@ public class FrostFsObject get; set; } - /// - /// The size of payload cannot exceed MaxObjectSize value from NetworkSettings - /// Used only for PutSingleObject method - /// - /// Buffer for output data - public byte[] Payload { get; set; } = []; - /// /// A payload is obtained via stream reader /// /// Reader for received data public IObjectReader? ObjectReader { get; set; } + internal byte[] SingleObjectPayload + { + get { return bytes ?? []; } + } + + /// + /// The size of payload cannot exceed MaxObjectSize value from NetworkSettings + /// Used only for PutSingleObject method + /// + /// Buffer for output data + public void SetSingleObjectPayload(byte[] bytes) + { + this.bytes = bytes; + } + /// /// Applied only for the last Object in chain in case of manual multipart uploading /// @@ -57,7 +67,7 @@ public class FrostFsObject public void SetParent(FrostFsObjectHeader largeObjectHeader) { if (Header?.Split == null) - throw new Exception("The object is not initialized properly"); + throw new InvalidObjectException("The object is not initialized properly"); Header.Split.ParentHeader = largeObjectHeader; } diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectFilter.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectFilter.cs index feae9bd..f2f21d7 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectFilter.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectFilter.cs @@ -27,7 +27,7 @@ public abstract class FrostFsObjectFilter(FrostFsMatchType matchType, string /// Match type /// Attribute key /// Attribute value -public class FilterByAttribute(FrostFsMatchType matchType, string key, string value) : FrostFsObjectFilter(matchType, key, value) { } +public class FilterByAttributePair(FrostFsMatchType matchType, string key, string value) : FrostFsObjectFilter(matchType, key, value) { } /// /// Creates filter to search by ObjectId diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectHeader.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectHeader.cs index e384ece..ad8887f 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectHeader.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectHeader.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using FrostFS.Object; @@ -11,15 +11,17 @@ namespace FrostFS.SDK; public class FrostFsObjectHeader( FrostFsContainerId containerId, FrostFsObjectType type = FrostFsObjectType.Regular, - FrostFsAttribute[]? attributes = null, + FrostFsAttributePair[]? attributes = null, FrostFsSplit? split = null, FrostFsOwner? owner = null, FrostFsVersion? version = null) { - private Header header; + private Header? header; private Container.Container.Types.Attribute[]? grpsAttributes; - - public List Attributes { get; internal set; } = attributes != null ? [.. attributes] : []; + + public ReadOnlyCollection? Attributes { get; internal set; } = + attributes == null ? null : + new ReadOnlyCollection(attributes); public FrostFsContainerId ContainerId { get; } = containerId; @@ -65,9 +67,12 @@ public class FrostFsObjectHeader( PayloadLength = PayloadLength }; - foreach (var attribute in Attributes) + if (Attributes != null) { - this.header.Attributes.Add(attribute.ToMessage()); + foreach (var attribute in Attributes) + { + this.header.Attributes.Add(attribute.ToMessage()); + } } var split = Split; diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectId.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectId.cs index 4e4dbf0..49fde5a 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectId.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsObjectId.cs @@ -10,6 +10,11 @@ public class FrostFsObjectId(string id) public static FrostFsObjectId FromHash(byte[] hash) { + if (hash is null) + { + throw new ArgumentNullException(nameof(hash)); + } + if (hash.Length != Constants.Sha256HashLength) throw new FormatException("ObjectID must be a sha256 hash."); diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsOwner.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsOwner.cs index d249d80..0a24164 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsOwner.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsOwner.cs @@ -8,7 +8,7 @@ namespace FrostFS.SDK; public class FrostFsOwner(string id) { - private OwnerID ownerID; + private OwnerID? ownerID; public string Value { get; } = id; @@ -17,9 +17,9 @@ public class FrostFsOwner(string id) return new FrostFsOwner(key.PublicKey().PublicKeyToAddress()); } - internal OwnerID OwnerID + internal OwnerID OwnerID { - get + get { ownerID ??= this.ToMessage(); return ownerID; diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsSplit.cs b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsSplit.cs index 947662f..64e68e2 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsSplit.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/FrostFsSplit.cs @@ -1,8 +1,13 @@ -using System.Collections.Generic; +using System.Collections.ObjectModel; namespace FrostFS.SDK; -public class FrostFsSplit(SplitId splitId) +public class FrostFsSplit(SplitId splitId, + FrostFsObjectId? previous = null, + FrostFsObjectId? parent = null, + FrostFsObjectHeader? parentHeader = null, + FrostFsSignature? parentSignature = null, + ReadOnlyCollection? children = null) { public FrostFsSplit() : this(new SplitId()) { @@ -10,15 +15,14 @@ public class FrostFsSplit(SplitId splitId) public SplitId SplitId { get; private set; } = splitId; - public FrostFsObjectId? Parent { get; set; } + public FrostFsObjectId? Previous { get; } = previous; - public FrostFsObjectId? Previous { get; set; } + public FrostFsObjectId? Parent { get; } = parent; - public FrostFsSignature? ParentSignature { get; set; } + public FrostFsSignature? ParentSignature { get; } = parentSignature; - public FrostFsObjectHeader? ParentHeader { get; set; } + public FrostFsObjectHeader? ParentHeader { get; set; } = parentHeader; - public List Children { get; } = []; + public ReadOnlyCollection? Children { get; } = children; - public Refs.Signature ParentSignatureGrpc { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Models/Object/SplitId.cs b/src/FrostFS.SDK.ClientV2/Models/Object/SplitId.cs index bb8e617..177817e 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Object/SplitId.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Object/SplitId.cs @@ -1,35 +1,35 @@ -using FrostFS.SDK.Cryptography; +using System; + +using FrostFS.SDK.Cryptography; using Google.Protobuf; -using System; - namespace FrostFS.SDK; public class SplitId { private readonly Guid id; - private ByteString? _message; + private ByteString? message; public SplitId() { - id = Guid.NewGuid(); + this.id = Guid.NewGuid(); } - public SplitId(Guid guid) + public SplitId(Guid id) { - id = guid; + this.id = id; } private SplitId(byte[] binary) { - id = new Guid(binary); + this.id = new Guid(binary); } private SplitId(string str) { - id = new Guid(str); + this.id = new Guid(str); } public static SplitId CreateFromBinary(byte[] binaryData) @@ -44,19 +44,19 @@ public class SplitId public override string ToString() { - return id.ToString(); + return this.id.ToString(); } public byte[]? ToBinary() { - if (id == Guid.Empty) + if (this.id == Guid.Empty) return null; - return id.ToBytes(); + return this.id.ToBytes(); } public ByteString? GetSplitId() { - return _message ??= ByteString.CopyFrom(ToBinary()); + return this.message ??= ByteString.CopyFrom(ToBinary()); } } diff --git a/src/FrostFS.SDK.ClientV2/Models/Response/FrostFsSignature.cs b/src/FrostFS.SDK.ClientV2/Models/Response/FrostFsSignature.cs index d7ca6b6..995b161 100644 --- a/src/FrostFS.SDK.ClientV2/Models/Response/FrostFsSignature.cs +++ b/src/FrostFS.SDK.ClientV2/Models/Response/FrostFsSignature.cs @@ -1,6 +1,6 @@ namespace FrostFS.SDK; -public class FrostFsSignature +public class FrostFsSignature() { public byte[]? Key { get; set; } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/Context.cs b/src/FrostFS.SDK.ClientV2/Parameters/Context.cs index f1704a9..0e96929 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/Context.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/Context.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Security.Cryptography; using System.Threading; @@ -13,32 +13,32 @@ namespace FrostFS.SDK.ClientV2; public class Context() { - private List? interceptors; + private ReadOnlyCollection? interceptors; private ByteString? publicKeyCache; - public ECDsa Key { get; set; } + public ECDsa? Key { get; set; } - public FrostFsOwner OwnerId { get; set; } + public FrostFsOwner? OwnerId { get; set; } - public FrostFsVersion Version { get; set; } + public FrostFsVersion? Version { get; set; } + + public CancellationToken CancellationToken { get; set; } + + public TimeSpan Timeout { get; set; } - public CancellationToken CancellationToken { get; set; } = default; - - public TimeSpan Timeout { get; set; } = default; - public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null; public Action? Callback { get; set; } - public List Interceptors + public ReadOnlyCollection? Interceptors { - get { return this.interceptors ??= []; } + get { return this.interceptors; } set { this.interceptors = value; } } public ByteString? GetPublicKeyCache() - { + { if (publicKeyCache == null && Key != null) { publicKeyCache = ByteString.CopyFrom(Key.PublicKey()); diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs index 1aedefe..2a6e542 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmBase.cs @@ -2,12 +2,12 @@ namespace FrostFS.SDK.ClientV2; -public class PrmBase() : IContext +public class PrmBase(NameValueCollection? xheaders = null) : IContext { /// /// FrostFS request X-Headers /// - public NameValueCollection XHeaders { get; set; } = []; + public NameValueCollection XHeaders { get; } = xheaders ?? []; /// public Context? Context { get; set; } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs index 01f87a8..da4c50b 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerCreate.cs @@ -9,7 +9,7 @@ public sealed class PrmContainerCreate(FrostFsContainerInfo container) : PrmBase /// /// Rules for polling the result public PrmWait? WaitParams { get; set; } - + /// /// Blank session token /// diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs index f129109..6031ca6 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetmapSnapshot.cs @@ -1,5 +1,5 @@ namespace FrostFS.SDK.ClientV2; public sealed class PrmNetmapSnapshot() : PrmBase -{ +{ } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs index 3e82ff5..f014f14 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmNetworkSettings.cs @@ -1,5 +1,5 @@ namespace FrostFS.SDK.ClientV2; public sealed class PrmNetworkSettings() : PrmBase -{ +{ } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs index 7a3d1dc..05b541d 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmNodeInfo.cs @@ -1,5 +1,5 @@ namespace FrostFS.SDK.ClientV2; public sealed class PrmNodeInfo() : PrmBase -{ +{ } diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs index 67919aa..40410f3 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectHeadGet.cs @@ -3,7 +3,7 @@ public sealed class PrmObjectHeadGet(FrostFsContainerId containerId, FrostFsObjectId objectId) : PrmBase, ISessionToken { public FrostFsContainerId ContainerId { get; set; } = containerId; - + public FrostFsObjectId ObjectId { get; set; } = objectId; /// diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs index 0be09e8..3d216d5 100644 --- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs +++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectPut.cs @@ -28,7 +28,7 @@ public sealed class PrmObjectPut : PrmBase, ISessionToken /// Overrides default size of the buffer for stream transferring. /// /// Size of the buffer - public int BufferMaxSize { get; set; } + public int BufferMaxSize { get; set; } /// /// Allows to define a buffer for chunks to manage by the memory allocation and releasing. @@ -40,7 +40,7 @@ public sealed class PrmObjectPut : PrmBase, ISessionToken internal int MaxObjectSizeCache { get; set; } - internal ulong CurrentStreamPosition { get; set; } = 0; + internal ulong CurrentStreamPosition { get; set; } - internal ulong FullLength { get; set; } = 0; + internal ulong FullLength { get; set; } } diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs index 1e0ef9b..c42b2ba 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs @@ -3,22 +3,22 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Threading.Tasks; -using FrostFS.SDK.ClientV2; using FrostFS.Container; +using FrostFS.Refs; +using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; -using FrostFS.Refs; using FrostFS.Session; namespace FrostFS.SDK.ClientV2; -internal class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientEnvironment context) : ContextAccessor(context), ISessionProvider +internal sealed class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientEnvironment context) : ContextAccessor(context), ISessionProvider { readonly SessionProvider sessions = new(context); public async ValueTask GetOrCreateSession(ISessionToken args, Context ctx) { - return await sessions.GetOrCreateSession(args, ctx); + return await sessions.GetOrCreateSession(args, ctx).ConfigureAwait(false); } internal async Task GetContainerAsync(PrmContainerGet args) @@ -35,22 +35,29 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient internal async IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args) { var ctx = args.Context!; + ctx.OwnerId ??= Context.Owner; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + if (ctx.OwnerId == null) + throw new InvalidObjectException(nameof(ctx.OwnerId)); var request = new ListRequest { - Body = new () + Body = new() { - OwnerId = ctx.OwnerId.ToMessage() + OwnerId = ctx.OwnerId.ToMessage() } }; - + request.AddMetaHeader(args.XHeaders); request.Sign(ctx.Key); var response = await service.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken); - + Verifier.CheckResponse(response); - + foreach (var cid in response.Body.ContainerIds) { yield return new FrostFsContainerId(Base58.Encode(cid.Value.ToByteArray())); @@ -63,42 +70,52 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient var grpcContainer = args.Container.GetContainer(); - grpcContainer.OwnerId ??= ctx.OwnerId.ToMessage(); - grpcContainer.Version ??= ctx.Version.ToMessage(); + grpcContainer.OwnerId ??= ctx.OwnerId?.ToMessage(); + grpcContainer.Version ??= ctx.Version?.ToMessage(); + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + if (grpcContainer.OwnerId == null) + throw new InvalidObjectException(nameof(grpcContainer.OwnerId)); + if (grpcContainer.Version == null) + throw new InvalidObjectException(nameof(grpcContainer.Version)); var request = new PutRequest { Body = new PutRequest.Types.Body { Container = grpcContainer, - Signature = ctx.Key.SignRFC6979(grpcContainer) + Signature = ctx.Key.SignRFC6979(grpcContainer) } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateContainerTokenContext( null, ContainerSessionContext.Types.Verb.Put, ctx.Key, - ctx.GetPublicKeyCache()); + ctx.GetPublicKeyCache()!); request.AddMetaHeader(args.XHeaders, sessionToken); request.Sign(ctx.Key); var response = await service.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken); - + Verifier.CheckResponse(response); - await WaitForContainer(WaitExpects.Exists, response.Body.ContainerId, args.WaitParams, ctx); - - return new FrostFsContainerId(response.Body.ContainerId); + await WaitForContainer(WaitExpects.Exists, response.Body.ContainerId, args.WaitParams, ctx).ConfigureAwait(false); + + return new FrostFsContainerId(response.Body.ContainerId); } internal async Task DeleteContainerAsync(PrmContainerDelete args) { var ctx = args.Context!; + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var request = new DeleteRequest { Body = new DeleteRequest.Types.Body @@ -108,13 +125,13 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateContainerTokenContext( request.Body.ContainerId, ContainerSessionContext.Types.Verb.Delete, ctx.Key, - ctx.GetPublicKeyCache()); + ctx.GetPublicKeyCache()!); request.AddMetaHeader(args.XHeaders, sessionToken); @@ -124,13 +141,17 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient Verifier.CheckResponse(response); - await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx); - + await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx) + .ConfigureAwait(false); + Verifier.CheckResponse(response); } private static GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders, Context ctx) { + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var request = new GetRequest { Body = new GetRequest.Types.Body @@ -146,7 +167,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient } private enum WaitExpects - { + { Exists, Removed } @@ -161,7 +182,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient Verifier.CheckResponse(response); } - await WaitFor(action, expect, waitParams); + await WaitFor(action, expect, waitParams).ConfigureAwait(false); } private static async Task WaitFor( @@ -176,7 +197,7 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient { try { - await action(); + await action().ConfigureAwait(false); if (expect == WaitExpects.Exists) return; @@ -184,20 +205,20 @@ internal class ContainerServiceProvider(ContainerService.ContainerServiceClient if (DateTime.UtcNow >= deadLine) throw new TimeoutException(); - await Task.Delay(waitParams.PollInterval); + await Task.Delay(waitParams.PollInterval).ConfigureAwait(false); } catch (ResponseException ex) { if (DateTime.UtcNow >= deadLine) throw new TimeoutException(); - if (ex.Status.Code != FrostFsStatusCode.ContainerNotFound) + if (ex.Status?.Code != FrostFsStatusCode.ContainerNotFound) throw; if (expect == WaitExpects.Removed) return; - await Task.Delay(waitParams.PollInterval); + await Task.Delay(waitParams.PollInterval).ConfigureAwait(false); } } } diff --git a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs index d932dfb..91cca73 100644 --- a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs @@ -8,12 +8,12 @@ using static FrostFS.Netmap.NetworkConfig.Types; namespace FrostFS.SDK.ClientV2; -internal class NetmapServiceProvider : ContextAccessor -{ +internal sealed class NetmapServiceProvider : ContextAccessor +{ private readonly NetmapService.NetmapServiceClient netmapServiceClient; - - internal NetmapServiceProvider(NetmapService.NetmapServiceClient netmapServiceClient, ClientEnvironment context) - : base(context) + + internal NetmapServiceProvider(NetmapService.NetmapServiceClient netmapServiceClient, ClientEnvironment context) + : base(context) { this.netmapServiceClient = netmapServiceClient; } @@ -23,7 +23,7 @@ internal class NetmapServiceProvider : ContextAccessor if (Context.NetworkSettings != null) return Context.NetworkSettings; - var info = await GetNetworkInfoAsync(ctx); + var info = await GetNetworkInfoAsync(ctx).ConfigureAwait(false); var settings = new NetworkSettings(); @@ -33,13 +33,18 @@ internal class NetmapServiceProvider : ContextAccessor } Context.NetworkSettings = settings; - + return settings; } internal async Task GetLocalNodeInfoAsync(PrmNodeInfo args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var request = new LocalNodeInfoRequest { Body = new LocalNodeInfoRequest.Types.Body { } @@ -47,7 +52,7 @@ internal class NetmapServiceProvider : ContextAccessor request.AddMetaHeader(args.XHeaders); request.Sign(ctx.Key); - + var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); Verifier.CheckResponse(response); @@ -57,12 +62,18 @@ internal class NetmapServiceProvider : ContextAccessor internal async Task GetNetworkInfoAsync(Context ctx) { - var request = new NetworkInfoRequest(); + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + + var request = new NetworkInfoRequest(); request.AddMetaHeader(null); request.Sign(ctx.Key); - - var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken) + .ConfigureAwait(false); Verifier.CheckResponse(response); @@ -72,12 +83,16 @@ internal class NetmapServiceProvider : ContextAccessor internal async Task GetNetmapSnapshotAsync(PrmNetmapSnapshot args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); var request = new NetmapSnapshotRequest(); request.AddMetaHeader(args.XHeaders); request.Sign(ctx.Key); - + var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken); Verifier.CheckResponse(response); @@ -95,12 +110,12 @@ internal class NetmapServiceProvider : ContextAccessor ulong val = 0; for (var i = bytes.Length - 1; i >= 0; i--) val = (val << 8) + bytes[i]; - + return val; } private static void SetNetworksParam(Parameter param, NetworkSettings settings) - { + { var key = Encoding.UTF8.GetString(param.Key.ToByteArray()); var valueBytes = param.Value.ToByteArray(); diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs index bf8acf2..dce9787 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs @@ -6,9 +6,8 @@ using System.Threading.Tasks; using FrostFS.Object; using FrostFS.Refs; -using FrostFS.SDK.ClientV2.Extensions; -using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using FrostFS.Session; @@ -16,19 +15,30 @@ using Google.Protobuf; namespace FrostFS.SDK.ClientV2; -internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientEnvironment env) - : ContextAccessor(env), ISessionProvider +internal sealed class ObjectServiceProvider : ContextAccessor, ISessionProvider { - readonly SessionProvider sessions = new (env); + private readonly SessionProvider sessions; + private ObjectService.ObjectServiceClient client; - public async ValueTask GetOrCreateSession(ISessionToken args, Context ctx) + internal ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientEnvironment env) + : base(env) { - return await sessions.GetOrCreateSession(args, ctx); + this.sessions = new(Context); + this.client = client; + } + + public async ValueTask GetOrCreateSession(ISessionToken args, Context ctx) + { + return await sessions.GetOrCreateSession(args, ctx).ConfigureAwait(false); } internal async Task GetObjectHeadAsync(PrmObjectHeadGet args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); var request = new HeadRequest { @@ -42,7 +52,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateObjectTokenContext( request.Body.Address, @@ -53,8 +63,8 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C request.Sign(ctx.Key); - var response = await client!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken); - + var response = await client!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken).ConfigureAwait(false); + Verifier.CheckResponse(response); return response.Body.Header.Header.ToModel(); @@ -63,7 +73,12 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C internal async Task GetObjectAsync(PrmObjectGet args) { var ctx = args.Context!; - + + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var request = new GetRequest { Body = new GetRequest.Types.Body @@ -76,7 +91,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateObjectTokenContext( request.Body.Address, @@ -87,12 +102,17 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C request.Sign(ctx.Key); - return await GetObject(request, ctx); + return await GetObject(request, ctx).ConfigureAwait(false); } internal async Task DeleteObjectAsync(PrmObjectDelete args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var request = new DeleteRequest { Body = new DeleteRequest.Types.Body @@ -105,7 +125,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateObjectTokenContext( request.Body.Address, @@ -116,26 +136,29 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C request.Sign(ctx.Key); var response = await client.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken); - + Verifier.CheckResponse(response); } internal async IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args) { var ctx = args.Context!; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var request = new SearchRequest { Body = new SearchRequest.Types.Body { ContainerId = args.ContainerId.ToMessage(), - Filters = { }, Version = 1 // TODO: clarify this param } }; request.Body.Filters.AddRange(args.Filters.Select(f => f.ToMessage())); - - var sessionToken = await GetOrCreateSession(args, ctx); + + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateObjectTokenContext( new Address { ContainerId = request.Body.ContainerId }, @@ -143,7 +166,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C ctx.Key); request.AddMetaHeader(args.XHeaders, sessionToken); - + request.Sign(ctx.Key); var objectsIds = SearchObjects(request, ctx); @@ -156,14 +179,17 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C internal async Task PutObjectAsync(PrmObjectPut args) { + if (args is null) + throw new ArgumentNullException(nameof(args)); + if (args.Header == null) - throw new ArgumentException("Value cannot be null", nameof(args.Header)); - + throw new ArgumentException(nameof(args.Header)); + if (args.Payload == null) - throw new ArgumentException("Value cannot be null", nameof(args.Payload)); + throw new ArgumentException(nameof(args.Payload)); if (args.ClientCut) - return await PutClientCutObject(args); + return await PutClientCutObject(args).ConfigureAwait(false); else { if (args.Header.PayloadLength > 0) @@ -171,24 +197,28 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C else if (args.Payload.CanSeek) args.FullLength = (ulong)args.Payload.Length; - return (await PutStreamObject(args)).ObjectId; + return (await PutStreamObject(args).ConfigureAwait(false)).ObjectId; } } - + internal async Task PutSingleObjectAsync(PrmSingleObjectPut args) { var ctx = args.Context!; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, ctx); var request = new PutSingleRequest { - Body = new () { Object = grpcObject } + Body = new() { Object = grpcObject } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateObjectTokenContext( - new Address { ContainerId = grpcObject.Header.ContainerId, ObjectId = grpcObject.ObjectId}, + new Address { ContainerId = grpcObject.Header.ContainerId, ObjectId = grpcObject.ObjectId }, ObjectSessionContext.Types.Verb.Put, ctx.Key); @@ -196,18 +226,18 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C request.Sign(ctx.Key); - var response = await client.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken); + var response = await client.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken).ConfigureAwait(false); Verifier.CheckResponse(response); - + return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray()); } - + private async Task PutClientCutObject(PrmObjectPut args) { var ctx = args.Context!; - - var tokenRaw = await GetOrCreateSession(args, ctx); + + var tokenRaw = await GetOrCreateSession(args, ctx).ConfigureAwait(false); var token = new FrostFsSessionToken(tokenRaw.Serialize()); args.SessionToken = token; @@ -216,7 +246,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C var header = args.Header!; var fullLength = header.PayloadLength; - + if (payloadStream.CanSeek && fullLength == 0) fullLength = (ulong)payloadStream.Length; @@ -224,12 +254,14 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C if (args.MaxObjectSizeCache == 0) { - var networkSettings = await Context.Client.GetNetworkSettingsAsync(new PrmNetworkSettings() { Context = ctx }); + var networkSettings = await Context.Client.GetNetworkSettingsAsync(new PrmNetworkSettings() { Context = ctx }) + .ConfigureAwait(false); + args.MaxObjectSizeCache = (int)networkSettings.MaxObjectSize; } var restBytes = fullLength - args.CurrentStreamPosition; - var objectSize = restBytes > 0 ? (int)Math.Min((ulong)args.MaxObjectSizeCache, restBytes) : args.MaxObjectSizeCache; + var objectSize = restBytes > 0 ? (int)Math.Min((ulong)args.MaxObjectSizeCache, restBytes) : args.MaxObjectSizeCache; //define collection capacity var restPart = (restBytes % (ulong)objectSize) > 0 ? 1 : 0; @@ -238,23 +270,20 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C List sentObjectIds = new(objectsCount); FrostFsSplit? split = null; + SplitId splitId = new(); // keep attributes for the large object var attributes = args.Header!.Attributes; + args.Header!.Attributes = null; // send all parts except the last one as separate Objects - while (restBytes > (ulong)args.MaxObjectSizeCache) + while (restBytes > (ulong)args.MaxObjectSizeCache) { - if (split == null) - { - split = new FrostFsSplit(); - args.Header!.Attributes = []; - } + split = new FrostFsSplit(splitId, sentObjectIds.LastOrDefault()); - split!.Previous = sentObjectIds.LastOrDefault(); args.Header!.Split = split; - var result = await PutStreamObject(args); + var result = await PutStreamObject(args).ConfigureAwait(false); sentObjectIds.Add(result.ObjectId); @@ -264,26 +293,30 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C // send the last part and create linkObject if (sentObjectIds.Count > 0) { - var largeObjectHeader = new FrostFsObjectHeader(header.ContainerId) { PayloadLength = fullLength }; - - largeObjectHeader.Attributes.AddRange(attributes); + var largeObjectHeader = new FrostFsObjectHeader(header.ContainerId, FrostFsObjectType.Regular, [.. attributes]) + { + PayloadLength = fullLength, + }; args.Header.Split!.ParentHeader = largeObjectHeader; - var result = await PutStreamObject(args); + var result = await PutStreamObject(args).ConfigureAwait(false); sentObjectIds.Add(result.ObjectId); - var linkObject = new FrostFsLinkObject(header.ContainerId, split!.SplitId, largeObjectHeader) - .AddChildren(sentObjectIds); + var linkObject = new FrostFsLinkObject(header.ContainerId, split!.SplitId, largeObjectHeader, sentObjectIds); - _ = await PutSingleObjectAsync(new PrmSingleObjectPut(linkObject) { Context = args.Context}); + _ = await PutSingleObjectAsync(new PrmSingleObjectPut(linkObject) { Context = args.Context }).ConfigureAwait(false); - return split.Parent!; + var parentHeader = args.Header.GetHeader(); + + return parentHeader.Split!.Parent.ToModel(); } // We are here if the payload is placed to one Object. It means no cut action, just simple PUT. - var singlePartResult = await PutStreamObject(args); + args.Header!.Attributes = attributes; + + var singlePartResult = await PutStreamObject(args).ConfigureAwait(false); return singlePartResult.ObjectId; } @@ -297,6 +330,9 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C private async Task PutStreamObject(PrmObjectPut args) { var ctx = args.Context!; + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + var payload = args.Payload!; var chunkSize = args.BufferMaxSize > 0 ? args.BufferMaxSize : Constants.ObjectChunkSize; @@ -307,7 +343,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C bool isRentBuffer = false; byte[]? chunkBuffer = null; - + try { if (args.CustomBuffer != null) @@ -316,7 +352,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } else { - chunkBuffer = env.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize); + chunkBuffer = Context.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize); isRentBuffer = true; } @@ -325,7 +361,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C // 0 means no limit from client, so server side cut is performed var objectLimitSize = args.ClientCut ? args.MaxObjectSizeCache : 0; - var stream = await GetUploadStream(args, ctx); + using var stream = await GetUploadStream(args, ctx).ConfigureAwait(false); while (objectLimitSize == 0 || sentBytes < objectLimitSize) { @@ -334,7 +370,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C (int)Math.Min(objectLimitSize - sentBytes, chunkSize) : chunkSize; - var bytesCount = await payload.ReadAsync(chunkBuffer, 0, bufferSize, ctx.CancellationToken); + var bytesCount = await payload.ReadAsync(chunkBuffer, 0, bufferSize, ctx.CancellationToken).ConfigureAwait(false); if (bytesCount == 0) break; @@ -351,10 +387,10 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C chunkRequest.Sign(ctx.Key); - await stream.Write(chunkRequest); + await stream.Write(chunkRequest).ConfigureAwait(false); } - var response = await stream.Close(); + var response = await stream.Close().ConfigureAwait(false); Verifier.CheckResponse(response); return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()), sentBytes); @@ -372,6 +408,9 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C { var header = args.Header!; + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + header.OwnerId ??= ctx.OwnerId; header.Version ??= ctx.Version; @@ -395,7 +434,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } }; - var sessionToken = await GetOrCreateSession(args, ctx); + var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false); sessionToken.CreateObjectTokenContext( new Address { ContainerId = grpcHeader.ContainerId, ObjectId = oid }, @@ -407,19 +446,19 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C initRequest.Sign(ctx.Key); - return await PutObjectInit(initRequest, ctx); + return await PutObjectInit(initRequest, ctx).ConfigureAwait(false); } private async Task GetObject(GetRequest request, Context ctx) { var reader = GetObjectInit(request, ctx); - - var grpcObject = await reader.ReadHeader(); + + var grpcObject = await reader.ReadHeader().ConfigureAwait(false); var modelObject = grpcObject.ToModel(); - + modelObject.ObjectReader = reader; - return modelObject; + return modelObject; } private ObjectReader GetObjectInit(GetRequest initRequest, Context ctx) @@ -428,7 +467,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C throw new ArgumentNullException(nameof(initRequest)); var call = client.Get(initRequest, null, ctx.Deadline, ctx.CancellationToken); - + return new ObjectReader(call); } @@ -441,7 +480,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C var call = client.Put(null, ctx.Deadline, ctx.CancellationToken); - await call.RequestStream.WriteAsync(initRequest); + await call.RequestStream.WriteAsync(initRequest).ConfigureAwait(false); return new ObjectStreamer(call); } @@ -449,14 +488,14 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C private async IAsyncEnumerable SearchObjects(SearchRequest request, Context ctx) { using var stream = GetSearchReader(request, ctx); - + while (true) { - var ids = await stream.Read(ctx.CancellationToken); + var ids = await stream.Read(ctx.CancellationToken).ConfigureAwait(false); if (ids == null) break; - + foreach (var oid in ids) { yield return oid; @@ -472,7 +511,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } var call = client.Search(initRequest, null, ctx.Deadline, ctx.CancellationToken); - + return new SearchReader(call); - } + } } diff --git a/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs index 4498b3c..de22165 100644 --- a/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs @@ -1,17 +1,17 @@ using System.Threading.Tasks; -using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.Session; namespace FrostFS.SDK.ClientV2; -internal class SessionServiceProvider : ContextAccessor -{ +internal sealed class SessionServiceProvider : ContextAccessor +{ private readonly SessionService.SessionServiceClient? _sessionServiceClient; internal SessionServiceProvider(SessionService.SessionServiceClient? sessionServiceClient, ClientEnvironment context) - : base (context) + : base(context) { _sessionServiceClient = sessionServiceClient; } @@ -19,27 +19,30 @@ internal class SessionServiceProvider : ContextAccessor internal async Task CreateSessionAsync(PrmSessionCreate args) { var ctx = args.Context!; + + ctx.OwnerId ??= Context.Owner; + var request = new CreateRequest { Body = new CreateRequest.Types.Body { - OwnerId = ctx.OwnerId.ToMessage(), + OwnerId = ctx.OwnerId!.ToMessage(), Expiration = args.Expiration } }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ctx.Key!); - return await CreateSession(request, args.Context!); + return await CreateSession(request, args.Context!).ConfigureAwait(false); } internal async Task CreateSession(CreateRequest request, Context ctx) { var response = await _sessionServiceClient!.CreateAsync(request, null, ctx.Deadline, ctx.CancellationToken); - + Verifier.CheckResponse(response); - + return new SessionToken { Body = new SessionToken.Types.Body diff --git a/src/FrostFS.SDK.ClientV2/Services/Shared/ApeManagerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/Shared/ApeManagerServiceProvider.cs index 596d79e..ea92329 100644 --- a/src/FrostFS.SDK.ClientV2/Services/Shared/ApeManagerServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/Shared/ApeManagerServiceProvider.cs @@ -5,12 +5,12 @@ using Frostfs.V2.Apemanager; namespace FrostFS.SDK.ClientV2; -internal class ApeManagerServiceProvider : ContextAccessor -{ +internal sealed class ApeManagerServiceProvider : ContextAccessor +{ private readonly APEManagerService.APEManagerServiceClient? _apeManagerServiceClient; internal ApeManagerServiceProvider(APEManagerService.APEManagerServiceClient? apeManagerServiceClient, ClientEnvironment context) - : base (context) + : base(context) { _apeManagerServiceClient = apeManagerServiceClient; } @@ -18,12 +18,16 @@ internal class ApeManagerServiceProvider : ContextAccessor internal async Task AddChainAsync(PrmApeChainAdd args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); AddChainRequest request = new() { Body = new() { - Chain = new () { Raw = args.Chain.GetRaw() }, + Chain = new() { Raw = args.Chain.GetRaw() }, Target = args.Target.GetChainTarget() } }; @@ -41,6 +45,11 @@ internal class ApeManagerServiceProvider : ContextAccessor internal async Task RemoveChainAsync(PrmApeChainRemove args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + RemoveChainRequest request = new() { Body = new() @@ -61,6 +70,11 @@ internal class ApeManagerServiceProvider : ContextAccessor internal async Task ListChainAsync(PrmApeChainList args) { var ctx = args.Context!; + ctx.Key ??= Context.Key?.ECDsaKey; + + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + ListChainsRequest request = new() { Body = new() diff --git a/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs b/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs index 9e5b142..8d0d1f5 100644 --- a/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs @@ -7,13 +7,14 @@ internal interface ISessionProvider ValueTask GetOrCreateSession(ISessionToken args, Context ctx); } -internal class SessionProvider(ClientEnvironment env) -{ +internal sealed class SessionProvider(ClientEnvironment env) +{ public async ValueTask GetOrCreateSession(ISessionToken args, Context ctx) { if (args.SessionToken is null) - { - return await env.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue) { Context = ctx }); + { + return await env.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue) { Context = ctx }) + .ConfigureAwait(false); } return new Session.SessionToken().Deserialize(args.SessionToken.Token); diff --git a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs index 1ac5183..11023e7 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs @@ -6,9 +6,9 @@ using Grpc.Net.Client; namespace FrostFS.SDK.ClientV2; -public class ClientEnvironment(Client client, ECDsa? key, FrostFsOwner? owner, GrpcChannel channel, FrostFsVersion version) : IDisposable +public class ClientEnvironment(FrostFSClient client, ECDsa? key, FrostFsOwner? owner, GrpcChannel channel, FrostFsVersion version) : IDisposable { - private ArrayPool _arrayPool; + private ArrayPool? _arrayPool; internal FrostFsOwner? Owner { get; } = owner; @@ -18,7 +18,7 @@ public class ClientEnvironment(Client client, ECDsa? key, FrostFsOwner? owner, G internal NetworkSettings? NetworkSettings { get; set; } - internal Client Client { get; } = client; + internal FrostFSClient Client { get; } = client; internal ClientKey? Key { get; } = key != null ? new ClientKey(key) : null; @@ -28,7 +28,7 @@ public class ClientEnvironment(Client client, ECDsa? key, FrostFsOwner? owner, G internal ArrayPool GetArrayPool(int size) { _arrayPool ??= ArrayPool.Create(size, 256); - + return _arrayPool; } diff --git a/src/FrostFS.SDK.ClientV2/Tools/Object.cs b/src/FrostFS.SDK.ClientV2/Tools/Object.cs deleted file mode 100644 index 05d957c..0000000 --- a/src/FrostFS.SDK.ClientV2/Tools/Object.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace FrostFS.SDK.ClientV2.Extensions; - -public static class ObjectExtensions -{ - public static FrostFsObject SetPayloadLength(this FrostFsObject obj, ulong length) - { - obj.Header.PayloadLength = length; - return obj; - } - - public static FrostFsObject SetPayload(this FrostFsObject obj, byte[] bytes) - { - obj.Payload = bytes; - return obj; - } - - public static FrostFsObject AddAttribute(this FrostFsObject obj, string key, string value) - { - obj.AddAttribute(new FrostFsAttribute(key, value)); - return obj; - } - - public static FrostFsObject AddAttribute(this FrostFsObject obj, FrostFsAttribute attribute) - { - obj.Header.Attributes.Add(attribute); - return obj; - } - - public static FrostFsObject AddAttributes(this FrostFsObject obj, IEnumerable attributes) - { - obj.Header.Attributes.AddRange(attributes); - return obj; - } - - public static FrostFsObject SetSplit(this FrostFsObject obj, FrostFsSplit? split) - { - obj.Header.Split = split; - return obj; - } - - public static FrostFsLinkObject AddChildren(this FrostFsLinkObject linkObject, IEnumerable objectIds) - { - linkObject.Header.Split!.Children.AddRange(objectIds); - return linkObject; - } - - public static FrostFsObject CalculateObjectId(this FrostFsObject obj) - { - if (obj.Header == null) - throw new MissingFieldException("Header cannot be null"); - - return obj; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Tools/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectReader.cs index 07c4793..b1ad4f7 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ObjectReader.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ObjectReader.cs @@ -1,30 +1,30 @@ using System; +using System.Threading; using System.Threading.Tasks; +using FrostFS.Object; + using Grpc.Core; -using FrostFS.Object; -using System.Threading; - namespace FrostFS.SDK.ClientV2; -public class ObjectReader(AsyncServerStreamingCall call) : IObjectReader +public sealed class ObjectReader(AsyncServerStreamingCall call) : IObjectReader { - private bool disposed = false; + private bool disposed; public AsyncServerStreamingCall Call { get; private set; } = call; internal async Task ReadHeader() { - if (!await Call.ResponseStream.MoveNext()) + if (!await Call.ResponseStream.MoveNext().ConfigureAwait(false)) throw new InvalidOperationException("unexpected end of stream"); - + var response = Call.ResponseStream.Current; Verifier.CheckResponse(response); - + if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init) throw new InvalidOperationException("unexpected message type"); - + return new Object.Object { ObjectId = response.Body.Init.ObjectId, @@ -34,12 +34,12 @@ public class ObjectReader(AsyncServerStreamingCall call) : IObjectR public async Task?> ReadChunk(CancellationToken cancellationToken = default) { - if (!await Call.ResponseStream.MoveNext(cancellationToken)) + if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) return null; - + var response = Call.ResponseStream.Current; Verifier.CheckResponse(response); - + if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk) throw new InvalidOperationException("unexpected message type"); diff --git a/src/FrostFS.SDK.ClientV2/Tools/ObjectStreamer.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectStreamer.cs index 9350282..acd0d28 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ObjectStreamer.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ObjectStreamer.cs @@ -1,13 +1,13 @@ using System; using System.Threading.Tasks; -using Grpc.Core; - using FrostFS.Object; +using Grpc.Core; + namespace FrostFS.SDK.ClientV2; -internal class ObjectStreamer(AsyncClientStreamingCall call) : IDisposable +internal sealed class ObjectStreamer(AsyncClientStreamingCall call) : IDisposable { public AsyncClientStreamingCall Call { get; private set; } = call; @@ -18,14 +18,14 @@ internal class ObjectStreamer(AsyncClientStreamingCall throw new ArgumentNullException(nameof(request)); } - await Call.RequestStream.WriteAsync(request); + await Call.RequestStream.WriteAsync(request).ConfigureAwait(false); } public async Task Close() { - await Call.RequestStream.CompleteAsync(); - - return await Call.ResponseAsync; + await Call.RequestStream.CompleteAsync().ConfigureAwait(false); + + return await Call.ResponseAsync.ConfigureAwait(false); } public void Dispose() diff --git a/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs index 64c09cd..0303626 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs @@ -1,35 +1,35 @@ using System.Linq; -using Google.Protobuf; - using FrostFS.Object; using FrostFS.Refs; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; +using Google.Protobuf; + namespace FrostFS.SDK.ClientV2; -internal class ObjectTools +internal static class ObjectTools { internal static FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, Context ctx) - { + { var grpcHeader = CreateHeader(header, [], ctx); if (header.Split != null) SetSplitValues(grpcHeader, header.Split, ctx); - return new ObjectID { Value = grpcHeader.Sha256() }.ToModel(); + return new ObjectID { Value = grpcHeader.Sha256() }.ToModel(); } - internal static Object.Object CreateObject(FrostFsObject @object, Context ctx) + internal static Object.Object CreateObject(FrostFsObject @object, Context ctx) { @object.Header.OwnerId ??= ctx.OwnerId; @object.Header.Version ??= ctx.Version; var grpcHeader = @object.Header.GetHeader(); - - grpcHeader.PayloadLength = (ulong)@object.Payload.Length; - grpcHeader.PayloadHash = Sha256Checksum(@object.Payload); + + grpcHeader.PayloadLength = (ulong)@object.SingleObjectPayload.Length; + grpcHeader.PayloadHash = Sha256Checksum(@object.SingleObjectPayload); var split = @object.Header.Split; if (split != null) @@ -41,15 +41,15 @@ internal class ObjectTools { Header = grpcHeader, ObjectId = new ObjectID { Value = grpcHeader.Sha256() }, - Payload = ByteString.CopyFrom(@object.Payload) + Payload = ByteString.CopyFrom(@object.SingleObjectPayload) }; - obj.Signature = new Refs.Signature + obj.Signature = new Signature { Key = ctx.GetPublicKeyCache(), - Sign = ByteString.CopyFrom(ctx.Key.SignData(obj.ObjectId.ToByteArray())), - }; - + Sign = ByteString.CopyFrom(ctx.Key!.SignData(obj.ObjectId.ToByteArray())), + }; + return obj; } @@ -58,6 +58,9 @@ internal class ObjectTools if (split == null) return; + if (ctx.Key == null) + throw new InvalidObjectException(nameof(ctx.Key)); + grpcHeader.Split = new Header.Types.Split { SplitId = split.SplitId?.GetSplitId() @@ -72,13 +75,11 @@ internal class ObjectTools grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() }; grpcHeader.Split.ParentHeader = grpcParentHeader; - grpcHeader.Split.ParentSignature = new Refs.Signature + grpcHeader.Split.ParentSignature = new Signature { Key = ctx.GetPublicKeyCache(), Sign = ByteString.CopyFrom(ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray())), }; - - split.Parent = grpcHeader.Split.Parent.ToModel(); } grpcHeader.Split.Previous = split.Previous?.ToMessage(); @@ -90,10 +91,10 @@ internal class ObjectTools header.Version ??= ctx.Version; var grpcHeader = header.GetHeader(); - + if (payload != null) // && payload.Length > 0 grpcHeader.PayloadHash = Sha256Checksum(payload); - + return grpcHeader; } diff --git a/src/FrostFS.SDK.ClientV2/Tools/Range.cs b/src/FrostFS.SDK.ClientV2/Tools/Range.cs index eae7b50..8add54e 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/Range.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Range.cs @@ -25,7 +25,7 @@ namespace System { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - + if (fromEnd) _value = ~value; else @@ -51,7 +51,7 @@ namespace System { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - + return new Index(value); } @@ -62,7 +62,7 @@ namespace System { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - + return new Index(~value); } @@ -119,9 +119,9 @@ namespace System public override string ToString() { if (IsFromEnd) - return "^" + ((uint)Value).ToString(); + return $"^{(uint)Value}"; - return ((uint)Value).ToString(); + return $"{(uint)Value}"; } } @@ -189,7 +189,7 @@ namespace System { int start; var startIndex = Start; - + if (startIndex.IsFromEnd) start = length - startIndex.Value; else @@ -205,7 +205,7 @@ namespace System if ((uint)end > (uint)length || (uint)start > (uint)end) throw new ArgumentOutOfRangeException(nameof(length)); - + return (start, end - start); } } @@ -222,7 +222,7 @@ namespace System.Runtime.CompilerServices { if (array == null) throw new ArgumentNullException(nameof(array)); - + (int offset, int length) = range.GetOffsetAndLength(array.Length); if (default(T) != null || typeof(T[]) == array.GetType()) @@ -231,7 +231,7 @@ namespace System.Runtime.CompilerServices if (length == 0) return []; - + var dest = new T[length]; Array.Copy(array, offset, dest, 0, length); return dest; diff --git a/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs index c12d280..6f19ad0 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Specialized; using System.Linq; using System.Security.Cryptography; @@ -12,9 +13,14 @@ namespace FrostFS.SDK.ClientV2; public static class RequestConstructor { - public static void AddMetaHeader(this IRequest request, NameValueCollection? xHeaders, Session.SessionToken? sessionToken = null) + public static void AddMetaHeader(this IRequest request, + NameValueCollection? xHeaders, + SessionToken? sessionToken = null) { - if (request.MetaHeader is not null) + if (request is null) + throw new ArgumentNullException(nameof(request)); + + if (request.MetaHeader is not null) return; request.MetaHeader = MetaHeader.Default().ToMessage(); @@ -28,11 +34,21 @@ public static class RequestConstructor (k, v) => new XHeader { Key = k, Value = v })); } - public static void CreateObjectTokenContext(this Session.SessionToken sessionToken, + public static void CreateObjectTokenContext(this SessionToken sessionToken, Address address, ObjectSessionContext.Types.Verb verb, ECDsa key) { + if (sessionToken is null) + { + throw new ArgumentNullException(nameof(sessionToken)); + } + + if (address is null) + { + throw new ArgumentNullException(nameof(address)); + } + if (sessionToken.Body.Object?.Target != null) return; @@ -52,7 +68,7 @@ public static class RequestConstructor sessionToken.Signature = key.SignMessagePart(sessionToken.Body); } - public static void CreateContainerTokenContext(this Session.SessionToken sessionToken, + internal static void CreateContainerTokenContext(this SessionToken sessionToken, ContainerID? containerId, ContainerSessionContext.Types.Verb verb, ECDsa key, @@ -61,7 +77,7 @@ public static class RequestConstructor if (sessionToken.Body.Container?.ContainerId != null) return; - sessionToken.Body.Container = new (){ Verb = verb }; + sessionToken.Body.Container = new() { Verb = verb }; if (containerId != null) sessionToken.Body.Container.ContainerId = containerId; @@ -69,7 +85,7 @@ public static class RequestConstructor sessionToken.Body.Container.Wildcard = true; sessionToken.Body.SessionKey = publicKey; - + sessionToken.Signature = key.SignMessagePart(sessionToken.Body); } } diff --git a/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs index c2d80f7..13d87b0 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs @@ -18,35 +18,45 @@ namespace FrostFS.SDK.ClientV2; public static class RequestSigner { - public const int RFC6979SignatureSize = 64; + internal const int RFC6979SignatureSize = 64; - public static byte[] SignRFC6979(this ECDsa key, byte[] data) + internal static byte[] SignRFC6979(this ECDsa key, byte[] data) { + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + var digest = new Sha256Digest(); var secp256R1 = SecNamedCurves.GetByName("secp256r1"); var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); var privateKey = new ECPrivateKeyParameters(new BigInteger(1, key.PrivateKey()), ecParameters); var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); var hash = new byte[digest.GetDigestSize()]; - + digest.BlockUpdate(data, 0, data.Length); digest.DoFinal(hash, 0); signer.Init(true, privateKey); - + var rs = signer.GenerateSignature(hash); var signature = new byte[RFC6979SignatureSize]; var rbytes = rs[0].ToByteArrayUnsigned(); var sbytes = rs[1].ToByteArrayUnsigned(); var index = RFC6979SignatureSize / 2 - rbytes.Length; - + rbytes.CopyTo(signature, index); index = RFC6979SignatureSize - sbytes.Length; sbytes.CopyTo(signature, index); - + return signature; } - public static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message) + internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message) { return new SignatureRFC6979 { @@ -54,8 +64,8 @@ public static class RequestSigner Sign = ByteString.CopyFrom(key.SignRFC6979(message.ToByteArray())), }; } - - public static SignatureRFC6979 SignRFC6979(this ECDsa key, ByteString data) + + internal static SignatureRFC6979 SignRFC6979(this ECDsa key, ByteString data) { return new SignatureRFC6979 { @@ -74,7 +84,7 @@ public static class RequestSigner return hash; } - public static Signature SignMessagePart(this ECDsa key, IMessage? data) + internal static Signature SignMessagePart(this ECDsa key, IMessage? data) { var data2Sign = data is null ? [] : data.ToByteArray(); var sig = new Signature @@ -86,7 +96,7 @@ public static class RequestSigner return sig; } - public static void Sign(this IVerifiableMessage message, ECDsa key) + internal static void Sign(this IVerifiableMessage message, ECDsa key) { var meta = message.GetMetaHeader(); IVerificationHeader verify = message switch @@ -95,17 +105,17 @@ public static class RequestSigner IResponse => new ResponseVerificationHeader(), _ => throw new InvalidOperationException("Unsupported message type") }; - + var verifyOrigin = message.GetVerificationHeader(); - + if (verifyOrigin is null) verify.BodySignature = key.SignMessagePart(message.GetBody()); - else + else verify.SetOrigin(verifyOrigin); - + verify.MetaSignature = key.SignMessagePart(meta); verify.OriginSignature = key.SignMessagePart(verifyOrigin); - + message.SetVerificationHeader(verify); } } diff --git a/src/FrostFS.SDK.ClientV2/Tools/SearchReader.cs b/src/FrostFS.SDK.ClientV2/Tools/SearchReader.cs index 1bcf398..ceed0da 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/SearchReader.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/SearchReader.cs @@ -11,17 +11,17 @@ using Grpc.Core; namespace FrostFS.SDK.ClientV2; -internal class SearchReader(AsyncServerStreamingCall call) : IDisposable +internal sealed class SearchReader(AsyncServerStreamingCall call) : IDisposable { public AsyncServerStreamingCall Call { get; private set; } = call; public async Task?> Read(CancellationToken cancellationToken) { - if (!await Call.ResponseStream.MoveNext(cancellationToken)) + if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) return null; - + var response = Call.ResponseStream.Current; - + Verifier.CheckResponse(response); return response.Body?.IdList.ToList(); diff --git a/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs index 2640e45..2896209 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs @@ -45,7 +45,7 @@ public static class Verifier var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); var bcPublicKey = new ECPublicKeyParameters(secp256R1.Curve.DecodePoint(publicKey), ecParameters); var hash = new byte[digest.GetDigestSize()]; - + digest.BlockUpdate(data, 0, data.Length); digest.DoFinal(hash, 0); signer.Init(false, bcPublicKey); @@ -55,11 +55,25 @@ public static class Verifier public static bool VerifyRFC6979(this SignatureRFC6979 signature, IMessage message) { + if (signature is null) + { + throw new ArgumentNullException(nameof(signature)); + } + return signature.Key.ToByteArray().VerifyRFC6979(message.ToByteArray(), signature.Sign.ToByteArray()); } public static bool VerifyData(this ECDsa key, byte[] data, byte[] sig) { + if (key is null) + throw new ArgumentNullException(nameof(key)); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + if (sig is null) + throw new ArgumentNullException(nameof(sig)); + return key.VerifyHash(data.Sha512(), sig[1..]); } @@ -70,36 +84,41 @@ public static class Verifier using var key = sig.Key.ToByteArray().LoadPublicKey(); var data2Verify = data is null ? [] : data.ToByteArray(); - + return key.VerifyData(data2Verify, sig.Sign.ToByteArray()); } - public static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) + internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) { - if (!verification.MetaSignature.VerifyMessagePart(meta)) + if (!verification.MetaSignature.VerifyMessagePart(meta)) return false; - + var origin = verification.GetOrigin(); - - if (!verification.OriginSignature.VerifyMessagePart(origin)) + + if (!verification.OriginSignature.VerifyMessagePart(origin)) return false; - + if (origin is null) return verification.BodySignature.VerifyMessagePart(body); - + return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin); } public static bool Verify(this IVerifiableMessage message) { + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader()); } - public static void CheckResponse(IResponse resp) + internal static void CheckResponse(IResponse resp) { if (!resp.Verify()) throw new FormatException($"invalid response, type={resp.GetType()}"); - + var status = resp.MetaHeader.Status.ToModel(); if (status != null && !status.IsSuccess) @@ -112,6 +131,11 @@ public static class Verifier /// Created by SDK request to gRpc proxy public static void CheckRequest(IRequest request) { + if (request is null) + { + throw new ArgumentNullException(nameof(request)); + } + if (!request.Verify()) throw new FormatException($"invalid response, type={request.GetType()}"); } diff --git a/src/FrostFS.SDK.ClientV2/Ростелеком.txt b/src/FrostFS.SDK.ClientV2/Ростелеком.txt new file mode 100644 index 0000000..d3687ed --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Ростелеком.txt @@ -0,0 +1,8 @@ +Для возврата денег потребуются документы: +1. Заполненное заявление +Форму заявления можно скачать с сайта +Шаблон заявления скачайте на сайте rt.ru. Внизу страницы перейдите в Договоры и соглашения -> «Бланки и заявления» +2. Скан-фото паспорта владельца договора (2, 3, 5 страница: кем и когда выдан, адрес регистрации) +3. Реквизиты счета владельца, выданные банком, на которые будет выполнен возврат +4. Чек об оплате +Сканы документов должны быть хорошо читаемые diff --git a/src/FrostFS.SDK.Cryptography/Base58.cs b/src/FrostFS.SDK.Cryptography/Base58.cs index 95b07f0..1b8f084 100644 --- a/src/FrostFS.SDK.Cryptography/Base58.cs +++ b/src/FrostFS.SDK.Cryptography/Base58.cs @@ -13,17 +13,17 @@ public static class Base58 { if (input is null) throw new ArgumentNullException(nameof(input)); - + byte[] buffer = Decode(input); - if (buffer.Length < 4) + if (buffer.Length < 4) throw new FormatException(); - + byte[] checksum = buffer[0..(buffer.Length - 4)].Sha256().Sha256(); if (!buffer.AsSpan(buffer.Length - 4).SequenceEqual(checksum[..4].AsSpan())) throw new FormatException(); - + var ret = buffer[..^4]; Array.Clear(buffer, 0, buffer.Length); return ret; @@ -34,7 +34,7 @@ public static class Base58 byte[] checksum = data.ToArray().Sha256().Sha256(); Span buffer = stackalloc byte[data.Length + 4]; data.CopyTo(buffer); - + checksum[..4].AsSpan().CopyTo(buffer[data.Length..]); var ret = Encode(buffer); buffer.Clear(); @@ -44,6 +44,9 @@ public static class Base58 public static byte[] Decode(string input) { + if (input == null) + throw new ArgumentNullException(nameof(input)); + // Decode Base58 string to BigInteger var bi = BigInteger.Zero; for (int i = 0; i < input.Length; i++) @@ -61,7 +64,7 @@ public static class Base58 if (bi.IsZero) return leadingZeros; - var bytesBigEndian = bi.ToByteArray().Reverse(); + var bytesBigEndian = bi.ToByteArray().Reverse().ToArray(); var firstNonZeroIndex = 0; diff --git a/src/FrostFS.SDK.Cryptography/Extentions.cs b/src/FrostFS.SDK.Cryptography/Extentions.cs index 32c8259..9d61972 100644 --- a/src/FrostFS.SDK.Cryptography/Extentions.cs +++ b/src/FrostFS.SDK.Cryptography/Extentions.cs @@ -1,17 +1,19 @@ -using Google.Protobuf; -using Org.BouncyCastle.Crypto.Digests; using System.Security.Cryptography; using System.Threading; +using Google.Protobuf; + +using Org.BouncyCastle.Crypto.Digests; + namespace FrostFS.SDK.Cryptography; public static class Extentions { private static readonly SHA256 _sha256 = SHA256.Create(); - private static SpinLock _spinlockSha256 = new(); + private static SpinLock _spinlockSha256; private static readonly SHA512 _sha512 = SHA512.Create(); - private static SpinLock _spinlockSha512 = new(); + private static SpinLock _spinlockSha512; internal static byte[] RIPEMD160(this byte[] value) { @@ -22,7 +24,7 @@ public static class Extentions digest.DoFinal(hash, 0); return hash; } - + public static ByteString Sha256(this IMessage data) { return ByteString.CopyFrom(data.ToByteArray().Sha256()); diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj index e276848..59bde16 100644 --- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj +++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj @@ -13,6 +13,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/FrostFS.SDK.Cryptography/Key.cs b/src/FrostFS.SDK.Cryptography/Key.cs index bbffab8..0c9fd3b 100644 --- a/src/FrostFS.SDK.Cryptography/Key.cs +++ b/src/FrostFS.SDK.Cryptography/Key.cs @@ -20,6 +20,9 @@ public static class KeyExtension public static byte[] Compress(this byte[] publicKey) { + if (publicKey == null) + throw new ArgumentNullException(nameof(publicKey)); + if (publicKey.Length != UncompressedPublicKeyLength) throw new FormatException( $"{nameof(Compress)} argument isn't uncompressed public key. " + @@ -34,6 +37,9 @@ public static class KeyExtension public static byte[] Decompress(this byte[] publicKey) { + if (publicKey == null) + throw new ArgumentNullException(nameof(publicKey)); + if (publicKey.Length != CompressedPublicKeyLength) throw new FormatException( $"{nameof(Decompress)} argument isn't compressed public key. " + @@ -64,6 +70,9 @@ public static class KeyExtension public static byte[] GetScriptHash(this byte[] publicKey) { + if (publicKey == null) + throw new ArgumentNullException(nameof(publicKey)); + var script = publicKey.CreateSignatureRedeemScript(); return script.Sha256().RIPEMD160(); } @@ -79,14 +88,14 @@ public static class KeyExtension private static byte[] GetPrivateKeyFromWIF(string wif) { - if (wif == null) + if (wif == null) throw new ArgumentNullException(nameof(wif)); - + var data = wif.Base58CheckDecode(); - + if (data.Length != 34 || data[0] != 0x80 || data[33] != 0x01) throw new FormatException(); - + var privateKey = new byte[32]; Buffer.BlockCopy(data, 1, privateKey, 0, privateKey.Length); Array.Clear(data, 0, data.Length); @@ -100,6 +109,9 @@ public static class KeyExtension public static string PublicKeyToAddress(this byte[] publicKey) { + if (publicKey == null) + throw new ArgumentNullException(nameof(publicKey)); + if (publicKey.Length != CompressedPublicKeyLength) throw new FormatException( nameof(publicKey) + @@ -112,6 +124,9 @@ public static class KeyExtension public static byte[] PublicKey(this ECDsa key) { + if (key == null) + throw new ArgumentNullException(nameof(key)); + var param = key.ExportParameters(false); var pubkey = new byte[33]; var pos = 33 - param.Q.X.Length; @@ -127,6 +142,9 @@ public static class KeyExtension public static byte[] PrivateKey(this ECDsa key) { + if (key == null) + throw new ArgumentNullException(nameof(key)); + return key.ExportParameters(true).D; } @@ -153,7 +171,7 @@ public static class KeyExtension public static ECDsa LoadWif(this string wif) { var privateKey = GetPrivateKeyFromWIF(wif); - + return LoadPrivateKey(privateKey); } diff --git a/src/FrostFS.SDK.Cryptography/Murmur3_128.cs b/src/FrostFS.SDK.Cryptography/Murmur3_128.cs index 5cb35a1..e8f52fc 100644 --- a/src/FrostFS.SDK.Cryptography/Murmur3_128.cs +++ b/src/FrostFS.SDK.Cryptography/Murmur3_128.cs @@ -28,7 +28,7 @@ internal class Murmur3_128 : HashAlgorithm length += cbSize; int remainder = cbSize & 15; int alignedLength = ibStart + (cbSize - remainder); - + for (int i = ibStart; i < alignedLength; i += 16) { ulong k1 = BinaryPrimitives.ReadUInt64LittleEndian(array.AsSpan(i)); @@ -50,7 +50,7 @@ internal class Murmur3_128 : HashAlgorithm h2 += h1; h2 = h2 * m + n2; } - + if (remainder > 0) { ulong k1 = 0, k2 = 0; @@ -101,7 +101,7 @@ internal class Murmur3_128 : HashAlgorithm h2 = Fimix64(h2); h1 += h2; h2 += h1; - + return BitConverter.GetBytes(h1); } diff --git a/src/FrostFS.SDK.Cryptography/Range.cs b/src/FrostFS.SDK.Cryptography/Range.cs index 92aa82f..495479d 100644 --- a/src/FrostFS.SDK.Cryptography/Range.cs +++ b/src/FrostFS.SDK.Cryptography/Range.cs @@ -122,9 +122,9 @@ namespace System public override string ToString() { if (IsFromEnd) - return "^" + ((uint)Value).ToString(); + return $"^{(uint)Value}"; - return ((uint)Value).ToString(); + return $"{(uint)Value}"; } } @@ -140,7 +140,7 @@ namespace System /// Construct a Range object using the start and end indexes. /// Represent the inclusive start index of the range. /// Represent the exclusive end index of the range. - internal readonly struct Range (Index start, Index end) : IEquatable + internal readonly struct Range(Index start, Index end) : IEquatable { /// Represent the inclusive start index of the Range. public Index Start { get; } = start; diff --git a/src/FrostFS.SDK.Cryptography/UUID.cs b/src/FrostFS.SDK.Cryptography/UUID.cs index fee2df9..c92a874 100644 --- a/src/FrostFS.SDK.Cryptography/UUID.cs +++ b/src/FrostFS.SDK.Cryptography/UUID.cs @@ -1,5 +1,6 @@ -using Google.Protobuf; -using System; +using System; + +using Google.Protobuf; namespace FrostFS.SDK.Cryptography; @@ -7,13 +8,16 @@ public static class UUIDExtension { public static Guid ToUuid(this ByteString id) { + if (id == null) + throw new ArgumentNullException(nameof(id)); + var bytes = id.ToByteArray(); - + var orderedBytes = GetGuidBytesDirectOrder(bytes); return new Guid(orderedBytes); } - + /// /// Serializes Guid to binary representation in direct order bytes format /// diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs index 6a637c3..32ba5d4 100644 --- a/src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs @@ -1,4 +1,5 @@ using FrostFS.Refs; + using Google.Protobuf; namespace FrostFS.Session; diff --git a/src/FrostFS.SDK.ProtosV2/apemanager/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/apemanager/Extension.Message.cs index 1f02ea5..408622d 100644 --- a/src/FrostFS.SDK.ProtosV2/apemanager/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/apemanager/Extension.Message.cs @@ -1,7 +1,7 @@ -using Google.Protobuf; - -using FrostFS.Session; using FrostFS.SDK.ProtosV2.Interfaces; +using FrostFS.Session; + +using Google.Protobuf; namespace Frostfs.V2.Apemanager; diff --git a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs index 0b876f0..010072e 100644 --- a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs @@ -1,7 +1,7 @@ -using Google.Protobuf; - -using FrostFS.Session; using FrostFS.SDK.ProtosV2.Interfaces; +using FrostFS.Session; + +using Google.Protobuf; namespace FrostFS.Container; diff --git a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs index 2c1c8b6..1001bce 100644 --- a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs @@ -1,5 +1,6 @@ using FrostFS.SDK.ProtosV2.Interfaces; using FrostFS.Session; + using Google.Protobuf; namespace FrostFS.Netmap; diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs index dbfdd79..7c0c87d 100644 --- a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs @@ -1,6 +1,8 @@ using System.Diagnostics; + using FrostFS.SDK.ProtosV2.Interfaces; using FrostFS.Session; + using Google.Protobuf; namespace FrostFS.Object @@ -117,7 +119,7 @@ namespace FrostFS.Object } } - public partial class PutSingleRequest : IRequest + public partial class PutSingleRequest : IRequest { IMetaHeader IVerifiableMessage.GetMetaHeader() { diff --git a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs index decf002..4f0850b 100644 --- a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs @@ -1,4 +1,5 @@ using FrostFS.SDK.ProtosV2.Interfaces; + using Google.Protobuf; namespace FrostFS.Session; diff --git a/src/FrostFS.SDK.Tests/ContainerTest.cs b/src/FrostFS.SDK.Tests/ContainerTest.cs index fcfcb39..e58e18c 100644 --- a/src/FrostFS.SDK.Tests/ContainerTest.cs +++ b/src/FrostFS.SDK.Tests/ContainerTest.cs @@ -1,12 +1,12 @@ using FrostFS.SDK.ClientV2; -using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Interfaces; -using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; -using Microsoft.Extensions.Options; using Google.Protobuf; +using Microsoft.Extensions.Options; + namespace FrostFS.SDK.Tests; public abstract class ContainerTestsBase @@ -34,7 +34,7 @@ public abstract class ContainerTestsBase protected IFrostFSClient GetClient() { - return ClientV2.Client.GetTestInstance( + return ClientV2.FrostFSClient.GetTestInstance( Settings, null, new NetworkMocker(this.key).GetMock().Object, @@ -45,17 +45,17 @@ public abstract class ContainerTestsBase } public class ContainerTest : ContainerTestsBase -{ +{ [Fact] public async void CreateContainerTest() { - var param = new PrmContainerCreate(new FrostFsContainerInfo(BasicAcl.PublicRW, Mocker.PlacementPolicy)); + var param = new PrmContainerCreate(new FrostFsContainerInfo(Mocker.PlacementPolicy)); var result = await GetClient().CreateContainerAsync(param); Assert.NotNull(result); - Assert.NotNull(result.Value); - Assert.True(Base58.Encode(Mocker.ContainerGuid.ToBytes()) == result.Value); + Assert.NotNull(result.GetValue()); + Assert.True(Base58.Encode(Mocker.ContainerGuid.ToBytes()) == result.GetValue()); } [Fact] @@ -63,14 +63,11 @@ public class ContainerTest : ContainerTestsBase { var cid = new FrostFsContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes())); - Mocker.Acl = BasicAcl.PublicRO; - var result = await GetClient().GetContainerAsync(new PrmContainerGet(cid)); Assert.NotNull(result); - Assert.Equal(Mocker.Acl, result.BasicAcl); Assert.Equal(Mocker.ContainerGuid, result.Nonce); - Assert.Equal(0, Mocker.PlacementPolicy.CompareTo(result.PlacementPolicy)); + Assert.Equal(Mocker.PlacementPolicy, result.PlacementPolicy); Assert.Equal(Mocker.Version.ToString(), result.Version!.ToString()); } @@ -89,7 +86,7 @@ public class ContainerTest : ContainerTestsBase await foreach (var cid in result) { var val = Base58.Encode(ByteString.CopyFrom(Mocker.ContainerIds[i++]).ToByteArray()); - Assert.Equal(val, cid.Value); + Assert.Equal(val, cid.GetValue()); } Assert.Equal(3, i); @@ -106,7 +103,7 @@ public class ContainerTest : ContainerTestsBase Assert.Single(Mocker.Requests); var request = Mocker.Requests.First(); - - Assert.Equal(cid.ToMessage(), request.Request.Body.ContainerId); + + Assert.Equal(cid.ToMessage(), request.Request.Body.ContainerId); } } diff --git a/src/FrostFS.SDK.Tests/MetricsInterceptor.cs b/src/FrostFS.SDK.Tests/MetricsInterceptor.cs index cc6b438..958a73e 100644 --- a/src/FrostFS.SDK.Tests/MetricsInterceptor.cs +++ b/src/FrostFS.SDK.Tests/MetricsInterceptor.cs @@ -1,7 +1,7 @@ -using Grpc.Core; -using Grpc.Core.Interceptors; +using System.Diagnostics; -using System.Diagnostics; +using Grpc.Core; +using Grpc.Core.Interceptors; namespace FrostFS.SDK.SmokeTests; @@ -21,12 +21,12 @@ public class MetricsInterceptor() : Interceptor call.GetTrailers, call.Dispose); } - + private static async Task HandleUnaryResponse(AsyncUnaryCall call) { var watch = new Stopwatch(); watch.Start(); - + var response = await call.ResponseAsync; watch.Stop(); @@ -34,6 +34,6 @@ public class MetricsInterceptor() : Interceptor // Do something with call info // var elapsed = watch.ElapsedTicks * 1_000_000/Stopwatch.Frequency; - return response; + return response; } } diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs index a8ddbbd..22172f2 100644 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs @@ -20,7 +20,7 @@ public class AsyncStreamReaderMock(string key, FrostFsObjectHeader objectHeader) { var header = new Header { - ContainerId = objectHeader.ContainerId.ToMessage(), + ContainerId = objectHeader.ContainerId.ToMessage(), PayloadLength = objectHeader.PayloadLength, Version = objectHeader.Version!.ToMessage(), OwnerId = objectHeader.OwnerId!.ToMessage() diff --git a/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs b/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs index 56746e4..9836d37 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs @@ -1,6 +1,7 @@ -using Grpc.Core; using FrostFS.SDK.ProtosV2.Interfaces; +using Grpc.Core; + namespace FrostFS.SDK.Tests; public class ClientStreamWriter : IClientStreamWriter @@ -17,13 +18,13 @@ public class ClientStreamWriter : IClientStreamWriter public Task CompleteAsync() { CompletedTask = true; - return Task.CompletedTask; + return Task.CompletedTask; } public Task WriteAsync(IRequest message) { Object.PutRequest pr = new((Object.PutRequest)message); - Messages.Add(pr); + Messages.Add(pr); return Task.CompletedTask; } } diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs index 4235f71..ba34cde 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs @@ -1,15 +1,17 @@ using System.Security.Cryptography; -using FrostFS.Container; -using Moq; +using FrostFS.Container; +using FrostFS.Object; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using FrostFS.Session; + using Google.Protobuf; -using FrostFS.SDK.ClientV2; -using FrostFS.Object; using Grpc.Core; -using FrostFS.SDK.ClientV2.Mappers.GRPC; + +using Moq; namespace FrostFS.SDK.Tests; @@ -18,11 +20,9 @@ public abstract class ServiceBase(string key) public string StringKey { get; private set; } = key; public ECDsa Key { get; private set; } = key.LoadWif(); public FrostFsVersion Version { get; set; } = DefaultVersion; - public BasicAcl Acl { get; set; } = DefaultAcl; public FrostFsPlacementPolicy PlacementPolicy { get; set; } = DefaultPlacementPolicy; public static FrostFsVersion DefaultVersion { get; } = new(2, 13); - public static BasicAcl DefaultAcl { get; } = BasicAcl.PublicRW; public static FrostFsPlacementPolicy DefaultPlacementPolicy { get; } = new FrostFsPlacementPolicy(true, new FrostFsReplica(1)); public Metadata Metadata { get; protected set; } @@ -61,23 +61,23 @@ public abstract class ServiceBase(string key) } public ResponseMetaHeader ResponseMetaHeader => new() - { - Version = Version.ToMessage(), - Epoch = 100, - Ttl = 1 - }; + { + Version = Version.ToMessage(), + Epoch = 100, + Ttl = 1 + }; } -public abstract class ContainerServiceBase(string key) : ServiceBase (key) +public abstract class ContainerServiceBase(string key) : ServiceBase(key) { public Guid ContainerGuid { get; set; } = Guid.NewGuid(); - + public abstract Mock GetMock(); } -public abstract class ObjectServiceBase(string key) : ServiceBase (key) +public abstract class ObjectServiceBase(string key) : ServiceBase(key) { - public abstract Mock GetMock(); - + public abstract Mock GetMock(); + public Guid ContainerGuid { get; set; } = Guid.NewGuid(); } diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs index c0b69a3..2734a26 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs @@ -1,4 +1,5 @@ using FrostFS.Container; + using Moq; namespace FrostFS.SDK.Tests; diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs index c76cc9f..d77322a 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs @@ -1,14 +1,15 @@ using FrostFS.Container; -using Google.Protobuf; -using Grpc.Core; -using Moq; - -using FrostFS.SDK.Cryptography; -using FrostFS.SDK.ClientV2; - -using FrostFS.SDK.ClientV2.Mappers.GRPC; -using FrostFS.Session; using FrostFS.Refs; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; +using FrostFS.Session; + +using Google.Protobuf; + +using Grpc.Core; + +using Moq; namespace FrostFS.SDK.Tests; @@ -67,8 +68,7 @@ public class ContainerMocker(string key) : ContainerServiceBase(key) { Version = grpcVersion, Nonce = ByteString.CopyFrom(ContainerGuid.ToBytes()), - BasicAcl = (uint)Acl, - PlacementPolicy = PlacementPolicy.ToMessage() + PlacementPolicy = PlacementPolicy.GetPolicy() } }, MetaHeader = ResponseMetaHeader @@ -78,10 +78,10 @@ public class ContainerMocker(string key) : ContainerServiceBase(key) var getNoContainerResponse = new GetResponse { - Body = new (), + Body = new(), MetaHeader = new ResponseMetaHeader - { - Status = new Status.Status + { + Status = new Status.Status { Code = 3072, Message = "container not found" @@ -107,7 +107,7 @@ public class ContainerMocker(string key) : ContainerServiceBase(key) Task.FromResult(ResponseMetaData), () => new Grpc.Core.Status(StatusCode.NotFound, string.Empty), () => ResponseMetaData, - () => { }); + () => { }); } return new AsyncUnaryCall( @@ -115,7 +115,7 @@ public class ContainerMocker(string key) : ContainerServiceBase(key) Task.FromResult(ResponseMetaData), () => new Grpc.Core.Status(StatusCode.OK, string.Empty), () => ResponseMetaData, - () => { }); + () => { }); }); var listResponse = new ListResponse diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs index 8af698b..f60a2c8 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs @@ -9,4 +9,3 @@ public class RequestData(T request, Metadata m, DateTime? dt, CancellationTok public DateTime? Deadline => dt; public CancellationToken CancellationToken => ct; } - \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs b/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs index 8a63410..72b3aab 100644 --- a/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs +++ b/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs @@ -1,8 +1,11 @@ -using Moq; using FrostFS.Netmap; -using Grpc.Core; + using Google.Protobuf; +using Grpc.Core; + +using Moq; + namespace FrostFS.SDK.Tests; public class NetworkMocker(string key) : ServiceBase(key) diff --git a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs index 7e26a3e..1dc9152 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs @@ -1,12 +1,16 @@ -using Google.Protobuf; -using Grpc.Core; -using Moq; -using FrostFS.SDK.ClientV2; -using FrostFS.Object; -using FrostFS.SDK.ClientV2.Mappers.GRPC; using System.Security.Cryptography; + +using FrostFS.Object; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; +using Google.Protobuf; + +using Grpc.Core; + +using Moq; + namespace FrostFS.SDK.Tests; public class ObjectMocker(string key) : ObjectServiceBase(key) @@ -183,7 +187,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) () => { }); }); } - + return mock; } @@ -195,7 +199,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) public List? ResultObjectIds { get; set; } - public ClientStreamWriter? ClientStreamWriter { get; private set; } = new (); + public ClientStreamWriter? ClientStreamWriter { get; private set; } = new(); public List PutSingleRequests { get; private set; } = []; diff --git a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs index e1800b6..359f10b 100644 --- a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs @@ -1,6 +1,9 @@ using FrostFS.Session; + using Google.Protobuf; + using Grpc.Core; + using Moq; namespace FrostFS.SDK.Tests; diff --git a/src/FrostFS.SDK.Tests/NetworkTest.cs b/src/FrostFS.SDK.Tests/NetworkTest.cs index 11a7ff0..0079d82 100644 --- a/src/FrostFS.SDK.Tests/NetworkTest.cs +++ b/src/FrostFS.SDK.Tests/NetworkTest.cs @@ -1,3 +1,5 @@ +using System.Security.Cryptography; + using FrostFS.Netmap; using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2.Interfaces; @@ -8,8 +10,6 @@ using Google.Protobuf; using Microsoft.Extensions.Options; -using System.Security.Cryptography; - namespace FrostFS.SDK.Tests; public abstract class NetworkTestsBase @@ -40,7 +40,7 @@ public abstract class NetworkTestsBase protected IFrostFSClient GetClient() { - return ClientV2.Client.GetTestInstance( + return ClientV2.FrostFSClient.GetTestInstance( Settings, null, Mocker.GetMock().Object, @@ -88,9 +88,9 @@ public class NetworkTest : NetworkTestsBase } var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - + var result = await GetClient().GetNetworkSettingsAsync(param); - + var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); Assert.NotNull(result); @@ -105,7 +105,7 @@ public class NetworkTest : NetworkTestsBase Assert.Equal(Mocker.Parameters["MaxECParityCount"], [(byte)result.MaxECParityCount]); Assert.Equal(Mocker.Parameters["MaxObjectSize"], [(byte)result.MaxObjectSize]); Assert.Equal(Mocker.Parameters["WithdrawFee"], [(byte)result.WithdrawFee]); - + Assert.True(result.HomomorphicHashingDisabled); Assert.True(result.MaintenanceModeAllowed); @@ -142,13 +142,13 @@ public class NetworkTest : NetworkTestsBase nodeInfo1.Addresses.Add("address1"); nodeInfo1.Addresses.Add("address2"); - nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1"}); + nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); var nodeInfo2 = new NodeInfo { State = NodeInfo.Types.State.Offline, - PublicKey = ByteString.CopyFrom([3,4,5]) + PublicKey = ByteString.CopyFrom([3, 4, 5]) }; nodeInfo2.Addresses.Add("address3"); @@ -190,7 +190,7 @@ public class NetworkTest : NetworkTestsBase Assert.Equal(2, node1.Addresses.Count); Assert.Equal("address1", node1.Addresses.ElementAt(0)); Assert.Equal("address2", node1.Addresses.ElementAt(1)); - + Assert.Equal(2, node1.Attributes.Count); Assert.Equal("key1", node1.Attributes.ElementAt(0).Key); @@ -202,7 +202,7 @@ public class NetworkTest : NetworkTestsBase Assert.Equal(NodeState.Offline, node2.State); Assert.Single(node2.Addresses); Assert.Equal("address3", node2.Addresses.ElementAt(0)); - + Assert.Single(node2.Attributes); Assert.Equal("key3", node2.Attributes.ElementAt(0).Key); @@ -241,10 +241,10 @@ public class NetworkTest : NetworkTestsBase }, Version = new Refs.Version { Major = 2, Minor = 12 } }; - + body.NodeInfo.Addresses.Add("address1"); body.NodeInfo.Addresses.Add("address2"); - body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1"}); + body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); Mocker.NodeInfoResponse = new LocalNodeInfoResponse { Body = body }; @@ -273,11 +273,11 @@ public class NetworkTest : NetworkTestsBase Assert.NotNull(result); Assert.Equal(NodeState.Online, result.State); - + Assert.Equal(2, result.Addresses.Count); Assert.Equal("address1", result.Addresses.ElementAt(0)); Assert.Equal("address2", result.Addresses.ElementAt(1)); - + Assert.Equal(2, result.Attributes.Count); Assert.Equal("value1", result.Attributes["key1"]); Assert.Equal("value2", result.Attributes["key2"]); diff --git a/src/FrostFS.SDK.Tests/ObjectTest.cs b/src/FrostFS.SDK.Tests/ObjectTest.cs index 37fe81a..af58346 100644 --- a/src/FrostFS.SDK.Tests/ObjectTest.cs +++ b/src/FrostFS.SDK.Tests/ObjectTest.cs @@ -1,3 +1,6 @@ +using System.Security.Cryptography; +using System.Text; + using FrostFS.Refs; using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2.Interfaces; @@ -9,9 +12,6 @@ using Google.Protobuf; using Microsoft.Extensions.Options; -using System.Security.Cryptography; -using System.Text; - namespace FrostFS.SDK.Tests; public abstract class ObjectTestsBase @@ -29,7 +29,7 @@ public abstract class ObjectTestsBase protected ObjectTestsBase() { var ecdsaKey = key.LoadWif(); - + Settings = Options.Create(new SingleOwnerClientSettings { Key = key, @@ -46,17 +46,17 @@ public abstract class ObjectTestsBase ContainerId = new FrostFsContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes())); Mocker.ObjectHeader = new( - ContainerId, - FrostFsObjectType.Regular, - [new FrostFsAttribute("k", "v")], + ContainerId, + FrostFsObjectType.Regular, + [new FrostFsAttributePair("k", "v")], null, FrostFsOwner.FromKey(ecdsaKey), new FrostFsVersion(2, 13)); } protected IFrostFSClient GetClient() - { - return Client.GetTestInstance( + { + return FrostFSClient.GetTestInstance( Settings, null, NetworkMocker.GetMock().Object, @@ -75,30 +75,32 @@ public class ObjectTest : ObjectTestsBase var ecdsaKey = key.LoadWif(); - var ctx = new Context { - Key = ecdsaKey, - OwnerId = FrostFsOwner.FromKey(ecdsaKey), - Version = new FrostFsVersion(2, 13) }; + var ctx = new Context + { + Key = ecdsaKey, + OwnerId = FrostFsOwner.FromKey(ecdsaKey), + Version = new FrostFsVersion(2, 13) + }; var objectId = client.CalculateObjectId(Mocker.ObjectHeader!, ctx); - + var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId) { Context = ctx }); Assert.NotNull(result); - Assert.Equal(Mocker.ObjectHeader!.ContainerId.Value, result.Header.ContainerId.Value); + Assert.Equal(Mocker.ObjectHeader!.ContainerId.GetValue(), result.Header.ContainerId.GetValue()); Assert.Equal(Mocker.ObjectHeader!.OwnerId!.Value, result.Header.OwnerId!.Value); Assert.Equal(Mocker.ObjectHeader.PayloadLength, result.Header.PayloadLength); Assert.Single(result.Header.Attributes); Assert.Equal(Mocker.ObjectHeader.Attributes[0].Key, result.Header.Attributes[0].Key); - Assert.Equal(Mocker.ObjectHeader.Attributes[0].Value,result.Header.Attributes[0].Value); + Assert.Equal(Mocker.ObjectHeader.Attributes[0].Value, result.Header.Attributes[0].Value); } [Fact] public async void PutObjectTest() { Mocker.ResultObjectIds = new([SHA256.HashData([])]); - + Random rnd = new(); var bytes = new byte[1024]; rnd.NextBytes(bytes); @@ -119,20 +121,20 @@ public class ObjectTest : ObjectTestsBase Assert.NotNull(result); Assert.Equal(Mocker.ResultObjectIds.First(), result.ToHash()); - + Assert.True(Mocker.ClientStreamWriter.CompletedTask); Assert.Equal(0, body1!.Chunk.Length); Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Init, body1!.ObjectPartCase); Assert.Equal(1024, body2!.Chunk.Length); - Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Chunk, body2!.ObjectPartCase); + Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Chunk, body2!.ObjectPartCase); } [Fact] public async void ClientCutTest() { - NetworkMocker.Parameters = new Dictionary() { { "MaxObjectSize", [0x0, 0xa] } }; + NetworkMocker.Parameters = new Dictionary() { { "MaxObjectSize", [0x0, 0xa] } }; var blockSize = 2560; byte[] bytes = File.ReadAllBytes(@".\..\..\..\TestData\cat.jpg"); @@ -251,11 +253,11 @@ public class ObjectTest : ObjectTestsBase Assert.Equal(Mocker.ObjectId.ToMessage(), request.Body.Address.ObjectId); Assert.NotNull(response); - Assert.Equal(ContainerId.Value, response.ContainerId.Value); + Assert.Equal(ContainerId.GetValue(), response.ContainerId.GetValue()); Assert.Equal(Mocker.ObjectHeader!.OwnerId!.Value, response.OwnerId!.Value); Assert.Equal(Mocker.ObjectHeader!.Version!.ToString(), response.Version!.ToString()); - + Assert.Equal(Mocker.HeadResponse!.PayloadLength, response.PayloadLength); Assert.Equal(FrostFsObjectType.Regular, response.ObjectType); diff --git a/src/FrostFS.SDK.Tests/SessionTests.cs b/src/FrostFS.SDK.Tests/SessionTests.cs index 7eda05e..2e17816 100644 --- a/src/FrostFS.SDK.Tests/SessionTests.cs +++ b/src/FrostFS.SDK.Tests/SessionTests.cs @@ -1,3 +1,5 @@ +using System.Security.Cryptography; + using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.ClientV2.Mappers.GRPC; @@ -6,8 +8,6 @@ using FrostFS.SDK.Cryptography; using Microsoft.Extensions.Options; -using System.Security.Cryptography; - namespace FrostFS.SDK.Tests; public abstract class SessionTestsBase @@ -16,7 +16,7 @@ public abstract class SessionTestsBase protected IOptions Settings { get; set; } - + protected ECDsa ECDsaKey { get; set; } protected FrostFsOwner OwnerId { get; set; } protected SessionMocker Mocker { get; set; } @@ -41,7 +41,7 @@ public abstract class SessionTestsBase protected IFrostFSClient GetClient() { - return ClientV2.Client.GetTestInstance( + return ClientV2.FrostFSClient.GetTestInstance( Settings, null, new NetworkMocker(this.key).GetMock().Object, @@ -100,8 +100,8 @@ public class SessionTest : SessionTestsBase Assert.Equal(exp, Mocker.CreateSessionRequest.Body.Expiration); Assert.NotNull(Mocker.CreateSessionRequest.MetaHeader); Assert.Equal(Mocker.Version.ToMessage(), Mocker.CreateSessionRequest.MetaHeader.Version); - - + + Assert.Null(Mocker.Metadata); if (useContext) diff --git a/src/FrostFS.SDK.Tests/SmokeTests.cs b/src/FrostFS.SDK.Tests/SmokeTests.cs index 548d757..9c86ff9 100644 --- a/src/FrostFS.SDK.Tests/SmokeTests.cs +++ b/src/FrostFS.SDK.Tests/SmokeTests.cs @@ -11,16 +11,16 @@ namespace FrostFS.SDK.SmokeTests; public class SmokeTests : SmokeTestsBase { - private static readonly PrmWait lightWait = new (100, 1); + private static readonly PrmWait lightWait = new(100, 1); [Theory] [InlineData(false)] [InlineData(true)] public async void NetworkMapTest(bool isSingleOnwerClient) { - using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url)); - - PrmNetmapSnapshot? prm = isSingleOnwerClient ? default : new () { Context = Ctx }; + using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url)); + + PrmNetmapSnapshot? prm = isSingleOnwerClient ? default : new() { Context = Ctx }; var result = await client.GetNetmapSnapshotAsync(prm); Assert.True(result.Epoch > 0); @@ -41,7 +41,7 @@ public class SmokeTests : SmokeTestsBase [InlineData(true)] public async void NodeInfoTest(bool isSingleOnwerClient) { - using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url)); + using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url)); PrmNodeInfo? prm = isSingleOnwerClient ? default : new() { Context = Ctx }; @@ -63,9 +63,9 @@ public class SmokeTests : SmokeTestsBase Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds") }; - using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); - var result = await client.GetNodeInfoAsync(); + var result = await client.GetNodeInfoAsync(); } [Theory] @@ -73,7 +73,7 @@ public class SmokeTests : SmokeTestsBase [InlineData(true)] public async void GetSessionTest(bool isSingleOnwerClient) { - using var client = isSingleOnwerClient ? Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : Client.GetInstance(GetOptions(this.url)); + using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url)); PrmSessionCreate? prm = isSingleOnwerClient ? new PrmSessionCreate(100) : new PrmSessionCreate(100) { Context = Ctx }; @@ -82,7 +82,7 @@ public class SmokeTests : SmokeTestsBase var session = new Session.SessionToken().Deserialize(token.Token); var ownerHash = Base58.Decode(OwnerId.Value); - + Assert.NotNull(session); Assert.Null(session.Body.Container); Assert.Null(session.Body.Object); @@ -96,14 +96,14 @@ public class SmokeTests : SmokeTestsBase [Fact] public async void CreateObjectWithSessionToken() { - using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); - + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + await Cleanup(client); var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue)); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))); + new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))); createContainerParam.XHeaders.Add("key1", "value1"); @@ -116,39 +116,44 @@ public class SmokeTests : SmokeTestsBase Header = new FrostFsObjectHeader( containerId: containerId, type: FrostFsObjectType.Regular, - [new FrostFsAttribute("fileName", "test")]), + [new FrostFsAttributePair("fileName", "test")]), Payload = new MemoryStream(bytes), ClientCut = false, - SessionToken = token + SessionToken = token }; - - var objectId = await client.PutObjectAsync(param); - var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId)); + var objectId = await client.PutObjectAsync(param).ConfigureAwait(true); + + var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId)) + .ConfigureAwait(true); var downloadedBytes = new byte[@object.Header.PayloadLength]; MemoryStream ms = new(downloadedBytes); ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) + while ((chunk = await @object.ObjectReader!.ReadChunk().ConfigureAwait(true)) != null) { ms.Write(chunk.Value.Span); } - Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes)); + Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - await Cleanup(client); + await Cleanup(client).ConfigureAwait(true); } [Fact] public async void FilterTest() { - using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + + //var prm = new PrmApeChainList(new FrostFsChainTarget(FrostFsTargetType.Namespace, "root")); + + //var chains = await client.ListChainAsync(prm); await Cleanup(client); var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))) + new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))) { WaitParams = lightWait }; @@ -169,7 +174,7 @@ public class SmokeTests : SmokeTestsBase Header = new FrostFsObjectHeader( containerId: containerId, type: FrostFsObjectType.Regular, - [new FrostFsAttribute("fileName", "test")], + [new FrostFsAttributePair("fileName", "test")], new FrostFsSplit()), Payload = new MemoryStream(bytes), ClientCut = false @@ -189,7 +194,7 @@ public class SmokeTests : SmokeTestsBase await CheckFilter(client, containerId, new FilterBySplitId(FrostFsMatchType.Equals, param.Header.Split.SplitId)); - await CheckFilter(client, containerId, new FilterByAttribute(FrostFsMatchType.Equals, "fileName", "test")); + await CheckFilter(client, containerId, new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test")); await CheckFilter(client, containerId, new FilterByObjectId(FrostFsMatchType.Equals, objectId)); @@ -227,14 +232,14 @@ public class SmokeTests : SmokeTestsBase [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioTest(int objectSize) { - using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); - + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + await Cleanup(client); bool callbackInvoked = false; var ctx = new Context { - // Timeout = TimeSpan.FromSeconds(20), + // Timeout = TimeSpan.FromSeconds(20), Callback = new((CallStatistics cs) => { callbackInvoked = true; @@ -243,7 +248,7 @@ public class SmokeTests : SmokeTestsBase }; var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)),[new ("testKey", "testValue")])) + new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")])) { Context = ctx }; @@ -261,7 +266,7 @@ public class SmokeTests : SmokeTestsBase Header = new FrostFsObjectHeader( containerId: createdContainer, type: FrostFsObjectType.Regular, - [new FrostFsAttribute("fileName", "test")]), + [new FrostFsAttributePair("fileName", "test")]), Payload = new MemoryStream(bytes), ClientCut = false, Context = new Context @@ -272,7 +277,7 @@ public class SmokeTests : SmokeTestsBase var objectId = await client.PutObjectAsync(param); - var filter = new FilterByAttribute(FrostFsMatchType.Equals, "fileName", "test"); + var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); bool hasObject = false; await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] })) @@ -315,7 +320,7 @@ public class SmokeTests : SmokeTestsBase [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioWithSessionTest(int objectSize) { - using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue)); @@ -328,7 +333,7 @@ public class SmokeTests : SmokeTestsBase }; var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))) + new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))) { Context = ctx }; @@ -345,7 +350,7 @@ public class SmokeTests : SmokeTestsBase Header = new FrostFsObjectHeader( containerId: container, type: FrostFsObjectType.Regular, - [new FrostFsAttribute("fileName", "test")]), + [new FrostFsAttributePair("fileName", "test")]), Payload = new MemoryStream(bytes), ClientCut = false, Context = new Context @@ -357,7 +362,7 @@ public class SmokeTests : SmokeTestsBase var objectId = await client.PutObjectAsync(param); - var filter = new FilterByAttribute(FrostFsMatchType.Equals, "fileName", "test"); + var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); bool hasObject = false; await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(container) { Filters = [filter], SessionToken = token })) @@ -402,12 +407,12 @@ public class SmokeTests : SmokeTestsBase [InlineData(2 * 64 * 1024 * 1024 + 256)] [InlineData(200)] public async void ClientCutScenarioTest(int objectSize) - { - using var client = Client.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); + { + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.key, this.url)); await Cleanup(client); - var createContainerParam = new PrmContainerCreate(new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))) + var createContainerParam = new PrmContainerCreate(new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))) { WaitParams = lightWait }; @@ -431,14 +436,14 @@ public class SmokeTests : SmokeTestsBase Header = new FrostFsObjectHeader( containerId: containerId, type: FrostFsObjectType.Regular, - [new FrostFsAttribute("fileName", "test")]), + [new FrostFsAttributePair("fileName", "test")]), Payload = new MemoryStream(bytes), ClientCut = true }; var objectId = await client.PutObjectAsync(param); - var filter = new FilterByAttribute(FrostFsMatchType.Equals, "fileName", "test"); + var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); bool hasObject = false; await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, filter))) @@ -476,16 +481,16 @@ public class SmokeTests : SmokeTestsBase IAsyncEnumerator? enumerator = null; do { - if (deadline <= DateTime.UtcNow) - { - Assert.Fail("Containers exist"); - break; - } + if (deadline <= DateTime.UtcNow) + { + Assert.Fail("Containers exist"); + break; + } - enumerator = client.ListContainersAsync().GetAsyncEnumerator(); - await Task.Delay(500); + enumerator = client.ListContainersAsync().GetAsyncEnumerator(); + await Task.Delay(500); } - while (await enumerator!.MoveNextAsync()); + while (await enumerator!.MoveNextAsync()); } private static byte[] GetRandomBytes(int size) diff --git a/src/FrostFS.SDK.Tests/SmokeTestsBase.cs b/src/FrostFS.SDK.Tests/SmokeTestsBase.cs index 45f3460..5bf7733 100644 --- a/src/FrostFS.SDK.Tests/SmokeTestsBase.cs +++ b/src/FrostFS.SDK.Tests/SmokeTestsBase.cs @@ -8,6 +8,7 @@ namespace FrostFS.SDK.SmokeTests; public abstract class SmokeTestsBase { protected readonly string key = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK"; + protected readonly string url = "http://172.23.32.4:8080"; protected ECDsa Key { get; }