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]
}