Jump to content


laurent_tr

Member Since 10 Jun 2019
Offline Last Active Aug 19 2019 01:47 PM
-----

Topics I've Started

P4API.NET batch download file using GetFileContentEx()

16 July 2019 - 03:04 PM

Hello,

I want to use the GetFileContentEx() to download multiple files at once.
I have tested this with 2 scenarii,
1- Batch download text files only
2- Batch download text files and binary files (in my case the binary is a docx)

In the first case, the content of both text files are present in the API result but in the same string object with no clear separation in order for me to split them programmatically.
I have also tried leaving the file information in the result of GetFileContentEx() but the file information are separately stored at the head of result list.


In the second case, the result is in byte array and I cannot determine if the content of the demanded files are there or not, which makes it even harder to split the content of the two input files.

I would like to know if there is an option / a way to have GetFileContentEx() return the file content separately, each in an element of the result.

Following is my snippet of code to reproduce the test scenarii. In the attachment you can find the standard output of my 2 test scenarii.Attached File  TestPerforce-with-binary-files.txt   17.52K   27 downloads, Attached File  TestPerforce-without-binary-files.txt   935bytes   27 downloads
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perforce.P4;
namespace TestPerforce
{
class Program
{
	 static void Main(string[] args)
	 {
		 // Test API
		 if (!Connect())
		 {
			 return;
		 }
		 LogProgress($"Connected to {ServerUri}.");
		 TestBatchDownloadFiles();
		 if (!Disconnect())
		 {
			 LogError("Could not disconnect from Perforce server.");
		 }
		 LogProgress("Disconnection succeeds.");
		 Console.WriteLine("Press any key to exit.");
		 Console.ReadKey();
	 }
	 private static string ServerUri = string.Empty;
	 private static string User = string.Empty;
	 private static string Password = string.Empty;
	 private static Repository P4Repository;
	 private static Connection P4Connection;
	 private static DateTime TicketExpirationTime;
	 private static IList<FileSpec>Files = new List<FileSpec>
	 {
		 new DepotPath("//test/root/limited_to_group1/helloworld.go"),
		 new DepotPath("//test/root/limited_to_group1/loop.go"),
		 new DepotPath("//test/root/limited_to_group1/text.docx"),
	 };
	 private static bool Connect()
	 {
		 // define the server, repository and connection
		 var server = new Server(new ServerAddress(ServerUri));
		 P4Repository = new Repository(server);
		 P4Connection = P4Repository.Connection;		 // use the connection variables for this connection
		 P4Connection.UserName = User;
		 P4Connection.Client = new Client();
		 var options = new Options();
		 options["ProgramName"] = "TestPerforceApi";
		 options["ProgramVersion"] = "2019.1.177.4442";		 // connect to the server
		 try
		 {
			 if (!P4Connection.Connect(options))
			 {
				 LogError($"Connect could not connect to Perforce server at {ServerUri}, with the user {User}.");
				 return false;
			 }
			 var credential = P4Connection.Login(Password);
			 TicketExpirationTime = credential.Expires;
			 LogProgress($"Connect obtains new validation ticket, valid until {TicketExpirationTime.ToString(CultureInfo.InvariantCulture)}.");
			 return true;
		 }
		 catch (Exception e)
		 {
			 LogException($"Connect could not connect to Perforce server at {ServerUri}, with the user {User}.", e);
			 return false;
		 }
	 }
	 private static bool Disconnect()
	 {
		 if (P4Connection != null)
		 {
			 P4Connection.Dispose();
			 P4Connection = null;
			 P4Repository = null;
		 }
		 return true;
	 }	

	 private static bool IsTicketExpired() => TicketExpirationTime == default || DateTime.UtcNow > TicketExpirationTime;
	
private static bool RefreshTicket()
	 {
		 if (P4Connection == null)
		 {
			 LogError("RefreshTicket there is no connection to refresh the authentication ticket.");
			 return false;
		 }
		 if (P4Connection.Status != ConnectionStatus.Connected)
		 {
			 LogError("RefreshTicket the connection is disconnected.");
			 return false;
		 }
		 var credential = P4Connection.Login(Password);
		 TicketExpirationTime = credential.Expires;
		 LogProgress($"RefreshTicket obtains new validation ticket, valid until {TicketExpirationTime.ToString(CultureInfo.InvariantCulture)}.");
		 return true;
	 }
	 private static bool TestBatchDownloadFiles()
	 {
		 LogProgress("Metadata");
		 // Get file metadata
		 {
			 var options = new GetFileMetaDataCmdOptions(
				 GetFileMetadataCmdFlags.FileSize,
				 null, null, 0, null, null, null);
			 var res = P4Repository.GetFileMetaData(Files, options);
			 for (var i = 0; i < res.Count; i++)
			 {
				 var metadata = res[i];
				 var sb = new StringBuilder()
					 .Append("File #").Append(i+1).Append(" ")
					 .Append("Name: ").Append(metadata.DepotPath.GetFileName()).Append(", ")
					 .Append("size: ").Append(metadata.FileSize).Append(", ")
					 .Append("modified: ").Append($"{metadata.HeadTime.ToString("yyyy/mm/dd", CultureInfo.InvariantCulture)}");
				 LogProgress(sb.ToString());
			 }
		 }
		 LogProgress("--------------------");
		 LogProgress("Content");
		 // Get file content
		 {
			 var options = new GetFileContentsCmdOptions(GetFileContentsCmdFlags.None, null);
			 var res = P4Repository.GetFileContentsEx(Files, options);
			 for (var i = 0; i < res.Count; i++)
			 {
				 LogProgress($"--- Element #{i+1}:");
				 var elem = res[i];
				 switch (elem)
				 {
					 case FileSpec spec:
						 LogProgress(spec.DepotPath.GetFileName());
						 break;
					 case string s:
						 LogProgress(s);
						 break;
					 case byte[] byteArray:
						 LogProgress(Encoding.Default.GetString(byteArray));
						 break;
				 }
			 }
		 }		 return true;
	 }
	 private static void LogProgress(string message) => Console.WriteLine(message);
	 private static void LogError(string message) => Console.WriteLine($"Error: {message}");
	 private static void LogException(string message, Exception ex) => Console.WriteLine($"Error: {message}, exception: {ex}");
}
}

