-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
849 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Configuration; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Documents.Client; | ||
using Microsoft.Azure.WebJobs.Host; | ||
using TollBooth.Models; | ||
|
||
namespace TollBooth | ||
{ | ||
internal class DatabaseMethods | ||
{ | ||
private readonly string _endpointUrl = ConfigurationManager.AppSettings["cosmosDBEndPointUrl"]; | ||
private readonly string _authorizationKey = ConfigurationManager.AppSettings["cosmosDBAuthorizationKey"]; | ||
private readonly string _databaseId = ConfigurationManager.AppSettings["cosmosDBDatabaseId"]; | ||
private readonly string _collectionId = ConfigurationManager.AppSettings["cosmosDBCollectionId"]; | ||
private readonly TraceWriter _log; | ||
// Reusable instance of DocumentClient which represents the connection to a Cosmos DB endpoint. | ||
private DocumentClient _client; | ||
|
||
public DatabaseMethods(TraceWriter log) | ||
{ | ||
_log = log; | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves all license plate records (documents) that have not yet been exported. | ||
/// </summary> | ||
/// <returns></returns> | ||
public List<LicensePlateDataDocument> GetLicensePlatesToExport() | ||
{ | ||
_log.Info("Retrieving license plates to export"); | ||
int exportedCount = 0; | ||
var collectionLink = UriFactory.CreateDocumentCollectionUri(_databaseId, _collectionId); | ||
List<LicensePlateDataDocument> licensePlates; | ||
|
||
using (_client = new DocumentClient(new Uri(_endpointUrl), _authorizationKey)) | ||
{ | ||
// MaxItemCount value tells the document query to retrieve 100 documents at a time until all are returned. | ||
// TODO 5: Retrieve a List of LicensePlateDataDocument objects from the collectionLink where the exported value is false. | ||
// COMPLETE: licensePlates = _client.CreateDocumentQuery ... | ||
// TODO 6: Remove the line below. | ||
licensePlates = new List<LicensePlateDataDocument>(); | ||
} | ||
|
||
exportedCount = licensePlates.Count(); | ||
_log.Info($"{exportedCount} license plates found that are ready for export"); | ||
return licensePlates; | ||
} | ||
|
||
/// <summary> | ||
/// Updates license plate records (documents) as exported. Call after successfully | ||
/// exporting the passed in license plates. | ||
/// In a production environment, it would be best to create a stored procedure that | ||
/// bulk updates the set of documents, vastly reducing the number of transactions. | ||
/// </summary> | ||
/// <param name="licensePlates"></param> | ||
/// <returns></returns> | ||
public async Task MarkLicensePlatesAsExported(IEnumerable<LicensePlateDataDocument> licensePlates) | ||
{ | ||
_log.Info("Updating license plate documents exported values to true"); | ||
var collectionLink = UriFactory.CreateDocumentCollectionUri(_databaseId, _collectionId); | ||
|
||
using (_client = new DocumentClient(new Uri(_endpointUrl), _authorizationKey)) | ||
{ | ||
foreach (var licensePlate in licensePlates) | ||
{ | ||
licensePlate.exported = true; | ||
var response = await _client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(_databaseId, _collectionId, licensePlate.Id), licensePlate); | ||
|
||
var updated = response.Resource; | ||
//_log.Info($"Exported value of updated document: {updated.GetPropertyValue<bool>("exported")}"); | ||
} | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.WebJobs; | ||
using Microsoft.Azure.WebJobs.Extensions.Http; | ||
using Microsoft.Azure.WebJobs.Host; | ||
|
||
namespace TollBooth | ||
{ | ||
public static class ExportLicensePlates | ||
{ | ||
[FunctionName("ExportLicensePlates")] | ||
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log) | ||
{ | ||
int exportedCount = 0; | ||
log.Info("Finding license plate data to export"); | ||
|
||
var databaseMethods = new DatabaseMethods(log); | ||
var licensePlates = databaseMethods.GetLicensePlatesToExport(); | ||
if (licensePlates.Any()) | ||
{ | ||
log.Info($"Retrieved {licensePlates.Count} license plates"); | ||
var fileMethods = new FileMethods(log); | ||
var uploaded = await fileMethods.GenerateAndSaveCsv(licensePlates); | ||
if (uploaded) | ||
{ | ||
await databaseMethods.MarkLicensePlatesAsExported(licensePlates); | ||
exportedCount = licensePlates.Count; | ||
log.Info("Finished updating the license plates"); | ||
} | ||
else | ||
{ | ||
log.Info( | ||
"Export file could not be uploaded. Skipping database update that marks the documents as exported."); | ||
} | ||
|
||
log.Info($"Exported {exportedCount} license plates"); | ||
} | ||
else | ||
{ | ||
log.Info("No license plates to export"); | ||
} | ||
|
||
return exportedCount == 0 | ||
? req.CreateResponse(HttpStatusCode.NoContent) | ||
: req.CreateResponse(HttpStatusCode.OK, $"Exported {exportedCount} license plates"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Configuration; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using CsvHelper; | ||
using Microsoft.Azure.WebJobs.Host; | ||
using Microsoft.WindowsAzure.Storage; | ||
using Microsoft.WindowsAzure.Storage.Blob; | ||
using TollBooth.Models; | ||
|
||
namespace TollBooth | ||
{ | ||
internal class FileMethods | ||
{ | ||
private readonly CloudBlobClient _blobClient; | ||
private readonly string _containerName = ConfigurationManager.AppSettings["exportCsvContainerName"]; | ||
private readonly string _blobStorageConnection = ConfigurationManager.AppSettings["blobStorageConnection"]; | ||
private readonly TraceWriter _log; | ||
|
||
public FileMethods(TraceWriter log) | ||
{ | ||
_log = log; | ||
// Retrieve storage account information from connection string. | ||
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_blobStorageConnection); | ||
|
||
// Create a blob client for interacting with the blob service. | ||
_blobClient = storageAccount.CreateCloudBlobClient(); | ||
} | ||
|
||
public async Task<bool> GenerateAndSaveCsv(IEnumerable<LicensePlateDataDocument> licensePlates) | ||
{ | ||
var successful = false; | ||
|
||
_log.Info("Generating CSV file"); | ||
string blobName = $"{DateTime.UtcNow.ToString("s")}.csv"; | ||
|
||
using (var stream = new MemoryStream()) | ||
{ | ||
using (var textWriter = new StreamWriter(stream)) | ||
using (var csv = new CsvWriter(textWriter)) | ||
{ | ||
csv.Configuration.Delimiter = ","; | ||
csv.WriteRecords(licensePlates.Select(ToLicensePlateData)); | ||
await textWriter.FlushAsync(); | ||
|
||
_log.Info($"Beginning file upload: {blobName}"); | ||
try | ||
{ | ||
var container = _blobClient.GetContainerReference(_containerName); | ||
|
||
// Retrieve reference to a blob. | ||
var blob = container.GetBlockBlobReference(blobName); | ||
await container.CreateIfNotExistsAsync(); | ||
|
||
// Upload blob. | ||
stream.Position = 0; | ||
// TODO 7: Asyncronously upload the blob from the memory stream. | ||
// COMPLETE: await blob...; | ||
|
||
successful = true; | ||
} | ||
catch (Exception e) | ||
{ | ||
_log.Error($"Could not upload CSV file: {e.Message}", e); | ||
successful = false; | ||
} | ||
} | ||
} | ||
|
||
return successful; | ||
} | ||
|
||
/// <summary> | ||
/// Used for mapping from a LicensePlateDataDocument object to a LicensePlateData object. | ||
/// </summary> | ||
/// <param name="source"></param> | ||
/// <returns></returns> | ||
private static LicensePlateData ToLicensePlateData(LicensePlateDataDocument source) | ||
{ | ||
return new LicensePlateData | ||
{ | ||
FileName = source.fileName, | ||
LicensePlateText = source.licensePlateText, | ||
TimeStamp = source.Timestamp | ||
}; | ||
} | ||
} | ||
} |
Oops, something went wrong.