View Issue Details

IDProjectCategoryView StatusLast Update
0035436FPCFCLpublic2019-12-06 09:55
ReporterDave ConnollyAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Platformx86_64OSLinux 64OS VersionDebian 9.8
Product Version3.3.1Product Build41927 
Target VersionFixed in Version3.3.1 
Summary0035436: Google APIs not working
DescriptionTrying to retrieve a list of Tasks via the Google API results in corrupt data.

Results in error

An unhandled exception occurred at $00000000004CFF90:
ERESTAPI: TTaskLists: unsupported array element type :
  $00000000004CFF90
  $00000000004D05BC
  $00000000004D2A73
  $00000000004D2DA5
  $0000000000457455
Steps To ReproduceUse the example code new.pas
Additional InformationProblem seems to be in restbase.pp
TagsNo tags attached.
Fixed in Revision43654
FPCOldBugId
FPCTarget3.2.0
Attached Files
  • new.pas (4,474 bytes)
    {$mode objfpc}{$H+}
    program help;
    
    uses
    	sysutils,
    	fpoauth2,
    	googlebase, 
    	googleservice, 
    	googleclient, 
    	googletasks, 
    	fphttpwebclient,
    	jsonparser,
    	{$IFDEF VER3_3}
    	opensslsockets,
    	{$ENDIF}
    	inifiles;
    
    Type  
    	TMyObject = Class(TObject)  
    		procedure startUp();
    		procedure DoUserConsent(Const AURL: String; Out AAuthCode: String);
    		procedure LoadAuthConfig();
    		procedure SaveRefreshToken();
    	end;  
    
    
    var
    	
    	M		: TMyObject;
    	client		: TGoogleClient;
    	tasksAPI	: TTasksAPI;
    	taskLists	: TTaskLists;
    	tasks 		: TTasks;
    	Resource 	: TTaskListsResource;
    	i		: longInt;
    	entry		: TTaskList;
      	en 		: AnsiString;
    	i2		: longInt;
    	entry2		: TTask;
      	en2 		: AnsiString;
    	
    procedure TMyObject.startUp();
    begin
    	TTasksAPI.RegisterAPIResources;
    	client := TGoogleClient.Create( nil );
    	
    	client.WebClient := TFPHTTPWebClient.Create( nil );
    	client.WebClient.RequestSigner := client.AuthHandler;
    	client.WebClient.LogFile := 'requests.log';
    	client.AuthHandler.WebClient := client.WebClient;
    	client.AuthHandler.Config.AccessType := atOffLine;
    	// We want to enter a code.
    	client.OnUserConsent := @DoUserConsent;
    	
    	taskLists := TTaskLists.Create();
    
    	tasksAPI := TTasksAPI.Create( nil );
    	tasksAPI.GoogleClient := client;
    end;
    
    procedure TMyObject.DoUserConsent(Const AURL: String; Out AAuthCode: String);
    begin
    	writeln( 'DoUserConsent' );
    	writeln( AURL );
    	writeln();
    	writeln();
    	
    	readln( AAuthCode );	
    	writeln( 'End DoUserConsent' );
    end;
    
    procedure TMyObject.LoadAuthConfig();
    var
      	ini	: TIniFile;
    begin
    	writeln( 'LoadAuthConfig' );
      	ini:=TIniFile.Create('google.ini');
      	try
    		// Registered application needs tasks scope
        		client.AuthHandler.Config.ClientID:=ini.ReadString('Credentials','ClientID','');;
        		client.AuthHandler.Config.ClientSecret:=ini.ReadString('Credentials','ClientSecret','');
        		client.AuthHandler.Config.AuthScope:=ini.ReadString('Credentials','Scope','https://www.googleapis.com/auth/tasks');
       		 // We are offline.
        		client.AuthHandler.Config.RedirectUri:='urn:ietf:wg:oauth:2.0:oob';
        		// Session data
    		client.AuthHandler.Session.RefreshToken:=ini.ReadString('Session','RefreshToken','');
        		client.AuthHandler.Session.AccessToken:=ini.ReadString('Session','AccesToken','');
        		client.AuthHandler.Session.AuthTokenType:=ini.ReadString('Session','TokenType','');
        		client.AuthHandler.Session.AuthExpires:=ini.ReadDateTime('Session','AuthExpires',0);
        		client.AuthHandler.Session.AuthExpiryPeriod:=Ini.ReadInteger('Session','AuthPeriod',0);
      	finally
        		Ini.Free;
    	end;
    	writeln( 'End LoadAuthConfig' );
    end;
    
    
    procedure TMyObject.SaveRefreshToken();
    var
      	ini	: TIniFile;
    begin
    	writeln( 'SaveRefreshToken' );
      	// We save the refresh token for later use.
      	if( client.AuthHandler.Session.RefreshToken <> '' ) then
        	begin
        		ini:=TIniFile.Create('google.ini');
        		try
          			ini.WriteString('Session','RefreshToken',client.AuthHandler.Session.RefreshToken);
          			ini.WriteString('Session','AccessToken',client.AuthHandler.Session.AccessToken);
          			ini.WriteString('Session','TokenType',client.AuthHandler.Session.AuthTokenType);
          			ini.WriteDateTime('Session','AuthExpires',client.AuthHandler.Session.AuthExpires);
          			ini.WriteInteger('Session','AuthPeriod',client.AuthHandler.Session.AuthExpiryPeriod);
        		finally
          			Ini.Free;
        		end;
    	end;
    	writeln( 'End SaveRefreshToken' );
    end;	
    
    
    begin
    	writeln( 'Start' );
    	
    	M := TMyObject.Create();
    	M.startUp();
    		
    	FreeAndNil( taskLists );
    	Resource := nil;
    	writeln( 'Try this' );
    	try
    		M.LoadAuthConfig();
    		writeln( 'Config loaded' );
    		Resource := tasksAPI.CreateTaskListsResource;
    		writeln( 'Resource set' );
    		taskLists := Resource.list();
    		writeln( 'taskLists set' );
    		M.SaveRefreshToken();
    		writeln( 'After saving' );
    		if( assigned( taskLists ) ) then
    		begin
    			writeln( 'YES ', Length( taskLists.Items ) );
    			for i:= 0 to Length( taskLists.Items ) - 1 do
    			begin
    				entry := taskLists.Items[ i ];
    				en := entry.title;
    				writeln( IntToStr( i ) + ': ' + en );
    				FreeAndNil( tasks );
    				tasks := tasksAPI.TasksResource.list( entry.id, '' );
    				if( assigned( tasks ) ) then
    				begin
    					for i2 := 0 to Length( tasks.items ) - 1 do
    					begin
    						entry2 := tasks.Items[ i2 ];
    						en2 := entry2.title;
    						writeln( '	', IntToStr( i2 ) + ': ' + en2 + ' ' + entry2.id + ' ' + entry2.parent );
    					end;
    				end;
    			end;
    		end;
    	finally
    		FreeAndNil( Resource );
    	end;
    end.
    new.pas (4,474 bytes)

