Skip to content

Commit 7f04fb1

Browse files
authored
Merge pull request #11 from g4-api/development
Development
2 parents 7988a18 + 343b42b commit 7f04fb1

12 files changed

Lines changed: 1899 additions & 1149 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using G4.Credentials.Models;
2+
3+
using System.Collections.Generic;
4+
5+
namespace G4.Api.Abstractions
6+
{
7+
/// <summary>
8+
/// Defines a client interface for managing OAuth credentials, including retrieval,
9+
/// creation, and persistence of credential models.
10+
/// </summary>
11+
public interface ICredentialsClient
12+
{
13+
/// <summary>
14+
/// Retrieves a single <see cref="OAuthCredentialModel"/> by its identifier or name.
15+
/// </summary>
16+
/// <param name="idOrName">The unique identifier or logical name of the credential.</param>
17+
/// <returns>The matching <see cref="OAuthCredentialModel"/>.</returns>
18+
OAuthCredentialModel GetCredentials(string idOrName);
19+
20+
/// <summary>
21+
/// Retrieves all stored <see cref="OAuthCredentialModel"/> entries.
22+
/// </summary>
23+
/// <returns>A dictionary where the keys are credential identifiers and names, and the values are the corresponding <see cref="OAuthCredentialModel"/> instances.</returns>
24+
IDictionary<string, OAuthCredentialModel> GetCredentials();
25+
26+
/// <summary>
27+
/// Creates a new <see cref="OAuthCredentialsResponseModel"/> from the provided
28+
/// <see cref="OAuthCredentialModel"/>.
29+
/// </summary>
30+
/// <param name="oauth">The source OAuth credential model containing the client configuration.</param>
31+
/// <returns>An <see cref="OAuthCredentialsResponseModel"/> representing the initializedOAuth credential response.</returns>
32+
OAuthCredentialsResponseModel NewCredentials(OAuthCredentialModel oauth);
33+
34+
/// <summary>
35+
/// Removes OAuth credential records that match the specified Id or Name.
36+
/// </summary>
37+
/// <param name="idOrName">The credential identifier or friendly name to delete.</param>
38+
/// <returns>The number of rows removed from the <c>OAuthCredentials</c> table.</returns>
39+
int RemoveCredentials(string idOrName);
40+
41+
/// <summary>
42+
/// Persists the provided <see cref="OAuthCredentialModel"/> to the underlying
43+
/// credential store.
44+
/// </summary>
45+
/// <param name="obj">The OAuth credential model to save.</param>
46+
/// <returns>The saved <see cref="OAuthCredentialModel"/>.</returns>
47+
OAuthCredentialModel SaveCredentials(OAuthCredentialModel obj);
48+
}
49+
}

src/G4.Api/Abstractions/IIntegrationClient.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using G4.Attributes.Abstraction;
22
using G4.Cache;
3+
using G4.Credentials.Models;
34
using G4.Models;
45

56
using System.Collections.Concurrent;
@@ -27,6 +28,19 @@ public interface IIntegrationClient
2728
/// <returns>A dictionary where the keys are plugin types and the values are dictionaries containing plugin cache models.</returns>
2829
IDictionary<string, ConcurrentDictionary<string, G4PluginCacheModel>> GetCache();
2930

31+
/// <summary>
32+
/// Retrieves a single <see cref="OAuthCredentialModel"/> by its identifier or name.
33+
/// </summary>
34+
/// <param name="idOrName">The unique identifier or logical name of the credential.</param>
35+
/// <returns>The matching <see cref="OAuthCredentialModel"/>.</returns>
36+
OAuthCredentialModel GetCredentials(string idOrName);
37+
38+
/// <summary>
39+
/// Retrieves all stored <see cref="OAuthCredentialModel"/> entries.
40+
/// </summary>
41+
/// <returns>A dictionary where the keys are credential identifiers and names, and the values are the corresponding <see cref="OAuthCredentialModel"/> instances.</returns>
42+
IDictionary<string, OAuthCredentialModel> GetCredentials();
43+
3044
/// <summary>
3145
/// Retrieves the document associated with the specified key. If the document is not found in the cache,
3246
/// it synchronizes the cache with the provided external repository and attempts to retrieve the document again.
@@ -132,6 +146,13 @@ public interface IIntegrationClient
132146
/// <param name="repositories">An array of external repository models that may be used to generate new cache data if necessary.</param>
133147
void SyncCache(CacheManager cache, params G4ExternalRepositoryModel[] repositories);
134148

