Skip to content

Commit 688bcb6

Browse files
committed
adjust front-end to simplified model (wip)
1 parent 9004b55 commit 688bcb6

23 files changed

+418
-518
lines changed

.github/workflows/dotnet-cd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
restore-keys: |
2424
${{ runner.os }}-nuget-
2525
26-
- name: Build with dotnet
26+
- name: dotnet build
2727
run: dotnet build rubberduckvba.Server --configuration Release
2828

2929
- name: dotnet publish
@@ -33,7 +33,7 @@ jobs:
3333
runs-on: self-hosted
3434
needs: build
3535
steps:
36-
- name: Deploy to IIS
36+
- name: deploy iis site
3737
run: |
3838
stop-webapppool -name "rubberduckvba"
3939
stop-iissite -name api -confirm: $false

rubberduckvba.Server/Api/Admin/AdminController.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,4 @@ public record class ConfigurationOptions(
5555
IOptions<GitHubSettings> GitHubOptions,
5656
IOptions<HangfireSettings> HangfireOptions)
5757
{
58-
5958
}

rubberduckvba.Server/Api/Features/FeatureViewModel.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public FeatureViewModel(Feature model)
2121
if (model is FeatureGraph graph)
2222
{
2323
Features = graph.Features.Select(e => new FeatureViewModel(e) { FeatureId = e.ParentId, FeatureName = graph.Name, FeatureTitle = graph.Title }).ToArray();
24-
Inspections = graph.Inspections.ToArray();
2524
}
2625
}
2726

@@ -42,10 +41,37 @@ public FeatureViewModel(Feature model)
4241
public bool HasImage { get; init; }
4342

4443
public FeatureViewModel[] Features { get; init; } = [];
45-
public Inspection[] Inspections { get; init; } = []; // InspectionViewModel[]
4644
}
4745

