Browse Source

Another attempt to download logs.

pull/344/head
Sebastian Stehle 7 years ago
parent
commit
6f00f440ff
  1. 6
      src/Squidex.Domain.Apps.Entities/Apps/DefaultAppLogStore.cs
  2. 2
      src/Squidex.Domain.Apps.Entities/Apps/IAppLogStore.cs
  3. 17
      src/Squidex/Areas/Api/Controllers/Statistics/Models/LogDownloadDto.cs
  4. 40
      src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs
  5. 4
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.html
  6. 4
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.scss
  7. 13
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts
  8. 8
      src/Squidex/app/shared/services/usages.service.spec.ts
  9. 7
      src/Squidex/app/shared/services/usages.service.ts
  10. 2
      src/Squidex/package.json

6
src/Squidex.Domain.Apps.Entities/Apps/DefaultAppLogStore.cs

@ -24,11 +24,11 @@ namespace Squidex.Domain.Apps.Entities.Apps
this.logStore = logStore;
}
public Task ReadLogAsync(IAppEntity app, DateTime from, DateTime to, Stream stream)
public Task ReadLogAsync(string appId, DateTime from, DateTime to, Stream stream)
{
Guard.NotNull(app, nameof(app));
Guard.NotNull(appId, nameof(appId));
return logStore.ReadLogAsync(app.Id.ToString(), from, to, stream);
return logStore.ReadLogAsync(appId, from, to, stream);
}
}
}

2
src/Squidex.Domain.Apps.Entities/Apps/IAppLogStore.cs

@ -13,6 +13,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
public interface IAppLogStore
{
Task ReadLogAsync(IAppEntity app, DateTime from, DateTime to, Stream stream);
Task ReadLogAsync(string appId, DateTime from, DateTime to, Stream stream);
}
}

17
src/Squidex/Areas/Api/Controllers/Statistics/Models/LogDownloadDto.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Areas.Api.Controllers.Statistics.Models
{
public sealed class LogDownloadDto
{
/// <summary>
/// The url to download the log.
/// </summary>
public string DownloadUrl { get; set; }
}
}

40
src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs

@ -9,8 +9,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Squidex.Areas.Api.Controllers.Statistics.Models;
using Squidex.Config;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Domain.Apps.Entities.Assets;
@ -31,13 +34,17 @@ namespace Squidex.Areas.Api.Controllers.Statistics
private readonly IAppLogStore appLogStore;
private readonly IAppPlansProvider appPlansProvider;
private readonly IAssetUsageTracker assetStatsRepository;
private readonly IDataProtector dataProtector;
private readonly MyUrlsOptions urlsOptions;
public UsagesController(
ICommandBus commandBus,
IUsageTracker usageTracker,
IAppLogStore appLogStore,
IAppPlansProvider appPlansProvider,
IAssetUsageTracker assetStatsRepository)
IAssetUsageTracker assetStatsRepository,
IDataProtectionProvider dataProtection,
IOptions<MyUrlsOptions> urlsOptions)
: base(commandBus)
{
this.usageTracker = usageTracker;
@ -45,6 +52,9 @@ namespace Squidex.Areas.Api.Controllers.Statistics
this.appLogStore = appLogStore;
this.appPlansProvider = appPlansProvider;
this.assetStatsRepository = assetStatsRepository;
this.urlsOptions = urlsOptions.Value;
dataProtector = dataProtection.CreateProtector("LogToken");
}
/// <summary>
@ -57,16 +67,40 @@ namespace Squidex.Areas.Api.Controllers.Statistics
/// </returns>
[HttpGet]
[Route("apps/{app}/usages/log/")]
[ProducesResponseType(typeof(CurrentCallsDto), 200)]
[ProducesResponseType(typeof(LogDownloadDto), 200)]
[ApiPermission(Permissions.AppCommon)]
[ApiCosts(0)]
public IActionResult GetLog(string app)
{
var token = dataProtector.Protect(App.Id.ToString());
var url = urlsOptions.BuildUrl($"/api/apps/log/{token}/");
var response = new LogDownloadDto { DownloadUrl = url };
return Ok(response);
}
/// <summary>
/// Get api calls as log file.
/// </summary>
/// <param name="token">The token for the log file.</param>
/// <returns>
/// 200 => Usage tracking results returned.
/// 404 => App not found.
/// </returns>
[HttpGet]
[Route("apps/log/{token}/")]
[ApiCosts(0)]
public IActionResult GetLogFile(string token)
{
var appId = dataProtector.Unprotect(token);
var today = DateTime.Today;
return new FileCallbackResult("text/csv", $"Usage-{today:yyy-MM-dd}.csv", false, stream =>
{
return appLogStore.ReadLogAsync(App, today.AddDays(-30), today, stream);
return appLogStore.ReadLogAsync(appId, today.AddDays(-30), today, stream);
});
}

