public ClusterFactory(IList <TNode> allNodes, CostAnalyzer <double> costanalyzer) { this.allNodes = allNodes; this.costanalyzer = costanalyzer; // We work with the indexes. Nodes will be flagged as part of cluster by removing them from this list. NodesAsIndexes = Enumerable.Range(0, allNodes.Count).ToList(); ListOfClusters = new List <Cluster <TNode, double> >(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddJsonOptions(options => { // required to serialize options.JsonSerializerOptions.Converters .Add(new ObjectDictionaryConverter()); options.JsonSerializerOptions.IgnoreNullValues = true; }); // graphql services.AddSingleton <IChat, Data.Chat>(); services.AddSingleton <IChatResolverService, ChatResolverService>(); services.AddSingleton <ChatSchemas>(); services.AddSingleton(provider => provider.GetRequiredService <ChatSchemas>().Chat); // configure execution options var tanka = services.AddTankaGraphQL() .ConfigureRules(rules => rules.Concat(new[] { CostAnalyzer.MaxCost(100, 1, true) }).ToArray()) .ConfigureSchema <ISchema>(schema => new ValueTask <ISchema>(schema)) .ConfigureWebSockets(); //if (Env.IsDevelopment()) tanka.AddExtension<TraceExtension>(); // signalr server services.AddSignalR(options => options.EnableDetailedErrors = true) .AddTankaGraphQL(); // graphql-ws websocket server // web socket server services.AddWebSockets(options => { options.AllowedOrigins.Add("http://localhost:5000"); options.AllowedOrigins.Add("http://localhost:3000"); }); // CORS is required for the graphql.samples.chat.ui React App services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins("http://localhost:3000"); policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowCredentials(); //policy.WithHeaders("X-Requested-With", "authorization"); }); }); }
private static List <int> CalculateCluster(IList <int> nodesAsIndexes, CostAnalyzer <double> costanalyzer, CostAnalyzer <double> transposedCostanalyzer, double maxCostOfNearestNeighbor, int maxNodesPerCluster) { var constructingCluster = new List <int>(); int startIndex = nodesAsIndexes[randomizer.Next(0, nodesAsIndexes.Count - 1)]; ExpandClusterFromStartPoint(startIndex, constructingCluster, true, nodesAsIndexes, costanalyzer, maxCostOfNearestNeighbor, maxNodesPerCluster); ExpandClusterFromStartPoint(startIndex, constructingCluster, false, nodesAsIndexes, transposedCostanalyzer, maxCostOfNearestNeighbor, maxNodesPerCluster); return(constructingCluster); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // graphql services.AddSingleton <IChat, Chat>(); services.AddSingleton <IChatResolverService, ChatResolverService>(); services.AddSingleton <ChatSchemas>(); services.AddSingleton(provider => provider.GetRequiredService <ChatSchemas>().Chat); // configure execution options services.AddTankaSchemaOptions() .Configure <ISchema>((options, schema) => { options.ValidationRules = ExecutionRules.All .Concat(new[] { CostAnalyzer.MaxCost(maxCost: 100, defaultFieldComplexity: 1, addExtensionData: true) }).ToArray(); options.GetSchema = query => new ValueTask <ISchema>(schema); }); // signalr server services.AddSignalR(options => options.EnableDetailedErrors = true) // add GraphQL query streaming hub .AddTankaServerHubWithTracing(); // graphql-ws websocket server // web socket server services.AddWebSockets(options => { options.AllowedOrigins.Add("https://localhost:5000"); options.AllowedOrigins.Add("https://localhost:3000"); }); services.AddTankaWebSocketServerWithTracing(); // CORS is required for the graphql.samples.chat.ui React App services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins("http://localhost:3000"); policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowCredentials(); policy.WithHeaders("X-Requested-With", "authorization"); }); }); }
public IList <Cluster <TNode, double> > Solve([NotNull] IList <TNode> Nodes, [NotNull] CostAnalyzer <double> costanalyzer, double maxCostOfNearestNeighbor, int maxNodesPerCluster = -1) { var clusterFactory = new ClusterFactory(Nodes, costanalyzer); var transposedCostMatrix = CostMatrix <double> .CreateTransposed(costanalyzer.CostMatrix); var transposedCostanalyzer = new CostAnalyzer <double>(transposedCostMatrix); // While unhandled indexes available and the loop runs for at most a certain amount of times, continue the algorithm for (var counter = 0; counter < Nodes.Count * maxFactorOfLoopCount && clusterFactory.NodesAsIndexes.Count > 0; counter++) { // set the current pointer to a random node index among the nodes not yet classified in a cluster int currentPointer = clusterFactory.NodesAsIndexes[randomizer.Next(0, clusterFactory.NodesAsIndexes.Count - 1)]; var candidateCluster = CalculateCluster(clusterFactory.NodesAsIndexes, costanalyzer, transposedCostanalyzer, maxCostOfNearestNeighbor, maxNodesPerCluster); if (candidateCluster.Count > 1) { // loop over the cluster again maybe to cut the cluster in pieces. Clusters with one item are thrown back in the pool for (int indexInCluster = 0; indexInCluster < candidateCluster.Count - 1; indexInCluster++) { var maxCostInCluster = indexInCluster == 0 ? costanalyzer.AllNeighborsOrdered(candidateCluster[0])[candidateCluster[1]] : candidateCluster.Take(indexInCluster + 1).Tuples().Max(t => costanalyzer.AllNeighborsOrdered(t.Item1)[t.Item2]); var nodesOutsideClusterQuiteCloseToCluster = costanalyzer.AllNeighborsOrdered(candidateCluster[indexInCluster]) .Where(neighbor => !candidateCluster.Contains(neighbor.Key) && neighbor.Value <= maxCostInCluster); if (nodesOutsideClusterQuiteCloseToCluster.Count() > 1) // Split the cluster { var firstPart = candidateCluster.GetRange(0, indexInCluster + 1); clusterFactory.CreateCluster(firstPart); candidateCluster.RemoveRange(0, indexInCluster + 1); indexInCluster = -1; } } } clusterFactory.CreateCluster(candidateCluster); } // Here we add the singleton clusters from the remaining nodes clusterFactory.CreateSingletonClusters(); return(clusterFactory.ListOfClusters); }
public void Cost_below_max_cost_with_with_costDirective_and_multiplier() { /* Given */ var document = Parser.ParseDocument( @"{ withMultiplier }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 3, defaultFieldComplexity: 0) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_below_max_cost_with_defaultComplexity() { /* Given */ var document = Parser.ParseDocument( @"{ default }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 1, defaultFieldComplexity: 1) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_below_max_cost_with_with_costDirective_and_multiplier() { /* Given */ var document = @"{ withMultiplier }"; /* When */ var result = Validate( document, CostAnalyzer.MaxCost( 3, 0) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_below_max_cost_with_defaultComplexity() { /* Given */ var document = @"{ default }"; /* When */ var result = Validate( document, CostAnalyzer.MaxCost( 1, 1) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_above_max_cost_with_costDirective_and_multiplier() { /* Given */ var document = Parser.ParseDocument( @"{ withMultiplier(count: 5) }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 5, defaultFieldComplexity: 0) ); /* Then */ Assert.False(result.IsValid); Assert.Single( result.Errors, error => error.Code == "MAX_COST"); }
public void Cost_above_max_cost_with_defaultComplexity() { /* Given */ var document = Parser.ParseDocument( @"{ default }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 0, defaultFieldComplexity: 1) ); /* Then */ Assert.False(result.IsValid); Assert.Single( result.Errors, error => error.Code == "MAX_COST"); }
public void Cost_above_max_cost_with_costDirective() { /* Given */ var document = @"{ withCost }"; /* When */ var result = Validate( document, CostAnalyzer.MaxCost( 0, 0) ); /* Then */ Assert.False(result.IsValid); Assert.Single( result.Errors, error => error.Code == "MAX_COST"); }
public void ConfigureServices(IServiceCollection services) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); AddForwardedHeaders(services); services.AddMemoryCache(); // signalr authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub", RoleClaimType = "role" }; options.Authority = Configuration["JWT:Authority"]; options.Audience = Configuration["JWT:Audience"]; options.SaveToken = true; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // Read the token out of the query string context.Token = accessToken; return(Task.CompletedTask); }, OnTokenValidated = async context => { if (context.SecurityToken is JwtSecurityToken jwt) { /* * PERFORMANCE/SECURITY * * For performance reasons we're caching the results of the userInfo request. * * This is NOT production quality and you should carefully think when using * it in your application */ var cache = context.HttpContext.RequestServices.GetRequiredService <IMemoryCache>(); var key = jwt.Claims.Single(c => c.Type == "sub").Value; var userInfoClaimsIdentity = await cache .GetOrCreateAsync(key, async entry => { entry.SetAbsoluteExpiration(jwt.ValidTo - TimeSpan.FromSeconds(5)); var configuration = await context.Options.ConfigurationManager .GetConfigurationAsync(CancellationToken.None); var userInfoEndpoint = configuration.UserInfoEndpoint; var client = new HttpClient(); var userInfo = await client.GetUserInfoAsync(new UserInfoRequest { Address = userInfoEndpoint, Token = jwt.RawData }); if (userInfo.IsError) { return(new ClaimsIdentity()); } return(new ClaimsIdentity(userInfo.Claims)); }); context.Principal.AddIdentity(userInfoClaimsIdentity); } } }; }); services.AddAuthorization(options => options.AddPolicy("authorize", policy => policy.RequireAuthenticatedUser())); services.AddHttpContextAccessor(); // add domain services.AddSingleton <Messages>(); // add schema services.AddSchemaControllers() .AddQueryController <QueryController>() .AddMutationController <MutationController>() .AddSubscriptionController <SubscriptionController>() .AddMessageController <MessageController>(); services.AddMemoryCache(); services.AddSingleton <SchemaCache>(); services.AddTankaGraphQL() .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd()) .ConfigureRules(rules => rules.Concat(new[] { CostAnalyzer.MaxCost( 100 ) }).ToArray()); // add signalr services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddTankaGraphQL(); }
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddJsonOptions(options => { // required to serialize options.JsonSerializerOptions.Converters .Add(new ObjectDictionaryConverter()); }); AddForwardedHeaders(services); // signalr authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub", RoleClaimType = "role" }; options.Authority = Configuration["JWT:Authority"]; options.Audience = Configuration["JWT:Audience"]; options.SaveToken = true; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"] .ToString(); if (string.IsNullOrEmpty(accessToken)) { accessToken = context.Request .Headers["Authorization"] .ToString() .Replace("Bearer ", string.Empty); } // Read the token out of the query string context.Token = accessToken; return(Task.CompletedTask); } }; }); services.AddAuthorization(options => options.AddPolicy("authorize", policy => policy.RequireAuthenticatedUser())); services.AddHttpContextAccessor(); // add domain services.AddSingleton <Channels>(); // add schema services.AddSchemaControllers() .AddQueryController <QueryController>() .AddChannelController <ChannelController>(); services.AddMemoryCache(); services.AddSingleton <SchemaCache>(); services.AddTankaGraphQL() .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd()) .ConfigureRules(rules => rules.Concat(new[] { CostAnalyzer.MaxCost( 100 ) }).ToArray()); // add signalr services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddTankaGraphQL(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); AddForwardedHeaders(services); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); services.AddCors(cors => { cors.AddDefaultPolicy(policy => { policy.SetIsOriginAllowed(origin => true); policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowCredentials(); }); }); // signalr authentication services.AddAuthentication() .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub", RoleClaimType = "role" }; options.Authority = Configuration["JWT:Authority"]; options.Audience = Configuration["JWT:Audience"]; options.SaveToken = false; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"].ToString(); if (string.IsNullOrEmpty(accessToken)) { var messageContext = context.HttpContext .RequestServices.GetRequiredService <IMessageContextAccessor>(); var message = messageContext.Context?.Message; if (message?.Type == MessageType.GQL_CONNECTION_INIT) { if (messageContext.Context?.Message.Payload is Dictionary <string, object> payload && payload.ContainsKey("authorization")) { accessToken = payload["authorization"].ToString() !; } } } // Read the token out of the query string context.Token = accessToken; return(Task.CompletedTask); }, OnTokenValidated = context => { if (context.SecurityToken is JwtSecurityToken jwt) { var tokenIdentity = new ClaimsIdentity( new[] { new Claim("access_token", jwt.RawData) }); context.Principal?.AddIdentity(tokenIdentity); } return(Task.CompletedTask); } }; }); services.AddAuthorization(options => options.AddPolicy("authorize", policy => policy .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser())); services.AddHttpContextAccessor(); // add schema cache services.AddSingleton <SchemaCache>(); services.AddMemoryCache(); // add execution options services.AddTankaGraphQL() .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd()) .ConfigureRules <ILogger <Startup> >((rules, logger) => rules.Concat(new[] { CostAnalyzer.MaxCost( 100, 1, onCalculated: operation => { logger.LogInformation( $"Operation '{operation.Operation.ToGraphQL()}' costs " + $"'{operation.Cost}' (max: '{operation.MaxCost}')"); } ) }).ToArray()) .ConfigureWebSockets <IHttpContextAccessor>( async(context, accessor) => { var succeeded = await AuthorizeHelper.AuthorizeAsync( accessor.HttpContext, new List <IAuthorizeData> { new AuthorizeAttribute("authorize") }); if (succeeded) { await context.Output.WriteAsync(new OperationMessage { Type = MessageType.GQL_CONNECTION_ACK }); } else { // you must decide what kind of message to send back to the client // in case the connection is not accepted. await context.Output.WriteAsync(new OperationMessage { Type = MessageType.GQL_CONNECTION_ERROR, Id = context.Message.Id }); // complete the output forcing the server to disconnect context.Output.Complete(); } }); // add signalr services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddTankaGraphQL(); }
private static void ExpandClusterFromStartPoint(int startIndex, List <int> constructingCluster, bool atTail, IList <int> nodesAsIndexes, CostAnalyzer <double> costanalyzer, double maxCostOfNearestNeighbor, int maxNodesPerCluster) { int currentPointer = startIndex; bool foundNextNodeInChain; if (maxNodesPerCluster < 0 || constructingCluster.Count < maxNodesPerCluster - 1) { do { foundNextNodeInChain = false; var myNeighborsNotAlreadyInConstructingCluster = costanalyzer.AllNeighborsOrdered(currentPointer).Where(neighbor => currentPointer != neighbor.Key && !constructingCluster.Contains(neighbor.Key)); if (myNeighborsNotAlreadyInConstructingCluster.Any()) { var nearestNeighbor = myNeighborsNotAlreadyInConstructingCluster.FirstOrDefault(); // The closest neighbor if (nearestNeighbor.Value <= maxCostOfNearestNeighbor && nodesAsIndexes.Contains(nearestNeighbor.Key)) // No overlapping with other clusters. We stop if the next one is in another cluster. { if (constructingCluster.Count == 0) { constructingCluster.Add(currentPointer); // If we start construct a cluster don't forget to add the beginning node. } if (atTail) { constructingCluster.Add(nearestNeighbor.Key); } else { constructingCluster.Insert(0, nearestNeighbor.Key); } currentPointer = nearestNeighbor.Key; // Move the pointer to the neighbor foundNextNodeInChain = true; } } }while (foundNextNodeInChain && (maxNodesPerCluster < 0 || constructingCluster.Count < maxNodesPerCluster)); } }