48-
public class FeatureXmlDocViewModel
46+
public class InspectionsFeatureViewModel : FeatureViewModel
4947
{
48+
public InspectionsFeatureViewModel(FeatureGraph model)
49+
: base(model)
50+
{
51+
Inspections = model.Inspections.ToArray();
52+
}
53+
54+
public Inspection[] Inspections { get; init; } = [];
55+
}
56+
57+
public class QuickFixesFeatureViewModel : FeatureViewModel
58+
{
59+
public QuickFixesFeatureViewModel(FeatureGraph model)
60+
: base(model)
61+
{
62+
QuickFixes = model.QuickFixes.ToArray();
63+
}
5064

51-
}
65+
public QuickFix[] QuickFixes { get; init; } = [];
66+
}
67+
68+
public class AnnotationsFeatureViewModel : FeatureViewModel
69+
{
70+
public AnnotationsFeatureViewModel(FeatureGraph model)
71+
: base(model)
72+
{
73+
Annotations = model.Annotations.ToArray();
74+
}
75+
76+
public Annotation[] Annotations { get; init; } = [];
77+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using rubberduckvba.Server.Model;
2+
using rubberduckvba.Server.Services;
3+
4+
namespace rubberduckvba.Server.Api.Features;
5+
6+
public class FeatureViewModelFactory(IMarkdownFormattingService md)
7+
{
8+
public FeatureViewModel[] Create(IEnumerable<Feature> models) => models.Select(Create).ToArray();
9+
10+
public FeatureViewModel Create(Feature model) => new(model);
11+
12+
public T Create<T>(FeatureGraph model) where T : FeatureViewModel =>
13+
// TODO format markdown before returning
14+
(T)(model is not FeatureGraph graph ? new FeatureViewModel(model) : model.Name switch
15+
{
16+
"Inspections" => new InspectionsFeatureViewModel(graph),
17+
"QuickFixes" => new QuickFixesFeatureViewModel(graph),
18+
"Annotations" => new AnnotationsFeatureViewModel(graph),
19+
_ => new FeatureViewModel(model)
20+
});
21+
}

rubberduckvba.Server/Api/Features/FeaturesController.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public record class MarkdownFormattingRequestViewModel
1717

1818
[ApiController]
1919
[AllowAnonymous]
20-
public class FeaturesController(IRubberduckDbService db, FeatureServices features, IMarkdownFormattingService md, ICacheService cache) : ControllerBase
20+
public class FeaturesController(IRubberduckDbService db, FeatureServices features, FeatureViewModelFactory vmFactory, IMarkdownFormattingService md, ICacheService cache) : ControllerBase
2121
{
2222
private static RepositoryOptionViewModel[] RepositoryOptions { get; } =
2323
Enum.GetValues<RepositoryId>().Select(e => new RepositoryOptionViewModel { Id = e, Name = e.ToString() }).ToArray();
@@ -26,13 +26,19 @@ private async Task<FeatureOptionViewModel[]> GetFeatureOptions(RepositoryId repo
2626
await db.GetTopLevelFeatures(repositoryId)
2727
.ContinueWith(t => t.Result.Select(e => new FeatureOptionViewModel { Id = e.Id, Name = e.Name, Title = e.Title }).ToArray());
2828

29+
private bool TryGetCachedContent(out string key, out object cached)
30+
{
31+
key = HttpContext.Request.Path;
32+
return cache.TryGet(key, out cached);
33+
}
34+
2935
[HttpGet("features")]
3036
[AllowAnonymous]
31-
public async Task<ActionResult<IEnumerable<FeatureViewModel>>> Index()
37+
public async Task<IActionResult> Index()
3238
{
33-
//if (cache.TryGet<FeatureViewModel[]>(HttpContext.Request.Path, out var cached))
39+
//if (TryGetCachedContent(out var key, out var cached))
3440
//{
35-
// return cached;
41+
// return Ok(cached);
3642
//}
3743

3844
var features = await db.GetTopLevelFeatures(RepositoryId.Rubberduck);
@@ -41,8 +47,8 @@ public async Task<ActionResult<IEnumerable<FeatureViewModel>>> Index()
4147
return NoContent();
4248
}
4349

44-
var model = features.Select(feature => new FeatureViewModel(feature)).ToArray();
45-
//cache.Write(HttpContext.Request.Path, model);
50+
var model = vmFactory.Create(features);
51+
//cache.Write(key, model);
4652

4753
return Ok(model);
4854
}
@@ -52,21 +58,22 @@ public async Task<ActionResult<IEnumerable<FeatureViewModel>>> Index()
5258

5359
[HttpGet("features/{name}")]
5460
[AllowAnonymous]
55-
public async Task<ActionResult<FeatureViewModel>> Info([FromRoute] string name)
61+
public IActionResult Info([FromRoute] string name)
5662
{
57-
//if (cache.TryGet<FeatureViewModel>(HttpContext.Request.Path, out var cached))
58-
//{
59-
// return cached;
60-
//}
63+
if (TryGetCachedContent(out var key, out var cached))
64+
{
65+
return Ok(cached);
66+
}
6167

6268
var feature = features.Get(name);
6369
if (feature is null)
6470
{
6571
return NotFound();
6672
}
6773

68-
var model = new FeatureViewModel(feature);
69-
//cache.Write(HttpContext.Request.Path, model);
74+
var model = vmFactory.Create(feature);
75+
cache.Write(key, model);
76+
7077
return Ok(model);
7178
}
7279

rubberduckvba.Server/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using NLog.Targets;
1010
using RubberduckServices;
1111
using rubberduckvba.Server.Api.Admin;
12+
using rubberduckvba.Server.Api.Features;
1213
using rubberduckvba.Server.ContentSynchronization;
1314
using rubberduckvba.Server.ContentSynchronization.Pipeline.Abstract;
1415
using rubberduckvba.Server.ContentSynchronization.Pipeline.Sections.Context;
@@ -169,6 +170,7 @@ private static void ConfigureServices(IServiceCollection services)
169170
services.AddSingleton<XmlDocQuickFixParser>();
170171
services.AddSingleton<XmlDocInspectionParser>();
171172

173+
services.AddSingleton<FeatureViewModelFactory>();
172174
services.AddSingleton<IDistributedCache, MemoryDistributedCache>();
173175
services.AddSingleton<ICacheService, CacheService>();
174176
services.AddSingleton<IContentCacheService, ContentCacheService>(); // TODO deprecate

rubberduckvba.Server/Services/DistributedCacheService.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Microsoft.Extensions.Caching.Distributed;
2-
using NLog.Targets;
2+
using System.Collections.Concurrent;
33
using System.Text;
44
using System.Text.Json;
55

@@ -11,29 +11,49 @@ public interface ICacheService
1111
void Write<T>(string key, T value);
1212

1313
void Invalidate(string key);
14+
void Invalidate();
1415
}
1516

1617
public class CacheService(IDistributedCache cache) : ICacheService
1718
{
1819
private static DistributedCacheEntryOptions CacheOptions { get; } = new DistributedCacheEntryOptions
1920
{
20-
SlidingExpiration = TimeSpan.FromHours(24),
21+
SlidingExpiration = TimeSpan.FromHours(1),
2122
};
2223

24+
private readonly ConcurrentDictionary<string, DateTime> _keys = [];
25+
26+
public void Invalidate()
27+
{
28+
foreach (var key in _keys.Keys)
29+
{
30+
Invalidate(key);
31+
}
32+
}
33+
2334
public void Invalidate(string key)
2435
{
36+
_keys.TryRemove(key, out _);
2537
cache.Remove(key);
2638
}
2739

2840
public bool TryGet<T>(string key, out T value)
2941
{
42+
if (!_keys.TryGetValue(key, out _))
43+
{
44+
value = default!;
45+
return false;
46+
}
47+
3048
var bytes = cache.Get(key);
3149
if (bytes == null)
3250
{
51+
Invalidate(key);
3352
value = default!;
3453
return false;
3554
}
3655

56+
_keys[key] = TimeProvider.System.GetUtcNow().DateTime;
3757
value = JsonSerializer.Deserialize<T>(bytes)!;
3858
return value != null;
3959
}
@@ -42,5 +62,6 @@ public void Write<T>(string key, T value)
4262
{
4363
var bytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(value));
4464
cache.Set(key, bytes, CacheOptions);
65+
_keys[key] = TimeProvider.System.GetUtcNow().DateTime;
4566
}
4667
}

