@@ -133,6 +133,7 @@ grant_fact_http_authority (WylHandle *handle, const gchar *subject)
133133 "wr.graph.manage" ,
134134 "wr.schema.manage" ,
135135 "wr.fact.write" ,
136+ "wr.datalog.query" ,
136137 };
137138 wyl_policy_store_t * store = wyl_handle_get_policy_store (handle );
138139 for (gsize i = 0 ; i < G_N_ELEMENTS (perms ); i ++ ) {
@@ -352,6 +353,107 @@ check_fact_http_contract (WylHandle *handle, const gchar *base_url)
352353 if (rc != 0 )
353354 return rc ;
354355
356+ g_clear_pointer (& body , g_free );
357+ g_autofree gchar * datalog_query = g_strdup_printf ("tenant=%s&%s" ,
358+ WYL_TENANT_DEFAULT , FACT_GUARD );
359+ rc = send_raw (session , "POST" , base_url ,
360+ "/datalog/__wr_default/orders/query" , datalog_query , NULL ,
361+ "{\"query\":\"orders(O,A)\",\"output\":\"json\",\"limit\":10}" ,
362+ & status , & body );
363+ if (rc != 0 )
364+ return rc ;
365+ if (status != 401 || strstr (body , "\"datalog_auth_required\"" ) == NULL )
366+ return 330 ;
367+
368+ g_clear_pointer (& body , g_free );
369+ rc = send_raw (session , "POST" , base_url ,
370+ "/datalog/__wr_default/orders/query" , datalog_query , deny_token ,
371+ "{\"query\":\"orders(O,A)\",\"output\":\"json\",\"limit\":10}" ,
372+ & status , & body );
373+ if (rc != 0 )
374+ return rc ;
375+ if (status != 403 || strstr (body , "\"datalog_denied\"" ) == NULL )
376+ return 331 ;
377+
378+ const gchar * invalid_datalog_bodies [] = {
379+ "{\"query\":\"orders(O,A) :- orders(O,A)\",\"output\":\"json\"}" ,
380+ "{\"query\":\".decl orders(O:symbol,A:int64)\",\"output\":\"json\"}" ,
381+ "{\"query\":\"orders(O,A);orders(O,A)\",\"output\":\"json\"}" ,
382+ "{\"query\":\"SELECT * FROM orders\",\"output\":\"json\"}" ,
383+ };
384+ for (gsize i = 0 ; i < G_N_ELEMENTS (invalid_datalog_bodies ); i ++ ) {
385+ g_clear_pointer (& body , g_free );
386+ rc = send_raw (session , "POST" , base_url ,
387+ "/datalog/__wr_default/orders/query" , datalog_query , admin_token ,
388+ invalid_datalog_bodies [i ], & status , & body );
389+ if (rc != 0 )
390+ return rc ;
391+ if (status != 400 || strstr (body , "\"invalid_datalog_request\"" ) == NULL )
392+ return 340 + (gint ) i ;
393+ }
394+
395+ g_clear_pointer (& body , g_free );
396+ rc = send_raw (session , "POST" , base_url ,
397+ "/datalog/__wr_default/orders/query" , datalog_query , admin_token ,
398+ "{\"query\":\"orders(O,A)\",\"output\":\"json\",\"limit\":10}" ,
399+ & status , & body );
400+ if (rc != 0 )
401+ return rc ;
402+ if (status != 200 || strstr (body , "\"relation\":\"orders\"" ) == NULL ||
403+ strstr (body , "\"columns\":[\"O\",\"A\"]" ) == NULL ||
404+ strstr (body , "{\"O\":\"o-1\",\"A\":42}" ) == NULL ||
405+ strstr (body , "facts.duckdb" ) != NULL )
406+ return 332 ;
407+
408+ g_clear_pointer (& body , g_free );
409+ rc = send_raw (session , "POST" , base_url ,
410+ "/datalog/__wr_default/orders/query" , datalog_query , admin_token ,
411+ "{\"query\":\"payments(P)\",\"output\":\"json\",\"limit\":10}" ,
412+ & status , & body );
413+ if (rc != 0 )
414+ return rc ;
415+ if (status != 403 || strstr (body , "\"datalog_relation_denied\"" ) == NULL )
416+ return 333 ;
417+
418+ if (wyl_handle_replay_fact_graphs (handle , NULL ) != WYRELOG_E_OK )
419+ return 334 ;
420+ g_clear_pointer (& body , g_free );
421+ rc = send_raw (session , "POST" , base_url ,
422+ "/datalog/__wr_default/orders/query" , datalog_query , admin_token ,
423+ "{\"query\":\"orders(\\\"o-1\\\",A)\",\"output\":\"json\",\"limit\":10}" ,
424+ & status , & body );
425+ if (rc != 0 )
426+ return rc ;
427+ if (status != 200 || strstr (body , "{\"A\":42}" ) == NULL ||
428+ strstr (body , "\"row_count\":1" ) == NULL )
429+ return 335 ;
430+
431+ g_clear_pointer (& body , g_free );
432+ g_autofree gchar * append_query_2 = g_strdup_printf
433+ ("tenant=%s&namespace=shop&schema_version=1&batch_id=batch-7&"
434+ "idempotency_key=key-7&%s" , WYL_TENANT_DEFAULT , FACT_GUARD );
435+ rc = send_raw (session , "POST" , base_url ,
436+ "/facts/__wr_default/orders/orders:append" , append_query_2 ,
437+ admin_token , "order_id\tamount\no-2\t84\n" , & status , & body );
438+ if (rc != 0 )
439+ return rc ;
440+ if (status != 200 || strstr (body , "\"inserted\":true" ) == NULL )
441+ return 336 ;
442+ rc = check_fact_projection_row_count (handle , "orders" , 2 );
443+ if (rc != 0 )
444+ return rc ;
445+
446+ g_clear_pointer (& body , g_free );
447+ rc = send_raw (session , "POST" , base_url ,
448+ "/datalog/__wr_default/orders/query" , datalog_query , admin_token ,
449+ "{\"query\":\"orders(O,A)\",\"output\":\"json\",\"limit\":1}" ,
450+ & status , & body );
451+ if (rc != 0 )
452+ return rc ;
453+ if (status != 200 || strstr (body , "\"row_count\":1" ) == NULL ||
454+ strstr (body , "\"truncated\":true" ) == NULL )
455+ return 337 ;
456+
355457 g_clear_pointer (& body , g_free );
356458 g_autofree gchar * bad_append_query = g_strdup_printf
357459 ("tenant=%s&namespace=shop&schema_version=1&batch_id=batch-2&"
@@ -363,7 +465,7 @@ check_fact_http_contract (WylHandle *handle, const gchar *base_url)
363465 return rc ;
364466 if (status != 400 || strstr (body , "\"invalid_fact_payload\"" ) == NULL )
365467 return 29 ;
366- rc = check_fact_projection_row_count (handle , "orders" , 1 );
468+ rc = check_fact_projection_row_count (handle , "orders" , 2 );
367469 if (rc != 0 )
368470 return rc ;
369471
0 commit comments