Activities

Dave Connolly

2019-04-24 11:01

reporter  

new.pas (4,474 bytes)
{$mode objfpc}{$H+}
program help;

uses
	sysutils,
	fpoauth2,
	googlebase, 
	googleservice, 
	googleclient, 
	googletasks, 
	fphttpwebclient,
	jsonparser,
	{$IFDEF VER3_3}
	opensslsockets,
	{$ENDIF}
	inifiles;

Type  
	TMyObject = Class(TObject)  
		procedure startUp();
		procedure DoUserConsent(Const AURL: String; Out AAuthCode: String);
		procedure LoadAuthConfig();
		procedure SaveRefreshToken();
	end;  


var
	
	M		: TMyObject;
	client		: TGoogleClient;
	tasksAPI	: TTasksAPI;
	taskLists	: TTaskLists;
	tasks 		: TTasks;
	Resource 	: TTaskListsResource;
	i		: longInt;
	entry		: TTaskList;
  	en 		: AnsiString;
	i2		: longInt;
	entry2		: TTask;
  	en2 		: AnsiString;
	
procedure TMyObject.startUp();
begin
	TTasksAPI.RegisterAPIResources;
	client := TGoogleClient.Create( nil );
	
	client.WebClient := TFPHTTPWebClient.Create( nil );
	client.WebClient.RequestSigner := client.AuthHandler;
	client.WebClient.LogFile := 'requests.log';
	client.AuthHandler.WebClient := client.WebClient;
	client.AuthHandler.Config.AccessType := atOffLine;
	// We want to enter a code.
	client.OnUserConsent := @DoUserConsent;
	
	taskLists := TTaskLists.Create();

	tasksAPI := TTasksAPI.Create( nil );
	tasksAPI.GoogleClient := client;
end;

procedure TMyObject.DoUserConsent(Const AURL: String; Out AAuthCode: String);
begin
	writeln( 'DoUserConsent' );
	writeln( AURL );
	writeln();
	writeln();
	
	readln( AAuthCode );	
	writeln( 'End DoUserConsent' );
end;

procedure TMyObject.LoadAuthConfig();
var
  	ini	: TIniFile;
