@@ -8,7 +8,6 @@ namespace ProductConstructionService.Common;
88
99public interface ICodeflowHistoryManager
1010{
11- void RefreshCodeflowHistory ( string repo , string branch ) ;
1211 Task < CodeflowHistoryResult > GetCodeflowHistory ( Guid ? subscriptionId ) ;
1312 Task < CodeflowHistory ? > FetchLatestCodeflowHistoryAsync ( Guid ? subscriptionId ) ;
1413}
@@ -32,7 +31,7 @@ public record CodeflowGraphCommit(
3231 DateTimeOffset CommitDate ,
3332 string Author ,
3433 string Description ,
35- List < CodeflowGraphCommit > IncomingCodeflows ) ;
34+ CodeflowGraphCommit ? IncomingCodeflow ) ;
3635
3736public class CodeflowHistoryManager : ICodeflowHistoryManager
3837{
@@ -47,60 +46,76 @@ public CodeflowHistoryManager(
4746 _remoteFactory = remoteFactory ;
4847 }
4948
50- public async void RefreshCodeflowHistory ( string repo , string branch )
49+ public async Task < CodeflowHistory ? > GetCachedCodeflowHistoryAsync ( Subscription subscription )
5150 {
52- //0. Fetch latest commit from local git repo if exists
53- //1. Fetch new parts from GitHub API
54- //2. Fetch old parts from disk
55- //3. Stitch them together
56- //4. Write contents to cache
57- await Task . CompletedTask ;
51+ string id = subscription . Id . ToString ( ) ! ;
52+ var cache = _redisCacheFactory . Create < CodeflowGraphCommit > ( id ) ;
53+ return await cache . TryGetStateAsync ( ) ;
5854 }
5955
60- public async Task < CodeflowHistoryResult > GetCodeflowHistory ( Subscription subscription , bool fetchLatest )
56+ public async Task < CodeflowHistory ? > GetCachedCodeflowHistoryAsync (
57+ Subscription subscription ,
58+ string commitSha ,
59+ int commitFetchCount )
6160 {
62- //todo: implement this method
63- }
64-
65- public async Task < CodeflowHistory ? > GetCachedCodeflowHistoryAsync ( Guid ? subscriptionId )
66- {
67- string id = subscriptionId . ToString ( ) ! ;
68-
69- var cache = _redisCacheFactory . Create < CodeflowHistory > ( id ) ;
70-
71- var cachedHistory = await cache . TryGetStateAsync ( ) ;
72- return cachedHistory ;
61+ // todo this method returns the codeflow history starting from commitSha.
62+ // It only reads from redis and never modifies the cache
7363 }
74-
75-
76- public async Task < CodeflowHistory ? > FetchLatestCodeflowHistoryAsync ( Subscription subscription )
64+ public async Task < CodeflowHistory ? > FetchLatestCodeflowHistoryAsync (
65+ Subscription subscription ,
66+ int commitFetchCount )
7767 {
68+ //todo acquire lock on the redis Zset here
7869 var cachedCommits = await GetCachedCodeflowHistoryAsync ( subscription . Id ) ;
7970
8071 var remote = await _remoteFactory . CreateRemoteAsync ( subscription . TargetRepository ) ;
8172
73+ latestCachedCommitSha = cachedCommits ? . Commits . FirstOrDefault ( ) ? . CommitSha ;
74+
8275 var latestCommits = await remote . FetchNewerRepoCommitsAsync (
8376 subscription . TargetBranch ,
8477 subscription . TargetBranch ,
85- cachedCommits ? . Commits . FirstOrDefault ( ) ? . CommitSha ,
86- 500 ) ;
78+ latestCachedCommitSha ,
79+ commitFetchCount ) ;
80+
81+ if ( latestCommits . Count == commitFetchCount &&
82+ latestCommits . LastOrDefault ( ) ? . CommitSha != latestCachedCommitSha )
83+ {
84+ // we have a gap in the history - throw away cache because we can't form a continuous history
85+ cachedCommits = [ ] ;
86+ }
87+ else
88+ {
89+ latestCommits = latestCommits
90+ . Where ( commit => commit . CommitSha != latestCachedCommitSha )
91+ . ToList ( ) ;
92+ }
8793
8894 var latestCachedCodeflow = cachedCommits ? . Commits . FirstOrDefault (
89- x => x . IncomingCodeflows . Count > 0 ) ;
95+ commit => commit . IncomingCodeflows != null ) ;
9096
9197 var codeFlows = await FetchLatestIncomingCodeflows (
9298 subscription . TargetRepository ,
9399 subscription . TargetBranch ,
94- latestCachedCodeflow ,
100+ latestCommits ,
95101 remote ) ;
96102
103+ foreach ( var commit in latestCommits )
104+ {
105+ string ? sourceCommitSha = codeflows . GetCodeflowSourceCommit ( commit . CommitSha ) ;
106+ commit . IncomingCodeflow = sourceCommitSha ;
107+ }
108+
109+ // todo cache fresh commits and release lock on the Zset
110+ await CacheCommits ( latestCommits ) ;
111+
97112 return null ;
98113 }
99114
100- private async Task < List < CodeflowGraphCommit > > FetchLatestIncomingCodeflows (
115+ private async Task < GraphCodeflows > FetchLatestIncomingCodeflows (
101116 string repo ,
102117 string branch ,
103- CodeflowGraphCommit ? latestCachedCommit ,
118+ List < CodeflowGraphCommit > latestCommits ,
104119 IRemote ? remote )
105120 {
106121 if ( remote == null )
@@ -113,4 +128,36 @@ private async Task<List<CodeflowGraphCommit>> FetchLatestIncomingCodeflows(
113128 //todo: implement this method
114129 return null ;
115130 }
131+
132+ private async Task CacheCommits ( List < CodeflowGraphCommit > commits )
133+ {
134+ // Cache the commits as part of the subscription's redis ZSet of CodeflowGraphCommit objects
135+ if ( commits . Count == 0 )
136+ {
137+ return ;
138+ }
139+ var cache = _redisCacheFactory . Create < CodeflowGraphCommit > ( subscription . Id . ToString ( ) ! ) ;
140+ await cache . SetStateAsync ( new CodeflowHistory ( commits , codeflows ) ) ;
141+
142+ }
143+ }
144+
145+ class GraphCodeflows
146+ {
147+ // keys: target repo commits that have incoing codeflows
148+ // values: commit SHAs of those codeflows in the source repo
149+ public Dictionary < string , string > Codeflows { get ; set ; } = [ ] ;
150+
151+ /// <summary>
152+ /// Returns the source commit of the codeflow if targetCommitSha is a target commit of a codeflow.
153+ /// Otherwise, return null
154+ /// </summary>
155+ public string ? GetCodeflowSourceCommit ( string targetCommitSha )
156+ {
157+ if ( Codeflows . TryGetValue ( targetCommitSha , out var sourceCommit ) )
158+ {
159+ return sourceCommit ;
160+ }
161+ return null ;
162+ }
116163}
0 commit comments