149+
/// <summary>
150+
/// Synchronizes Model Context Protocol (MCP) plugins from the specified servers into the cache.
151+
/// </summary>
152+
/// <param name="cache">The cache manager that will receive the synchronized plugin entries.</param>
153+
/// <param name="servers">The MCP servers whose plugins should be retrieved and merged into the cache.</param>
154+
void SyncCache(CacheManager cache, IDictionary<string, McpServerModel> servers);
155+
135156
/// <summary>
136157
/// Synchronizes the provided <paramref name="cache"/> instance by either updating it with default data or using it as the source of truth to sync other caches.
137158
/// Depending on the context, the <paramref name="cache"/> can either hold the default cache data to sync from or be the instance that needs to be updated.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using G4.Api.Abstractions;
2+
using G4.Cache;
3+
using G4.Credentials;
4+
using G4.Credentials.Models;
5+
6+
using Microsoft.Data.Sqlite;
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Linq;
11+
12+
namespace G4.Api.Clients
13+
{
14+
/// <summary>
15+
/// Provides credential persistence and caching capabilities for OAuth and related credential types.
16+
/// </summary>
17+
internal class CredentialsClient : ClientBase, ICredentialsClient
18+
{
19+
#region *** Fields ***
20+
// Manages caching mechanisms for plugins.
21+
private readonly CacheManager _cache;
22+
23+
// Manages credentials for external integrations.
24+
private readonly CredentialsManager _credentials;
25+
#endregion
26+
27+
#region *** Constructors ***
28+
/// <summary>
29+
/// Initializes a new instance of the <see cref="CredentialsClient"/> class
30+
/// using the shared SQLite connection from <see cref="CacheManager"/>.
31+
/// </summary>
32+
public CredentialsClient()
33+
: this(CacheManager.SqliteConnection)
34+
{
35+
}
36+
37+
/// <summary>
38+
/// Initializes a new instance of the <see cref="CredentialsClient"/> class
39+
/// with an explicit <see cref="SqliteConnection"/>.
40+
/// </summary>
41+
/// <param name="sqliteConnection">The SQLite connection used by the underlying <see cref="CredentialsManager"/>.</param>
42+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="sqliteConnection"/> is null.</exception>
43+
public CredentialsClient(SqliteConnection sqliteConnection)
44+
{
45+
// Validate that the provided SQLite connection is not null.
46+
ArgumentNullException.ThrowIfNull(sqliteConnection);
47+
48+
// Obtain shared in-memory cache instance
49+
_cache = CacheManager.Instance;
50+
51+
// Initialize credentials manager with provided SQLite connection
52+
_credentials = new CredentialsManager(sqliteConnection);
53+
}
54+
#endregion
55+
56+
#region *** Methods ***
57+
/// <inheritdoc />
58+
public OAuthCredentialModel GetCredentials(string idOrName)
59+
{
60+
// Get credentials from the underlying credentials database manager.
61+
// This will not use the cache, as credentials may be updated or created outside of this client.
62+
return _credentials.GetCredentials(idOrName);
63+
}
64+
65+
/// <inheritdoc />
66+
public IDictionary<string, OAuthCredentialModel> GetCredentials()
67+
{
68+
// Get all credentials from the underlying credentials database manager.
69+
// This will not use the cache, as credentials may be updated or created outside of this client.
70+
return _credentials
71+
.GetCredentials()
72+
.ToDictionary(i => ($"{i.Name};{i.Id}".ToLower()), i => i);
73+
}
74+
75+
/// <inheritdoc />
76+
public OAuthCredentialsResponseModel NewCredentials(OAuthCredentialModel oauth)
77+
{
78+
// Create new credentials using the underlying credentials manager.
79+
return _credentials.NewCredentials(oauth);
80+
}
81+
82+
/// <inheritdoc />
83+
public int RemoveCredentials(string idOrName)
84+
{
85+
// Resolve the credential so we can compute the cache key
86+
var credentials = _credentials.GetCredentials(idOrName);
87+
88+
// No credential found matching the provided identifier or name, so return 0 records removed.
89+
if (credentials == null) {
90+
return 0;
91+
}
92+
93+
// Remove from persistent store
94+
var removedCount = _credentials.RemoveCredentials(idOrName: credentials.Id);
95+
96+
// Build normalized cache key (Name + Id, case-insensitive)
97+
// Using invariant lowercase to ensure consistent dictionary access
98+
var key = $"{credentials.Name};{credentials.Id}".ToLowerInvariant();
99+
100+
// Remove from in-memory cache if it exists
101+
_cache.CredentialsCache.Remove(key);
102+
103+
// Return the number of records removed from the persistent store
104+
return removedCount;
105+
}
106+
107+
/// <inheritdoc />
108+
public OAuthCredentialModel SaveCredentials(OAuthCredentialModel oauth)
109+
{
110+
// Persist credentials using the underlying storage provider
111+
var credentials = _credentials.SaveCredentials(oauth)
112+
?? throw new InvalidOperationException("Failed to persist OAuth credentials.");
113+
114+
// Build normalized cache key (Name + Id, case-insensitive)
115+
// Using invariant lowercase to ensure consistent dictionary access
116+
var key = $"{credentials.Name};{credentials.Id}".ToLowerInvariant();
117+
118+
// Store in in-memory cache for fast lookup
119+
_cache.CredentialsCache[key] = credentials;
120+
121+
// Return the persisted credentials to the caller
122+
return credentials;
123+
}
124+
#endregion
125+
}
126+
}