begin
	writeln( 'LoadAuthConfig' );
  	ini:=TIniFile.Create('google.ini');
  	try
		// Registered application needs tasks scope
    		client.AuthHandler.Config.ClientID:=ini.ReadString('Credentials','ClientID','');;
    		client.AuthHandler.Config.ClientSecret:=ini.ReadString('Credentials','ClientSecret','');
    		client.AuthHandler.Config.AuthScope:=ini.ReadString('Credentials','Scope','https://www.googleapis.com/auth/tasks');
   		 // We are offline.
    		client.AuthHandler.Config.RedirectUri:='urn:ietf:wg:oauth:2.0:oob';
    		// Session data
		client.AuthHandler.Session.RefreshToken:=ini.ReadString('Session','RefreshToken','');
    		client.AuthHandler.Session.AccessToken:=ini.ReadString('Session','AccesToken','');
    		client.AuthHandler.Session.AuthTokenType:=ini.ReadString('Session','TokenType','');
    		client.AuthHandler.Session.AuthExpires:=ini.ReadDateTime('Session','AuthExpires',0);
    		client.AuthHandler.Session.AuthExpiryPeriod:=Ini.ReadInteger('Session','AuthPeriod',0);
  	finally
    		Ini.Free;
	end;
	writeln( 'End LoadAuthConfig' );
end;


procedure TMyObject.SaveRefreshToken();
var
  	ini	: TIniFile;
begin
	writeln( 'SaveRefreshToken' );
  	// We save the refresh token for later use.
  	if( client.AuthHandler.Session.RefreshToken <> '' ) then
    	begin
    		ini:=TIniFile.Create('google.ini');
    		try
      			ini.WriteString('Session','RefreshToken',client.AuthHandler.Session.RefreshToken);
      			ini.WriteString('Session','AccessToken',client.AuthHandler.Session.AccessToken);
      			ini.WriteString('Session','TokenType',client.AuthHandler.Session.AuthTokenType);
      			ini.WriteDateTime('Session','AuthExpires',client.AuthHandler.Session.AuthExpires);
      			ini.WriteInteger('Session','AuthPeriod',client.AuthHandler.Session.AuthExpiryPeriod);
    		finally
      			Ini.Free;
    		end;
	end;
	writeln( 'End SaveRefreshToken' );
end;	


begin
	writeln( 'Start' );
	
	M := TMyObject.Create();
	M.startUp();
		
	FreeAndNil( taskLists );
	Resource := nil;
	writeln( 'Try this' );
	try
		M.LoadAuthConfig();
		writeln( 'Config loaded' );
		Resource := tasksAPI.CreateTaskListsResource;
		writeln( 'Resource set' );
		taskLists := Resource.list();
		writeln( 'taskLists set' );
		M.SaveRefreshToken();
		writeln( 'After saving' );
		if( assigned( taskLists ) ) then
		begin
			writeln( 'YES ', Length( taskLists.Items ) );
			for i:= 0 to Length( taskLists.Items ) - 1 do
			begin
				entry := taskLists.Items[ i ];
				en := entry.title;
				writeln( IntToStr( i ) + ': ' + en );
				FreeAndNil( tasks );
				tasks := tasksAPI.TasksResource.list( entry.id, '' );
				if( assigned( tasks ) ) then
				begin
					for i2 := 0 to Length( tasks.items ) - 1 do
					begin
						entry2 := tasks.Items[ i2 ];
						en2 := entry2.title;
						writeln( '	', IntToStr( i2 ) + ': ' + en2 + ' ' + entry2.id + ' ' + entry2.parent );
					end;
				end;
			end;
		end;
	finally
		FreeAndNil( Resource );
	end;
end.
new.pas (4,474 bytes)

Dave Connolly

2019-04-24 11:04

reporter   ~0115769

Works in 3.0.4

Michael Van Canneyt

2019-12-06 09:55

administrator   ~0119654

Took some time to get around to this, but fixed it. RTTI data format changed, fixed the api to use the new format.
Thanks for reporting !

Issue History

Date Modified Username Field Change
2019-04-24 11:01 Dave Connolly New Issue
2019-04-24 11:01 Dave Connolly File Added: new.pas
2019-04-24 11:04 Dave Connolly Note Added: 0115769
2019-04-24 14:46 Michael Van Canneyt Assigned To => Michael Van Canneyt
2019-04-24 14:46 Michael Van Canneyt Status new => assigned
2019-12-06 09:55 Michael Van Canneyt Status assigned => resolved
2019-12-06 09:55 Michael Van Canneyt Resolution open => fixed
2019-12-06 09:55 Michael Van Canneyt Fixed in Version => 3.3.1
2019-12-06 09:55 Michael Van Canneyt Fixed in Revision => 43654
2019-12-06 09:55 Michael Van Canneyt FPCTarget => 3.2.0
2019-12-06 09:55 Michael Van Canneyt Note Added: 0119654