| name | drogon-service-api-migration |
| description | Migrate Drogon plugin tests from old plugin API to new Service-based architecture. Use this skill whenever the user mentions migrating tests, updating to Service API, converting plugin calls, refactoring Drogon tests, or needs to update test code to use PaymentService, RefundService, CallbackService, etc. This skill handles the API migration pattern from plugin.method(req, callback) to service->method(request, key, callback) with Promise/Future for async handling. |
| compatibility | ["Requires Drogon Framework C++ codebase","Works with Google Test framework","Requires existing Service implementations (PaymentService, RefundService, etc.)"] |
Drogon Service API Migration
Migrate Drogon plugin tests from old HTTP request-based API to new type-safe Service architecture with async callback pattern.
Background
Drogon plugins are evolving from a monolithic API to a service-based architecture:
Old API (HTTP Request-based):
plugin.createPayment(req, callback);
plugin.refund(req, callback);
plugin.queryOrder(req, callback);
New API (Service-based):
auto service = plugin.paymentService();
CreatePaymentRequest request;
service->createPayment(request, idempotencyKey, callback);
Benefits:
- Type-safe request structures
- Better separation of concerns
- Easier to test
- Consistent error handling with std::error_code
When to Use This Skill
Use when:
- Migrating existing tests to use new PaymentService, RefundService, CallbackService
- Converting plugin API calls to Service API
- Updating test code after Service architecture refactor
- User mentions "update tests", "migrate to new API", "Service API conversion"
- Test code uses old
plugin.method(req, callback) pattern
Migration Pattern
Step 1: Identify Old API Usage
Search for old API patterns in test files:
plugin.createPayment(req, callback);
plugin.refund(req, callback);
plugin.queryOrder(req, callback);
Step 2: Convert to New Service API
General transformation:
auto req = drogon::HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setBody(body);
req->addHeader("Idempotency-Key", idempotencyKey);
plugin.createPayment(req, [&promise](const drogon::HttpResponsePtr &resp) {
promise.set_value(resp);
});
CreatePaymentRequest request;
request.userId = "10001";
request.amount = "9.99";
request.currency = "CNY";
request.description = "Test Order";
request.channel = "WEB";
std::promise<Json::Value> resultPromise;
std::promise<std::error_code> errorPromise;
auto paymentService = plugin.paymentService();
paymentService->createPayment(
request,
idempotencyKey,
[&resultPromise, &errorPromise](const Json::Value& result, const std::error_code& error) {
resultPromise.set_value(result);
errorPromise.set_value(error);
});
auto errorFuture = errorPromise.get_future();
const auto error = errorFuture.get();
if (error) {
}
auto resultFuture = resultPromise.get_future();
const auto result = resultFuture.get();
Step 3: Add Required Includes
Ensure test file includes necessary headers:
#include <future>
#include "services/PaymentService.h"
#include "services/RefundService.h"
#include "services/CallbackService.h"
Step 4: Update Test Assertions
Old API used HttpResponse, new API uses Json::Value:
EXPECT_EQ(resp->getStatusCode(), k200OK);
auto body = resp->getBody();
Json::Value result;
Json::Reader().parse(body, result);
EXPECT_TRUE(result.isMember("data"));
EXPECT_TRUE(result.isMember("data"));
EXPECT_FALSE(result.isMember("error"));
Service-Specific Patterns
PaymentService
Old:
plugin.createPayment(req, callback);
New:
CreatePaymentRequest request;
request.userId = userId;
request.amount = amount;
request.currency = "CNY";
request.description = description;
request.channel = channel;
auto paymentService = plugin.paymentService();
paymentService->createPayment(request, idempotencyKey, callback);
RefundService
Old:
plugin.refund(req, callback);
New:
CreateRefundRequest request;
request.orderNo = orderNo;
request.paymentNo = paymentNo;
request.amount = amount;
request.reason = reason;
auto refundService = plugin.refundService();
refundService->createRefund(request, idempotencyKey, callback);
CallbackService
Old:
plugin.handleWechatCallback(req, callback);
New:
auto callbackService = plugin.callbackService();
callbackService->handlePaymentCallback(
req->body(),
req->getHeader("Wechatpay-Signature"),
req->getHeader("Wechatpay-Timestamp"),
req->getHeader("Wechatpay-Nonce"),
req->getHeader("Wechatpay-Serial"),
callback
);
Query Services
Old:
plugin.queryOrder(orderNo, callback);
New:
auto paymentService = plugin.paymentService();
paymentService->queryOrder(orderNo, callback);
Error Handling
New API uses std::error_code:
auto errorFuture = errorPromise.get_future();
const auto error = errorFuture.get();
if (error) {
std::cout << "Error: " << error.message() << std::endl;
}
Common error codes:
1001-1999: Payment errors
1404: Not found
1409: Conflict (idempotency)
Testing After Migration
After migrating tests, verify:
-
Compilation:
cd PayBackend
cmake --build build --target test_payplugin
-
Run specific test:
./build/Release/test_payplugin.exe --gtest_filter="*YourTest*"
-
Verify test still passes:
- Test logic should be identical
- Only API calls changed
- Assertions should still work
Common Pitfalls
1. Forgetting std::promise
Wrong:
service->createPayment(request, key, [](const Json::Value& result, const std::error_code& error) {
});
Right:
std::promise<Json::Value> resultPromise;
service->createPayment(request, key, [&resultPromise](...) {
resultPromise.set_value(result);
});
auto result = resultPromise.get_future().get();
2. Not checking errors
Wrong:
auto result = resultFuture.get();
Right:
auto error = errorFuture.get();
if (error) {
return;
}
auto result = resultFuture.get();
3. Mixing old and new APIs
Don't mix:
plugin.createPayment(req1, callback);
service->createPayment(req2, key, callback);
Be consistent - migrate all related calls together.
Best Practices
- Migrate test by test - Don't try to migrate everything at once
- Keep test logic intact - Only change API calls, not test behavior
- Run tests frequently - Verify after each migration
- Commit in small batches - Easier to revert if something breaks
- Add comments for complex migrations - Help future maintainers
Example: Complete Test Migration
Before (Old API):
TEST(PaymentTest, CreatePaymentSuccess) {
std::string body = R"({
"user_id": "10001",
"amount": "9.99",
"currency": "CNY"
})";
auto req = drogon::HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/api/v1/payment");
req->setBody(body);
std::promise<drogon::HttpResponsePtr> promise;
plugin.createPayment(req, [&promise](const drogon::HttpResponsePtr &resp) {
promise.set_value(resp);
});
auto resp = promise.get_future().get();
EXPECT_EQ(resp->getStatusCode(), k200OK);
}
After (New API):
TEST(PaymentTest, CreatePaymentSuccess) {
CreatePaymentRequest request;
request.userId = "10001";
request.amount = "9.99";
request.currency = "CNY";
request.description = "Test Order";
request.channel = "WEB";
std::string idempotencyKey = "test_key_" + std::to_string(std::time(nullptr));
std::promise<Json::Value> resultPromise;
std::promise<std::error_code> errorPromise;
auto paymentService = plugin.paymentService();
paymentService->createPayment(
request,
idempotencyKey,
[&resultPromise, &errorPromise](const Json::Value& result, const std::error_code& error) {
resultPromise.set_value(result);
errorPromise.set_value(error);
});
auto errorFuture = errorPromise.get_future();
const auto error = errorFuture.get();
EXPECT_FALSE(error);
auto resultFuture = resultPromise.get_future();
const auto result = resultFuture.get();
EXPECT_TRUE(result.isMember("data"));
}
Verification Checklist
After migration:
Related Skills
- cpp-project-file-cleanup - Clean up project after migration
- cpp-test-refactoring - General C++ test refactoring
- drogon-release-build - Ensure correct build configuration
References
See project examples:
test/QueryOrderTest.cc - Query API migration
test/RefundQueryTest.cc - Refund API migration
test/CreatePaymentIntegrationTest.cc - Payment API migration