4
src/Squidex/app/features/dashboard/pages/dashboard-page.component.html

@ -73,9 +73,7 @@
API Calls
<div class="float-right">
<img *ngIf="isLoadingLog" class="loader mr-2" src="/images/loader.gif" />
<a class="force" (click)="downloadLog()" [class.disabled]="isLoadingLog">
<a class="force" (click)="downloadLog()">
<small>Download Log</small>
</a>
</div>

4
src/Squidex/app/features/dashboard/pages/dashboard-page.component.scss

@ -31,10 +31,6 @@
color: $color-text-decent;
}
.loader {
height: 12px;
}
.card {
& {
margin-right: 1rem;

13
src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts

@ -6,7 +6,6 @@
*/
import { Component, OnDestroy, OnInit } from '@angular/core';
import { saveAs } from 'file-saver';
import { Subscription } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators';
@ -99,8 +98,6 @@ export class DashboardPageComponent implements OnDestroy, OnInit {
public callsCurrent = 0;
public callsMax = 0;
public isLoadingLog = false;
constructor(
public readonly appsState: AppsState,
public readonly authState: AuthService,
@ -211,15 +208,9 @@ export class DashboardPageComponent implements OnDestroy, OnInit {
}
public downloadLog() {
this.isLoadingLog = true;
this.usagesService.getLog(this.appsState.appName)
.subscribe(buffer => {
saveAs(buffer, 'Log.csv');
}, () => {
this.isLoadingLog = false;
}, () => {
this.isLoadingLog = false;
.subscribe(url => {
window.open(url, '_blank');
});
}
}

8
src/Squidex/app/shared/services/usages.service.spec.ts

@ -147,10 +147,10 @@ describe('UsagesService', () => {
it('should make get request to get log',
inject([UsagesService, HttpTestingController], (usagesService: UsagesService, httpMock: HttpTestingController) => {
let blob: Blob;
let url: string;
usagesService.getLog('my-app').subscribe(result => {
blob = result;
url = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/usages/log');
@ -158,8 +158,8 @@ describe('UsagesService', () => {
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush(new Blob([]));
req.flush({ downloadUrl: 'download/url' });
expect(blob!).toBeDefined();
expect(url!).toEqual('download/url');
}));
});

7
src/Squidex/app/shared/services/usages.service.ts

@ -59,10 +59,13 @@ export class UsagesService {
) {
}
public getLog(app: string): Observable<Blob> {
public getLog(app: string): Observable<string> {
const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/log`);
return this.http.get(url, { responseType: 'blob' }).pipe(
return this.http.get<any>(url).pipe(
map(response => {
return response.downloadUrl;
}),
pretifyError('Failed to load monthly api calls. Please reload.'));
}

2
src/Squidex/package.json

@ -28,7 +28,6 @@
"babel-polyfill": "6.26.0",
"bootstrap": "4.2.1",
"core-js": "2.6.3",
"file-saver": "2.0.0",
"graphiql": "0.12.0",
"graphql": "14.1.1",
"marked": "0.6.0",
@ -52,7 +51,6 @@
"@angular/compiler-cli": "7.2.2",
"@ngtools/webpack": "7.2.3",
"@types/core-js": "2.5.0",
"@types/file-saver": "2.0.0",
"@types/jasmine": "3.3.7",
"@types/marked": "0.6.0",
"@types/mousetrap": "1.6",

Loading…
Cancel
Save