rubberduckvba.client/angular.json

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@
3434
"browser": "src/main.ts"
3535
},
3636
"configurations": {
37+
"test": {
38+
"budgets": [
39+
{
40+
"type": "initial",
41+
"maximumWarning": "2.5mb",
42+
"maximumError": "4mb"
43+
},
44+
{
45+
"type": "anyComponentStyle",
46+
"maximumWarning": "10kb",
47+
"maximumError": "15kb"
48+
}
49+
],
50+
"fileReplacements": [
51+
{
52+
"replace": "src/environments/environment.ts",
53+
"with": "src/environments/environment.test.ts"
54+
}
55+
],
56+
"outputHashing": "all"
57+
},
3758
"production": {
3859
"budgets": [
3960
{
@@ -43,8 +64,8 @@
4364
},
4465
{
4566
"type": "anyComponentStyle",
46-
"maximumWarning": "5kb",
47-
"maximumError": "10kb"
67+
"maximumWarning": "10kb",
68+
"maximumError": "15kb"
4869
}
4970
],
5071
"fileReplacements": [

rubberduckvba.client/src/app/components/example-box/example-box.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ <h4>
3434
<div class="card mb-1 border-primary border-2">
3535
<div class="card-header bg-primary-subtle">
3636
<!--TODO icon for module.moduleType-->
37-
<h6 class="align-text-bottom" title="{{module.moduleName}} ({{module.moduleTypeName}})">
38-
<span class="icon {{module.moduleTypeIconClass}}"></span>
37+
<h6 class="align-text-bottom" title="{{module.moduleName}} ({{module.moduleType}})">
38+
<span class="icon {{module.moduleType}}"></span>
3939
<span>{{module.moduleName}}</span>
4040
</h6>
4141
</div>

rubberduckvba.client/src/app/components/example-box/example-box.component.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, Input, OnChanges, OnInit, SimpleChanges, TemplateRef, ViewChild, inject } from '@angular/core';
2-
import { XmlDocExample, InspectionExampleViewModel, ExampleModuleViewModel } from '../../model/feature.model';
2+
import { Example, InspectionExample, ExampleModule, AnnotationExample, QuickFixExample, XmlDocExample } from '../../model/feature.model';
33
import { BehaviorSubject } from 'rxjs';
44
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
55
import { fas } from '@fortawesome/free-solid-svg-icons';
@@ -13,7 +13,7 @@ import { ApiClientService } from '../../services/api-client.service';
1313
})
1414
export class ExampleBoxComponent implements OnInit {
1515

16-
private readonly _info: BehaviorSubject<InspectionExampleViewModel> = new BehaviorSubject<InspectionExampleViewModel>(null!);
16+
private readonly _info: BehaviorSubject<XmlDocExample> = new BehaviorSubject<XmlDocExample>(null!);
1717

1818
//@ViewChild('content', { read: TemplateRef }) content: TemplateRef<any> | undefined;
1919
//public modal = inject(NgbModal);
@@ -22,14 +22,14 @@ export class ExampleBoxComponent implements OnInit {
2222
public parentFeatureItemName: string = '';
2323

2424
@Input()
25-
public set inspectionExample(value: InspectionExampleViewModel | undefined) {
25+
public set inspectionExample(value: XmlDocExample | undefined) {
2626
if (value != null) {
2727
this._info.next(value);
2828
}
2929
}
3030

31-
public get inspectionExample(): InspectionExampleViewModel | undefined {
32-
return this._info.value;
31+
public get inspectionExample(): InspectionExample | undefined {
32+
return this._info.value as InspectionExample;
3333
}
3434

3535
constructor(private fa: FaIconLibrary, private api: ApiClientService) {

rubberduckvba.client/src/app/components/feature-box/feature-box.component.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
<div class="card-body">
44

55
<div class="row" *ngIf="!feature.hasImage">
6-
<div *ngIf="feature.parentId || !hasOwnDetailsPage" class="col-1"></div>
7-
<div [ngClass]="feature.parentId || !hasOwnDetailsPage ? 'col-10' : 'col-6'">
6+
<div *ngIf="feature.featureId || !hasOwnDetailsPage" class="col-1"></div>
7+
<div [ngClass]="feature.featureId || !hasOwnDetailsPage ? 'col-10' : 'col-6'">
88
<h4>{{feature.title}}</h4>
99
<p [innerHtml]="feature.shortDescription"></p>
1010
<a *ngIf="hasOwnDetailsPage" role="button" class="btn btn-outline-dark btn-ducky rounded-pill mt-4" href="features/{{feature.name.toLowerCase()}}">
1111
<span>Details ▸</span>
1212
</a>
13-
<div *ngIf="feature.parentId || !hasOwnDetailsPage" class="col-1"></div>
13+
<div *ngIf="feature.featureId || !hasOwnDetailsPage" class="col-1"></div>
1414
</div>
1515
</div>
1616

1717
<div class="row" *ngIf="feature.hasImage">
18-
<div [ngClass]="feature.parentId || !hasOwnDetailsPage ? 'col-6' : 'col-12'">
18+
<div [ngClass]="feature.featureId || !hasOwnDetailsPage ? 'col-6' : 'col-12'">
1919
<h4>{{feature.title}}</h4>
2020
<p [innerHtml]="feature.shortDescription"></p>
2121
</div>
22-
<div [ngClass]="feature.parentId || !hasOwnDetailsPage ? 'col-6' : 'col-12'">
22+
<div [ngClass]="feature.featureId || !hasOwnDetailsPage ? 'col-6' : 'col-12'">
2323
<img alt="{{feature.title}}" src='../../../assets/{{feature.name.toLowerCase()}}.gif' class="img-fluid" />
2424
</div>
2525
<div class="text-center">

rubberduckvba.client/src/app/components/feature-box/feature-box.component.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, Input, OnChanges, OnInit, SimpleChanges, TemplateRef, ViewChild, inject } from '@angular/core';
2-
import { Feature, FeatureItem, FeatureViewModel, QuickFixViewModel } from '../../model/feature.model';
2+
import { FeatureViewModel, QuickFixViewModel, SubFeatureViewModel } from '../../model/feature.model';
33
import { BehaviorSubject } from 'rxjs';
44
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
55
import { fas } from '@fortawesome/free-solid-svg-icons';
@@ -30,19 +30,23 @@ export class FeatureBoxComponent implements OnInit {
3030
}
3131

3232
public get feature(): FeatureViewModel | undefined {
33-
return this._info.value;
33+
return this._info.value as FeatureViewModel;
3434
}
3535

36-
private readonly _quickfixes: BehaviorSubject<FeatureItem[]> = new BehaviorSubject<FeatureItem[]>(null!);
36+
public get subFeature(): SubFeatureViewModel | undefined {
37+
return this._info.value as SubFeatureViewModel;
38+
}
39+
40+
private readonly _quickfixes: BehaviorSubject<QuickFixViewModel[]> = new BehaviorSubject<QuickFixViewModel[]>(null!);
3741

3842
@Input()
39-
public set quickFixes(value: FeatureItem[]) {
43+
public set quickFixes(value: QuickFixViewModel[]) {
4044
if (value != null) {
4145
this._quickfixes.next(value);
4246
}
4347
}
4448

45-
public get quickFixes(): FeatureItem[] {
49+
public get quickFixes(): QuickFixViewModel[] {
4650
return this._quickfixes.value;
4751
}
4852

0 commit comments

Comments
 (0)