P4API.NET connects to Perforce server via rsh protocol

25 June 2019 - 06:49 AM

Hello,

One of our clients has configured their Perforce server to be connected only via rsh protocol/
In general, Perforce users will have to generate a pair of private-public keys, the public key is stored in an internal SSHD server, and the private key is loaded in a Putty agent (we all use Windows workstation).
The P4PORT is then set to something like this: "rsh:plink.exe -ssh -batch -a -x -l p4user some.server.com ...."
In such a way, they no longer need to authenticate with "p4 login", instead they issue the command that they want, "p4 info" for example, their private key is verified with the public key and if it's matched, the p4 command is transferred to the Perforce server at some.server.com.

I have tried to find some documentation about the support of this rsh protocol on P4 documentation but the only one I have found, that's come from Perforce is this: https://www.perforce...ew/tsld020.htm.
What I want to know is how to make this works with the P4API.NET. In particular, how will I create the connection with the API?
Following is the snippet of code that I use to connect to our Perforce testing environment, which does not have the same authentication configuration as the client in question.

public bool Connect()
{
// define the server, repository and connection
_server = new Server(new ServerAddress(_cntr._serverUri));
_repository = new Repository( _server);
_connection = _repository.Connection;
// use the connection variables for this connection
_connection.UserName = _cntr._user;
_connection.Client = new Client();
var options = new Options();
options["ProgramName"] = ApplicationName;
options["ProgramVersion"] = ApiVersion;
// connect to the server
try
{
	 if (!_connection.Connect(options))
	 {
		 Sys.LogError($"{nameof(PerforceClient)}.Connect could not connect to Perforce server at {_cntr._serverUri}, with the user {_cntr._user}.");
		 return false;
	 }
	 var credential = _connection.Login(_cntr._pwd.Password);
	 // Save the ticket expiration time, remove 5 min to be more proactive on the expiration time
	 _ticketExpirationTime = Dat.MinSub(credential.Expires, 5);
	 return true;
}
catch (Exception e)
{
	 Sys.LogError($"{nameof(PerforceClient)}.Connect could not connect to Perforce server at {_cntr._serverUri}, with the user {_cntr._user}.", e);
	 return false;
}
}

Thank you very much for your help.

P4API.NET DepotType enum does not have an entry for Graph depot

19 June 2019 - 07:50 AM

Hello,

I am using the p4api.net dll version 2019.1.177.4442 and I have noticed that the DepotType enum does not have an entry for Graph depot.
Currently I do not have any Graph depot in my Perforce server but I plan to have one.
Therefore I would like to know if the "GetDepots" method returns all depots in the server including Graph ones.

Thanks.

P4API.NET How to check if a user/group can access a file on a given host.

10 June 2019 - 07:34 AM

Hello all,

I would like to verify if a given user has access to a given file without having to interpret the whole protection table.
It is said from the documentation of the "p4 protects" that this could be done by specifying the -M parameter ; which I do not manage to find an equivalent in the .NET API. There is the flag GetProtectionEntriesCmdFlags.AccessSummary which is equivalent to -m.

Anyhow, I have tried to use this .NET flag, and the API keeps throwing a KeyNotFoundException, without any information on which key it is complaining about. The reported line number does not match any line from my Decompiler.

Here is the Exception details as reported by Visual studio
System.Collections.Generic.KeyNotFoundException
  HResult=0x80131577
  Message=The given key was not present in the dictionary.
  Source=mscorlib
  StackTrace:
   at System.ThrowHelper.ThrowKeyNotFoundException()
   at Perforce.P4.Repository.GetProtectionEntries(IList`1 filespecs, Options options) in c:\tmp\99902514\P4.NET\r19.1\p4api.net\p4api.net\Repository.cs:line 2830
   at ...GetAccessSummary() in ...Test.cs:line 316
   at ...MainClass.Main(String[] args) in ...MainClass.cs:line 9

Do you have any suggestion about where the cause could be ?

Here is the snippet of code that reproduces the exception. I am using the version 2019.1.177.4442.
internal static int GetAccessSummary()
{
	// initialize the connection variables
	// note: this is a connection without using a password
	var uri = "";
	var user = "";
	var pwd = "";
	var ws_client = "admin_space";

	// define the server, repository and connection
	var server = new Server(new ServerAddress(uri));
	var repository = new Repository(server);
	var connection = repository.Connection;

	// use the connection variables for this connection
	connection.UserName = user;
	connection.Client = new Client();
	connection.Client.Name = ws_client;

	// connect to the server
	var bOk = connection.Connect(null);
	if (!bOk)
	{
		return -1;
	}
	connection.Login(pwd);
	var userToCheck = "";
	var opts = new GetProtectionEntriesCmdOptions(GetProtectionEntriesCmdFlags.AccessSummary, null, userToCheck, null);
	var res = repository.GetProtectionEntries(new List<FileSpec>()
	{
		new FileSpec(new DepotPath(""), null, null, null)
	}, opts);

	return 0;
}