using System.Globalization; using static MFiles.Grpc.Samples.MFilesValues; namespace MFiles.Grpc.Samples.Recipes; /// /// Small helpers shared by the recipes: arguments, printing values, test objects. /// public static class RecipeHelpers { /// The client name the recipes log in with. public const string ClientName = "M-Files gRPC recipes"; /// Titles of objects the recipes create start with this. public const string TestPrefix = "[docs-test]"; /// The positional argument at as an integer, or a default. public static int IntArg(string[] args, int index, int defaultValue) => Positional(args).ElementAtOrDefault(index) is { } value ? int.Parse(value, CultureInfo.InvariantCulture) : defaultValue; /// The positional argument at , or a default. public static string? StringArg(string[] args, int index, string? defaultValue = null) => Positional(args).ElementAtOrDefault(index) ?? defaultValue; /// True when the caller asked to keep the objects the recipe creates. public static bool Keep(string[] args) => args.Contains("--keep"); private static IEnumerable Positional(string[] args) => args.Where(a => !a.StartsWith("--")); // [START format-value] /// A typed value as text: lookups show their names and IDs. public static string FormatValue(TypedValue value) { if (value.IsNullValue) return "(empty)"; var data = value.Data; return data.DataCase switch { TypedValueUnion.DataOneofCase.Text => data.Text, TypedValueUnion.DataOneofCase.MultiLineText => data.MultiLineText, TypedValueUnion.DataOneofCase.Integer => data.Integer.ToString(CultureInfo.InvariantCulture), TypedValueUnion.DataOneofCase.Integer64 => data.Integer64.ToString(CultureInfo.InvariantCulture), TypedValueUnion.DataOneofCase.RealNumber => data.RealNumber.ToString(CultureInfo.InvariantCulture), TypedValueUnion.DataOneofCase.DecimalNumber => data.DecimalNumber, TypedValueUnion.DataOneofCase.Boolean => data.Boolean ? "yes" : "no", // A date is midnight UTC of the day. TypedValueUnion.DataOneofCase.Date => data.Date.ToDateTime().ToString("yyyy-MM-dd"), TypedValueUnion.DataOneofCase.Timestamp => data.Timestamp.ToDateTime().ToString("yyyy-MM-dd HH:mm:ss") + " UTC", TypedValueUnion.DataOneofCase.Lookup => FormatLookup(data.Lookup), TypedValueUnion.DataOneofCase.MultiSelectLookup => string.Join("; ", data.MultiSelectLookup.Values.Select(FormatLookup)), _ => $"({value.Type})", }; } private static string FormatLookup(Lookup lookup) => $"{lookup.ValueListItemInfo.Name} (ID {lookup.ValueListItemInfo.ObjId.ItemId.InternalId})"; // [END format-value] /// One line about an object version, the same in every recipe. public static string Describe(ObjectVersionEx item) => $"{item.VersionInfo.Title} (ID {item.ObjectInfo.ObjId.ItemId.InternalId}, " + $"version {item.VersionInfo.Version.InternalVersion}, " + $"modified {item.VersionInfo.LastModifiedAtUtc.ToDateTime():yyyy-MM-dd HH:mm:ss} UTC)"; /// /// Creates a "[docs-test]" document of class "Other Document" (1) without files, for recipes /// that need an object to change. /// public static async Task CreateTestDocumentAsync( MFilesConnection connection, string title, params PropertyValue[] extraProperties) { var objects = new IRPCObjectOperations.IRPCObjectOperationsClient(connection.CallInvoker); var response = await objects.AddObjectWithFilesAsync(new AddObjectWithFilesRequest { ObjectTypeId = 0, Properties = { Property(0, Text($"{TestPrefix} C# {title}")), // Name or title Property(100, Lookup(1, 1)), // Class: Other Document Property(22, Boolean(false)), // Single file: no extraProperties, }, // Named ACL -1, "Full control for all internal users". Without an ACL, not even the // creator could see the object. Acl = new AccessControlList { CustomComponent = new AccessControlListComponent { HasNamedAcl = true, NamedAclId = -1 }, }, CheckIn = true, }); return response.CreatedObject.ObjectVersion; } // [START delete-object] /// /// Deletes an object the recipe created. Deleted objects can be undeleted; destroying one /// (DestroyObject) is final and needs more rights. /// public static async Task DeleteAsync(MFilesConnection connection, ObjID objId) { var objects = new IRPCObjectOperations.IRPCObjectOperationsClient(connection.CallInvoker); await objects.RemoveObjectAsync(new RemoveObjectRequest { ObjId = objId }); Console.WriteLine($"Deleted object {objId.Type}-{objId.ItemId.InternalId}"); } // [END delete-object] }