2014-11-20 10:01:06 +03:00
using System ;
using System.Collections.Generic ;
2014-12-03 07:24:55 +03:00
using System.Net.Http ;
2014-11-20 10:01:06 +03:00
using System.Xml ;
namespace Microsoft.Office365.ReportingWebServiceClient
{
public class ReportingStream
{
2014-12-03 07:24:55 +03:00
#region Privates
2014-11-20 10:01:06 +03:00
private ReportingContext reportingContext ;
private ReportProvider reportProvider ;
private Type reportType ;
2014-12-17 05:36:10 +03:00
private string streamIdentifier = string . Empty ;
private string progressFilePath = string . Empty ;
2014-12-03 07:24:55 +03:00
#endregion Privates
#region Constructors
2014-11-20 10:01:06 +03:00
/// <summary>
///
/// </summary>
/// <param name="serviceEndpoint"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
2014-12-17 05:36:10 +03:00
public ReportingStream ( ReportingContext context , string reportType , string streamIdentifier , string progressFilePath = null )
2014-11-20 10:01:06 +03:00
{
this . reportingContext = context ;
this . streamIdentifier = streamIdentifier ;
2014-12-17 05:36:10 +03:00
this . progressFilePath = progressFilePath ;
2014-11-20 10:01:06 +03:00
this . reportProvider = new ReportProvider ( context . WebServiceUrl , context . UserName , context . Password , context . TraceLogger ) ;
this . reportType = Type . GetType ( "Microsoft.Office365.ReportingWebServiceClient.TenantReport." + reportType ) ;
if ( ! this . reportType . IsSubclassOf ( typeof ( ReportObject ) ) | | ! this . reportType . IsSerializable )
{
throw new ArgumentException ( string . Format ( "Report Type Must be Subclass of ReportObject and Serializable: {0}" , this . reportType . FullName ) ) ;
}
}
2014-12-03 07:24:55 +03:00
#endregion Constructors
2014-11-20 10:01:06 +03:00
2014-12-03 07:24:55 +03:00
#region Private methods
2014-11-20 10:01:06 +03:00
/// <summary>
///
/// </summary>
/// <param name="visitor"></param>
/// <returns>The count of data returned</returns>
private int RetrieveData ( IReportVisitor visitor , QueryFilter filter )
{
int totalResultCount = 0 ;
2014-12-03 07:24:55 +03:00
//If the TopCount is 0, then it was not specified, hence we take the constant value
if ( filter . TopCount = = 0 )
filter . TopCount = Constants . ResultPageSize ;
2014-11-20 10:01:06 +03:00
List < XmlNode > resultNodes = reportProvider . GetResponseXml ( this . reportType , filter ) ;
if ( resultNodes . Count > = Constants . ResultPageSize & & filter . SkipCount = = 0 )
{
reportingContext . TraceLogger . LogInformation ( "Result is exceeding limit, dividing the range." ) ;
List < QueryRange > subRangeList = filter . QueryRange . GetDividedRanges ( ) ;
if ( subRangeList ! = null & & subRangeList . Count > 0 )
{
foreach ( QueryRange range in subRangeList )
{
filter . QueryRange = range ;
totalResultCount + = RetrieveData ( visitor , filter ) ;
}
}
else
{
VisitXmlNodes ( resultNodes , visitor ) ;
totalResultCount = resultNodes . Count ;
reportingContext . TraceLogger . LogInformation ( string . Format ( "Retrieved [{0}] rows of data..." , totalResultCount ) ) ;
reportingContext . TraceLogger . LogInformation ( "Divided range is null, using Skips." ) ;
int subResult ;
filter . SkipCount = 0 ;
do
{
filter . SkipCount + = Constants . ResultPageSize ;
subResult = RetrieveData ( visitor , filter ) ;
totalResultCount + = subResult ;
} while ( subResult > = Constants . ResultPageSize ) ;
filter . SkipCount = 0 ;
}
}
else
{
VisitXmlNodes ( resultNodes , visitor ) ;
totalResultCount = resultNodes . Count ;
reportingContext . TraceLogger . LogInformation ( string . Format ( "Retrieved [{0}] rows of data..." , resultNodes . Count ) ) ;
}
2014-12-17 05:36:10 +03:00
StreamProgress progress = new StreamProgress ( this . progressFilePath , streamIdentifier , filter . QueryRange . EndDate , false ) ;
2014-12-15 08:11:13 +03:00
progress . SaveProgress ( ) ;
2014-11-20 10:01:06 +03:00
return totalResultCount ;
}
/// <summary>
///
/// </summary>
/// <typeparam name="TReport"></typeparam>
/// <param name="nodes"></param>
/// <param name="visitor"></param>
/// <returns></returns>
private List < ReportObject > VisitXmlNodes ( List < XmlNode > nodes , IReportVisitor visitor )
{
List < ReportObject > list = new List < ReportObject > ( ) ;
DateTime lastTimeStamp = DateTime . MinValue ;
visitor . Reset ( ) ;
foreach ( XmlNode node in nodes )
{
ReportObject report = ( ReportObject ) Activator . CreateInstance ( this . reportType ) ;
report . LoadFromXml ( node ) ;
list . Add ( report ) ;
visitor . AddReportToBatch ( report ) ;
lastTimeStamp = ( lastTimeStamp < report . Date ) ? report . Date : lastTimeStamp ;
}
visitor . VisitBatchReport ( ) ;
2014-12-17 05:36:10 +03:00
StreamProgress progress = new StreamProgress ( this . progressFilePath , streamIdentifier , lastTimeStamp , true ) ;
2014-11-20 10:01:06 +03:00
progress . SaveProgress ( ) ;
return list ;
}
2014-12-03 07:24:55 +03:00
#endregion Private methods
/// <summary>
/// This is a simple method that tries to fetch 1 record of the specified report
/// and if 0 or 1 record return then the authN & authZ to this report is validated
/// otherwise throws an exception
/// </summary>
/// <returns></returns>
public bool ValidateAccessToReport ( )
{
try
{
IReportVisitor visitor = new DefaultReportVisitor ( ) ;
int res = RetrieveData ( visitor , new QueryFilter ( ) { TopCount = 1 } ) ;
if ( res = = 0 | | res = = 1 )
return true ;
else
throw new ApplicationException ( "Tried to validate your credentials against the report specified, however the response we received from the server did not match what we expected to receive. Please try again." ) ;
}
catch ( Exception ex )
{
if ( ex is AggregateException )
{
if ( ex . InnerException is HttpRequestException )
{
//Indicates that this is an HTTP request unauthorized exception
if ( ex . InnerException . Message . Contains ( "401" ) )
return false ;
else
throw ex ;
}
else
throw ex ;
}
else
throw ex ;
}
}
/// <summary>
///
/// </summary>
public void ClearProgress ( )
{
2014-12-17 05:36:10 +03:00
StreamProgress progress = new StreamProgress ( this . progressFilePath , this . streamIdentifier ) ;
progress . ClearProgress ( ) ;
2014-12-03 07:24:55 +03:00
}
/// <summary>
/// This RetrieveData method the built-in ReportVisitor (Console)
/// </summary>
public void RetrieveData ( )
{
IReportVisitor visitor = new DefaultReportVisitor ( ) ;
RetrieveData ( visitor ) ;
}
/// <summary>
///
/// </summary>
/// <param name="visitor"></param>
public void RetrieveData ( IReportVisitor visitor )
{
reportingContext . TraceLogger . LogInformation ( string . Format ( "Start Retrieving Data For Report {0}" , this . reportType . Name ) ) ;
QueryFilter queryFilter = new QueryFilter ( ) ;
queryFilter . QueryRange . StartDate = this . reportingContext . FromDateTime ;
queryFilter . QueryRange . EndDate = this . reportingContext . ToDateTime ;
queryFilter . CustomFilter = this . reportingContext . DataFilter ;
2014-12-17 05:36:10 +03:00
StreamProgress progress = new StreamProgress ( this . progressFilePath , this . streamIdentifier ) ;
progress = progress . GetProgress ( ) ;
2014-12-03 07:24:55 +03:00
DateTime progressTimestamp = progress . TimeStamp ;
2014-12-17 05:36:10 +03:00
2014-12-03 07:24:55 +03:00
if ( queryFilter . QueryRange . StartDate < progressTimestamp )
queryFilter . QueryRange . StartDate = progressTimestamp ;
2014-12-15 08:11:13 +03:00
queryFilter . ExcludeStartItem = progress . ExcludeStartItem ;
2014-12-03 07:24:55 +03:00
int totalCount = RetrieveData ( visitor , queryFilter ) ;
reportingContext . TraceLogger . LogInformation ( string . Format ( "Retrieve Data Completed. Totally [{0}] of Data Retrieved." , totalCount ) ) ;
}
2014-11-20 10:01:06 +03:00
}
}