@@ -124,129 +124,3 @@ public async Task DeleteRelationAsync(string workspaceName, Guid relationId, Can
124124
125125 private static string EscapeFilterValue ( string value ) => value . Replace ( "'" , "''" ) ;
126126}
127- using Azure ;
128- using Azure . Data . Tables ;
129- using CentralMemoryMcp . Functions . Models ;
130- using CentralMemoryMcp . Functions . Storage ;
131-
132- namespace CentralMemoryMcp . Functions . Services ;
133-
134- public interface IRelationService
135- {
136- Task < RelationModel > UpsertRelationAsync ( RelationModel model , CancellationToken ct = default ) ;
137- Task < RelationModel ? > GetRelationAsync ( string workspaceName , Guid relationId , CancellationToken ct = default ) ;
138- Task < List < RelationModel > > GetRelationsFromEntityAsync ( string workspaceName , Guid fromEntityId , CancellationToken ct = default ) ;
139- Task < List < RelationModel > > GetRelationsForWorkspaceAsync ( string workspaceName , CancellationToken ct = default ) ;
140- Task DeleteRelationAsync ( string workspaceName , Guid relationId , CancellationToken ct = default ) ;
141- }
142-
143- public class RelationService ( ITableStorageService storage ) : IRelationService
144- {
145- public async Task < RelationModel > UpsertRelationAsync ( RelationModel model , CancellationToken ct = default )
146- {
147- var table = await storage . GetRelationsTableAsync ( ct ) ;
148- // Check for existing relation (same workspace, from, to, type)
149- var filter = $ "PartitionKey eq '{ model . WorkspaceName } ' and FromEntityId eq '{ model . FromEntityId : N} ' and ToEntityId eq '{ model . ToEntityId : N} ' and RelationType eq '{ EscapeFilterValue ( model . RelationType ) } '";
150- await foreach ( var e in table . QueryAsync < TableEntity > ( filter : filter , maxPerPage : 1 , cancellationToken : ct ) )
151- {
152- // Reuse its Id
153- if ( e . TryGetValue ( "Id" , out var idObj ) && idObj is string idStr && Guid . TryParse ( idStr , out var existingId ) )
154- {
155- model . Id = existingId ;
156- }
157- break ;
158- }
159- var entity = new TableEntity ( model . PartitionKey , model . RowKey )
160- {
161- { "Id" , model . Id . ToString ( "N" ) } ,
162- { "WorkspaceName" , model . WorkspaceName } ,
163- { "FromEntityId" , model . FromEntityId . ToString ( "N" ) } ,
164- { "ToEntityId" , model . ToEntityId . ToString ( "N" ) } ,
165- { "RelationType" , model . RelationType } ,
166- { "Metadata" , model . Metadata ?? string . Empty }
167- } ;
168- await table . UpsertEntityAsync ( entity , TableUpdateMode . Replace , ct ) ;
169- return model ;
170- }
171-
172- public async Task < RelationModel ? > GetRelationAsync ( string workspaceName , Guid relationId , CancellationToken ct = default )
173- {
174- var table = await storage . GetRelationsTableAsync ( ct ) ;
175- try
176- {
177- var response = await table . GetEntityAsync < TableEntity > ( workspaceName , relationId . ToString ( "N" ) , cancellationToken : ct ) ;
178- var model = new RelationModel (
179- response . Value . GetString ( "WorkspaceName" ) ! ,
180- Guid . Parse ( response . Value . GetString ( "FromEntityId" ) ! ) ,
181- Guid . Parse ( response . Value . GetString ( "ToEntityId" ) ! ) ,
182- response . Value . GetString ( "RelationType" ) ! ,
183- response . Value . GetString ( "Metadata" ) )
184- {
185- Id = relationId
186- } ;
187- return model ;
188- }
189- catch ( RequestFailedException ex ) when ( ex . Status == 404 )
190- {
191- return null ;
192- }
193- }
194-
195- public async Task < List < RelationModel > > GetRelationsFromEntityAsync ( string workspaceName , Guid fromEntityId , CancellationToken ct = default )
196- {
197- var table = await storage . GetRelationsTableAsync ( ct ) ;
198- var results = new List < RelationModel > ( ) ;
199- var fromIdStr = fromEntityId . ToString ( "N" ) ;
200- await foreach ( var e in table . QueryAsync < TableEntity > (
201- filter : $ "PartitionKey eq '{ workspaceName } ' and FromEntityId eq '{ fromIdStr } '",
202- cancellationToken : ct ) )
203- {
204- var relationId = Guid . TryParse ( e . GetString ( "Id" ) , out var rid ) ? rid : Guid . NewGuid ( ) ;
205- var model = new RelationModel (
206- e . GetString ( "WorkspaceName" ) ! ,
207- Guid . Parse ( e . GetString ( "FromEntityId" ) ! ) ,
208- Guid . Parse ( e . GetString ( "ToEntityId" ) ! ) ,
209- e . GetString ( "RelationType" ) ! ,
210- e . GetString ( "Metadata" ) ) ;
211- model . Id = relationId ;
212- results . Add ( model ) ;
213- }
214- return results ;
215- }
216-
217- public async Task < List < RelationModel > > GetRelationsForWorkspaceAsync ( string workspaceName , CancellationToken ct = default )
218- {
219- var table = await storage . GetRelationsTableAsync ( ct ) ;
220- var results = new List < RelationModel > ( ) ;
221- await foreach ( var e in table . QueryAsync < TableEntity > (
222- filter : $ "PartitionKey eq '{ workspaceName } '",
223- cancellationToken : ct ) )
224- {
225- var relationId = Guid . TryParse ( e . GetString ( "Id" ) , out var rid ) ? rid : Guid . NewGuid ( ) ;
226- var model = new RelationModel (
227- e . GetString ( "WorkspaceName" ) ! ,
228- Guid . Parse ( e . GetString ( "FromEntityId" ) ! ) ,
229- Guid . Parse ( e . GetString ( "ToEntityId" ) ! ) ,
230- e . GetString ( "RelationType" ) ! ,
231- e . GetString ( "Metadata" ) ) ;
232- model . Id = relationId ;
233- results . Add ( model ) ;
234- }
235- return results ;
236- }
237-
238- public async Task DeleteRelationAsync ( string workspaceName , Guid relationId , CancellationToken ct = default )
239- {
240- var table = await storage . GetRelationsTableAsync ( ct ) ;
241- try
242- {
243- await table . DeleteEntityAsync ( workspaceName , relationId . ToString ( "N" ) , cancellationToken : ct ) ;
244- }
245- catch ( RequestFailedException ex ) when ( ex . Status == 404 )
246- {
247- // not found; ignore
248- }
249- }
250-
251- private static string EscapeFilterValue ( string value ) => value . Replace ( "'" , "''" ) ;
252- }
0 commit comments