src/G4.Api/Clients/IntegrationClient.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using G4.Attributes;
33
using G4.Attributes.Abstraction;
44
using G4.Cache;
5+
using G4.Credentials;
6+
using G4.Credentials.Models;
57
using G4.Extensions;
68
using G4.Models;
79

@@ -24,6 +26,9 @@ internal class IntegrationClient: ClientBase, IIntegrationClient
2426
// Manages caching mechanisms for plugins.
2527
private readonly CacheManager _cache;
2628

29+
// Manages credentials for external integrations.
30+
private readonly CredentialsManager _credentials;
31+
2732
// Represents the internal cache for non-cached plugins.
2833
// This cache is used to store plugins that are not present in the main cache.
2934
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, G4PluginCacheModel>> _internalCache = new(StringComparer.OrdinalIgnoreCase);
@@ -70,6 +75,9 @@ public IntegrationClient(CacheManager cache)
7075
// Assign the provided cache to the internal cache field.
7176
_cache = cache;
7277

78+
// Initialize the credentials manager using a connection from the CacheManager.
79+
_credentials = new CredentialsManager(CacheManager.SqliteConnection);
80+
7381
// Merge driver data or other plugin data from the internal cache into the user-provided cache.
7482
foreach (var item in InternalCache)
7583
{
@@ -95,6 +103,23 @@ public IDictionary<string, ConcurrentDictionary<string, G4PluginCacheModel>> Get
95103
return _cache.PluginsCache;
96104
}
97105

106+
/// <inheritdoc />
107+
public OAuthCredentialModel GetCredentials(string idOrName)
108+
{
109+
const StringComparison comparison = StringComparison.OrdinalIgnoreCase;
110+
111+
return _cache
112+
.CredentialsCache
113+
.Values
114+
.FirstOrDefault(i => i.Id.Equals(idOrName, comparison) || i.Name.Equals(idOrName, comparison));
115+
}
116+
117+
/// <inheritdoc />
118+
public IDictionary<string, OAuthCredentialModel> GetCredentials()
119+
{
120+
return _cache.CredentialsCache;
121+
}
122+
98123
/// <inheritdoc />
99124
public string GetDocument(string key, G4ExternalRepositoryModel repository)
100125
{
@@ -316,6 +341,24 @@ public void SyncCache(CacheManager cache, params G4ExternalRepositoryModel[] rep
316341
}
317342
}
318343

344+
/// <inheritdoc />
345+
public void SyncCache(CacheManager cache, IDictionary<string, McpServerModel> servers)
346+
{
347+
// Ensure the server collection is always initialized before attempting synchronization.
348+
servers ??= new Dictionary<string, McpServerModel>();
349+
350+
// Retrieve all MCP plugins from the configured servers, grouped by plugin type.
351+
foreach (var item in servers.GetModelContextPlugins())
352+
{
353+
// Iterate through each plugin in the current plugin type group.
354+
foreach (var plugin in item.Value)
355+
{
356+
// Add or overwrite the plugin entry in the cache using the plugin type and plugin key.
357+
cache.PluginsCache[item.Key][plugin.Key] = plugin.Value;
358+
}
359+
}
360+
}
361+
319362
// Scans all cached types for those decorated with G4DriverPluginAttribute and aggregates them
320363
// into a key-value pair representing driver data.
321364
private static ConcurrentDictionary<string, G4PluginCacheModel> FindDrivers()

0 commit comments

Comments
 (0)