Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/qa-agent-notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,12 @@ jobs:
needs: [detect, wait-catalog]
if: always() && needs.detect.result == 'success' && needs.detect.outputs.skip != 'true' && needs.wait-catalog.result == 'success'
runs-on: [self-hosted, linux, x64, docker]
outputs:
tracking_id: ${{ steps.trigger.outputs.tracking_id }}
steps:
- uses: actions/checkout@v4
- name: Trigger qa-agent
id: trigger
env:
QA_AGENT_BASE_URL: http://am-qa-agents.am-apps-dev.svc.cluster.local:8150
QA_AGENT_GATEWAY_TOKEN: ${{ secrets.QA_AGENT_GATEWAY_TOKEN }}
Expand All @@ -145,3 +148,21 @@ jobs:
exit 1
fi
python3 scripts/qa_agent_notify_ci.py notify

wait-verify:
name: Wait verify (SPT/UI)
needs: [detect, notify]
if: always() && needs.detect.result == 'success' && needs.detect.outputs.skip != 'true' && needs.notify.result == 'success' && needs.notify.outputs.tracking_id != ''
runs-on: [self-hosted, linux, x64, docker]
steps:
- uses: actions/checkout@v4
- name: Poll until releasable decision
env:
QA_AGENT_BASE_URL: http://am-qa-agents.am-apps-dev.svc.cluster.local:8150
QA_AGENT_GATEWAY_TOKEN: ${{ secrets.QA_AGENT_GATEWAY_TOKEN }}
TRACKING_ID: ${{ needs.notify.outputs.tracking_id }}
QA_AGENT_VERIFY_TIMEOUT_SEC: "1200"
QA_AGENT_VERIFY_POLL_SEC: "10"
run: |
set -euo pipefail
python3 scripts/qa_agent_notify_ci.py wait-verify
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.am.trade.client.auth;

/**
* Optional per-request bearer for outbound trade HTTP calls.
* Provided by the host app (e.g. am-mcp-server AuthTokenProvider).
*/
@FunctionalInterface
public interface TradeAuthTokenSupplier {
String getToken();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import am.trade.sdk.AmTradeSdk;
import com.am.domain.trade.TradePortfolio;
import com.am.domain.trade.TradeTransaction;
import lombok.RequiredArgsConstructor;
import com.am.trade.client.auth.TradeAuthTokenSupplier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
Expand All @@ -14,73 +15,109 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

@Service
@Slf4j
@RequiredArgsConstructor
public class TradeClientService {

private final AmTradeSdk tradeSdk;
private final ObjectProvider<TradeAuthTokenSupplier> tokenSupplier;

public TradeClientService(AmTradeSdk tradeSdk, ObjectProvider<TradeAuthTokenSupplier> tokenSupplier) {
this.tradeSdk = tradeSdk;
this.tokenSupplier = tokenSupplier;
}

public List<TradePortfolio> getPortfolios(String userId) {
try {
Map<String, Object> response = tradeSdk.getPortfolioClient().getAllPortfolios(0, 100);
return mapPortfolios(response);
} catch (Exception e) {
log.error("Failed to fetch trade portfolios for user: {}", userId, e);
return Collections.emptyList();
}
return withAuth(() -> {
try {
Map<String, Object> response = tradeSdk.getPortfolioClient().getAllPortfolios(0, 100);
return mapPortfolios(response);
} catch (Exception e) {
log.error("Failed to fetch trade portfolios for user: {}", userId, e);
return Collections.emptyList();
}
});
}

public List<TradeTransaction> getRecentTrades(String userId) {
return getRecentTrades(userId, 0, 20);
}

public List<TradeTransaction> getRecentTrades(String userId, int page, int size) {
try {
Map<String, Object> response = tradeSdk.getTradeClient().getTradesByFreeTab(page, size);
return mapTrades(response, userId);
} catch (Exception e) {
log.error("Failed to fetch recent trades for user: {}", userId, e);
return Collections.emptyList();
}
return withAuth(() -> {
try {
Map<String, Object> response = tradeSdk.getTradeClient().getTradesByFreeTab(page, size);
return mapTrades(response, userId);
} catch (Exception e) {
log.error("Failed to fetch recent trades for user: {}", userId, e);
return Collections.emptyList();
}
});
}

public List<TradeTransaction> getTradesBySymbol(String userId, String symbol, int page, int size) {
try {
Map<String, Object> response = tradeSdk.getTradeClient()
.getTradesByFreeTabAndSymbol(symbol, page, size);
return mapTrades(response, userId);
} catch (Exception e) {
log.error("Failed to fetch trades for symbol {}: {}", symbol, e.getMessage());
return Collections.emptyList();
}
return withAuth(() -> {
try {
Map<String, Object> response = tradeSdk.getTradeClient()
.getTradesByFreeTabAndSymbol(symbol, page, size);
return mapTrades(response, userId);
} catch (Exception e) {
log.error("Failed to fetch trades for symbol {}: {}", symbol, e.getMessage());
return Collections.emptyList();
}
});
}

public Map<String, Object> filterTrades(Map<String, Object> filters) {
try {
return tradeSdk.getTradeClient().filterTrades(filters);
} catch (Exception e) {
log.error("Failed to filter trades: {}", e.getMessage());
return Map.of("error", e.getMessage() != null ? e.getMessage() : "filter failed");
}
return withAuth(() -> {
try {
return tradeSdk.getTradeClient().filterTrades(filters);
} catch (Exception e) {
log.error("Failed to filter trades: {}", e.getMessage());
return Map.of("error", e.getMessage() != null ? e.getMessage() : "filter failed");
}
});
}

public Map<String, Object> getTradeMetrics(String portfolioId) {
try {
return tradeSdk.getAnalyticsClient().getTradeMetrics(portfolioId);
} catch (Exception e) {
log.error("Failed to fetch trade metrics for {}: {}", portfolioId, e.getMessage());
return Map.of("error", e.getMessage() != null ? e.getMessage() : "metrics failed");
}
return withAuth(() -> {
try {
return tradeSdk.getAnalyticsClient().getTradeMetrics(portfolioId);
} catch (Exception e) {
log.error("Failed to fetch trade metrics for {}: {}", portfolioId, e.getMessage());
return Map.of("error", e.getMessage() != null ? e.getMessage() : "metrics failed");
}
});
}

public Map<String, Object> getTradePortfolioSummary(String portfolioId) {
try {
return tradeSdk.getPortfolioClient().getPortfolioSummary(portfolioId);
} catch (Exception e) {
log.error("Failed to fetch trade portfolio summary for {}: {}", portfolioId, e.getMessage());
return Map.of("error", e.getMessage() != null ? e.getMessage() : "summary failed");
return withAuth(() -> {
try {
return tradeSdk.getPortfolioClient().getPortfolioSummary(portfolioId);
} catch (Exception e) {
log.error("Failed to fetch trade portfolio summary for {}: {}", portfolioId, e.getMessage());
return Map.of("error", e.getMessage() != null ? e.getMessage() : "summary failed");
}
});
}

private <T> T withAuth(Supplier<T> action) {
TradeAuthTokenSupplier supplier = tokenSupplier.getIfAvailable();
if (supplier == null) {
return action.get();
}
Object lock = tradeSdk.getConfiguration();
synchronized (lock) {
String previous = tradeSdk.getConfiguration().getApiKey();
try {
String token = supplier.getToken();
tradeSdk.getConfiguration().setApiKey(token != null ? token : "");
return action.get();
} finally {
tradeSdk.getConfiguration().setApiKey(previous);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

Expand Down
Loading
Loading