| 326 | } |
| 327 | |
| 328 | static void emit_http_async_edge(cbm_pipeline_ctx_t *ctx, const CBMCall *call, |
| 329 | const cbm_gbuf_node_t *source, const cbm_gbuf_node_t *target, |
| 330 | const cbm_resolution_t *res, cbm_svc_kind_t svc, |
| 331 | bool suppress_plain_calls) { |
| 332 | const char *url_or_topic = call->first_string_arg; |
| 333 | bool is_url = (url_or_topic && url_or_topic[0] != '\0' && |
| 334 | (url_or_topic[0] == '/' || strstr(url_or_topic, "://") != NULL)); |
| 335 | bool is_topic = (url_or_topic && url_or_topic[0] != '\0' && svc == CBM_SVC_ASYNC && |
| 336 | strlen(url_or_topic) > PAIR_LEN); |
| 337 | if (!is_url && !is_topic) { |
| 338 | /* No URL/topic → this is not a real service call; the svc kind was a |
| 339 | * substring coincidence in the resolved QN (e.g. "SalesforceRestClient" |
| 340 | * matches the "RestClient" HTTP lib). Emit a plain CALLS edge — unless a |
| 341 | * weak TS/JS member-call match should be suppressed (#592/#606). */ |
| 342 | if (suppress_plain_calls) { |
| 343 | return; |
| 344 | } |
| 345 | char esc_callee[CBM_SZ_256]; |
| 346 | cbm_json_escape(esc_callee, sizeof(esc_callee), call->callee_name); |
| 347 | char props[CBM_SZ_512]; |
| 348 | snprintf(props, sizeof(props), |
| 349 | "{\"callee\":\"%s\",\"confidence\":%.2f,\"strategy\":\"%s\",\"candidates\":%d}", |
| 350 | esc_callee, res->confidence, res->strategy ? res->strategy : "unknown", |
| 351 | res->candidate_count); |
| 352 | calls_emit_edge(ctx->gbuf, source->id, target->id, "CALLS", props, sizeof(props), call); |
| 353 | return; |
| 354 | } |
| 355 | const char *edge_type = (svc == CBM_SVC_HTTP) ? "HTTP_CALLS" : "ASYNC_CALLS"; |
| 356 | const char *method = |
| 357 | (svc == CBM_SVC_HTTP) ? cbm_service_pattern_http_method(call->callee_name) : NULL; |
| 358 | const char *broker = |
| 359 | (svc == CBM_SVC_ASYNC) ? cbm_service_pattern_broker(res->qualified_name) : NULL; |
| 360 | int64_t route_id = create_svc_route_node(ctx, url_or_topic, svc, method, broker); |
| 361 | char esc_callee[CBM_SZ_256]; |
| 362 | char esc_url[CBM_SZ_256]; |
| 363 | cbm_json_escape(esc_callee, sizeof(esc_callee), call->callee_name); |
| 364 | cbm_json_escape(esc_url, sizeof(esc_url), url_or_topic); |
| 365 | char props[CBM_SZ_512]; |
| 366 | snprintf(props, sizeof(props), "{\"callee\":\"%s\",\"url_path\":\"%s\"%s%s%s%s%s}", esc_callee, |
| 367 | esc_url, method ? ",\"method\":\"" : "", method ? method : "", method ? "\"" : "", |
| 368 | broker ? ",\"broker\":\"" : "", broker ? broker : ""); |
| 369 | if (broker) { |
| 370 | size_t plen = strlen(props); |
| 371 | if (plen > 0 && props[plen - SKIP_ONE] != '}') { |
| 372 | snprintf(props + plen - 1, sizeof(props) - plen + SKIP_ONE, "\"}"); |
| 373 | } |
| 374 | } |
| 375 | calls_emit_edge(ctx->gbuf, source->id, route_id, edge_type, props, sizeof(props), call); |
| 376 | } |
| 377 | |
| 378 | /* Classify a resolved call and emit the appropriate edge. */ |
| 379 | /* When suppress_plain_calls is true (a TS/JS/TSX weak short-name member-call |
no test coverage detected