Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 20 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ on:
- "rabbitmq/**"
- "servicebus/**"
- "kafka/**"
- "sqs/**"
- ".github/workflows/release.yml"
pull_request:
branches: [ "main" ]
paths:
- "rabbitmq/**"
- "servicebus/**"
- "kafka/**"
- "sqs/**"
- ".github/workflows/release.yml"
workflow_dispatch:
inputs:
Expand All @@ -36,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
plugin: [rabbitmq, servicebus, kafka]
plugin: [rabbitmq, servicebus, kafka, sqs]
platform: [windows-latest, windows-11-arm, ubuntu-22.04, ubuntu-22.04-arm, macos-26-intel, macos-14]

steps:
Expand Down Expand Up @@ -91,6 +93,18 @@ jobs:
echo "INSTALL_PACKAGES=cmake" >> "$GITHUB_ENV"
fi
;;
sqs)
echo "DIRECTORY=sqs" >> "$GITHUB_ENV"
echo "ARTIFACT_PATH=sqs/build/SqsPlugin" >> "$GITHUB_ENV"
echo "CONFIGURE_ARGS=-DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV"
if [ "${{ runner.os }}" = "Linux" ]; then
echo "INSTALL_PACKAGES=build-essential cmake git ninja-build pkg-config" >> "$GITHUB_ENV"
elif [ "${{ runner.os }}" = "macOS" ]; then
echo "INSTALL_PACKAGES=cmake ninja pkg-config" >> "$GITHUB_ENV"
else
echo "INSTALL_PACKAGES=cmake" >> "$GITHUB_ENV"
fi
;;
kafka)
echo "DIRECTORY=kafka" >> "$GITHUB_ENV"
echo "ARTIFACT_PATH=kafka/build/KafkaPlugin" >> "$GITHUB_ENV"
Expand Down Expand Up @@ -140,6 +154,11 @@ jobs:
run: |
cmake --build build --config Release

- name: Test (${{ matrix.plugin }})
working-directory: ${{ env.DIRECTORY }}
run: |
ctest --test-dir build -C Release --output-on-failure

- name: Package (${{ matrix.plugin }})
run: |
mkdir -p release-payloads
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ build/
bin/
obj/
Binaries/
vcpkg_installed/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Official open-source Graftcode Gateway plugins for carrying Graft calls over ext
| [rabbitmq](rabbitmq/) | RabbitMQ (AMQP 0-9-1), request/reply |
| [servicebus](servicebus/) | Azure Service Bus (AMQP 1.0), request/reply and one-way |
| [kafka](kafka/) | Apache Kafka, request/reply (correlation-id) |
| [sqs](sqs/) | Amazon SQS, request/reply and one-way |
| [observability/opentelemetry](observability/opentelemetry/) | OpenTelemetry / Azure Application Insights connector |

Each plugin has its own README with build and configuration steps. For how the Gateway loads a plugin, see the "Plugin server config" section of the [Graftcode Gateway](https://github.com/grft-dev/graftcode-gateway) README.
Expand Down
21 changes: 21 additions & 0 deletions sqs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
set(CMAKE_MIN 3.22)
cmake_minimum_required(VERSION ${CMAKE_MIN})
set(CMAKE_POLICY_VERSION_MINIMUM ${CMAKE_MIN})
cmake_policy(VERSION ${CMAKE_MIN})

project("SqsPlugin" VERSION 1.0.0)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)

add_definitions(-DUNICODE)
enable_testing()

if(MSVC)
add_compile_options(/utf-8)
endif()

add_subdirectory("SqsPlugin")
add_subdirectory("SqsPluginTest")
22 changes: 22 additions & 0 deletions sqs/GraftcodePluginsInterfaces/IServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <cstddef>

namespace GraftcodeGateway
{
class IServer
{
public:
using byte = unsigned char;
using WriteResponseFn =
void(*)(void* context, const byte* data, std::size_t size);
using ProcessMessageFn =
bool(*)(const byte* requestData, std::size_t requestSize,
WriteResponseFn writeResponse, void* writeContext);

virtual ~IServer() = default;
virtual void configure(const char* jsonConfig, ProcessMessageFn processMessage) = 0;
virtual void start() = 0;
virtual void stop() = 0;
};
}
18 changes: 18 additions & 0 deletions sqs/GraftcodePluginsInterfaces/ITransport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include "_common.h"

namespace Hypertube::Native::Interfaces
{
class ITransport
{
public:
virtual ~ITransport() = default;
virtual int Initialize(
byte callingRuntimeNumber,
byte calledRuntimeNumber,
byte calledRuntimeVersion) = 0;
virtual int SendCommand(byte* messageByteArray, int32_t messageByteArrayLen) = 0;
virtual int ReadResponse(byte* responseByteArray, int32_t responseByteArrayLen) = 0;
};
}
5 changes: 5 additions & 0 deletions sqs/GraftcodePluginsInterfaces/_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

#include <cstdint>

typedef unsigned char byte;
155 changes: 155 additions & 0 deletions sqs/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Amazon SQS Plugin

This native C++ plugin carries Graftcode Gateway calls over Amazon Simple Queue
Service (SQS). It implements the standard Graftcode transport and server
interfaces and exports `CreateTransportChannel` / `DestroyTransportChannel` and
`CreateServer` / `DestroyServer`.

Binary Graftcode payloads are Base64-encoded into the SQS message body. RPC
metadata is carried in message attributes:

- `GraftcodeCorrelationId` identifies the request and response.
- `GraftcodeReplyTo` contains the caller's reply queue URL.

## Delivery model

SQS provides at-least-once delivery. The server deletes a request only after
`processMessage` succeeds and, for RPC, the response has been sent. Configure a
dead-letter queue and make called operations idempotent.

SQS cannot filter receives by message attribute. Consequently, an RPC reply
queue must be dedicated to one client/runtime plugin instance. Do not share one
reply queue between independent processes. Calls made through one transport
instance are serialized.

Standard and FIFO queues are supported. For a FIFO queue URL ending in
`.fifo`, the plugin automatically sets `MessageGroupId` and
`MessageDeduplicationId`.

## Build

The plugin uses the AWS SDK for C++ (`sqs`) and `nlohmann-json`, installed in
vcpkg manifest mode.

```powershell
git clone https://github.com/microsoft/vcpkg.git
.\vcpkg\bootstrap-vcpkg.bat

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE=.\vcpkg\scripts\buildsystems\vcpkg.cmake
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
```

The output is `build/SqsPlugin/SqsPlugin.dll` on Windows or
`build/SqsPlugin/libSqsPlugin.so` / `.dylib` on Linux/macOS. Use
`libSqsPlugin` as the configured plugin name when the library has the `lib`
prefix.

## Create queues

Create one request queue and one reply queue per client runtime:

```bash
aws sqs create-queue --queue-name graft-requests
aws sqs create-queue --queue-name graft-replies-client-1
```

Use the returned queue URLs in configuration. The gateway identity needs
`sqs:ReceiveMessage`, `sqs:DeleteMessage`, and `sqs:SendMessage`; a client needs
`sqs:SendMessage`, `sqs:ReceiveMessage`, and `sqs:DeleteMessage` for its queues.

## Gateway configuration

```json
{
"name": "SqsPlugin",
"region": "eu-central-1",
"requestQueueUrl": "https://sqs.eu-central-1.amazonaws.com/123456789012/graft-requests",
"waitTimeSeconds": 10,
"visibilityTimeoutSeconds": 60
}
```

Run the Gateway:

```powershell
./gg .\PhysicsCalculator.dll --config .\pluginConfig.json
```

## Runtime configuration

```csharp
string configSource =
"""
{
"configurations": {
"graft.nuget.PhysicsCalculator": {
"runtime": "netcore",
"stateless": true,
"plugin": {
"name": "SqsPlugin",
"region": "eu-central-1",
"requestQueueUrl": "https://sqs.eu-central-1.amazonaws.com/123456789012/graft-requests",
"replyQueueUrl": "https://sqs.eu-central-1.amazonaws.com/123456789012/graft-replies-client-1",
"rpcTimeoutMs": 30000,
"waitTimeSeconds": 10
}
}
}
}
""";

graft.nuget.PhysicsCalculator.GraftConfig.SetConfig(configSource);
```

The AWS SDK default credential provider chain is used. Prefer IAM roles,
environment variables, shared AWS config, or workload identity. Static
credentials are accepted as `accessKeyId`, `secretAccessKey`, and optional
`sessionToken`, primarily for local emulators.

## One-way calls

Set `"oneWay": true` in both runtime and gateway configuration. The client sends
without `GraftcodeReplyTo`, and the gateway processes and deletes the request
without sending a response.

## LocalStack

Start the included LocalStack setup:

```bash
docker compose up -d
aws --endpoint-url http://localhost:4566 sqs create-queue --queue-name graft-requests
aws --endpoint-url http://localhost:4566 sqs create-queue --queue-name graft-replies
```

Use this additional configuration:

```json
{
"region": "us-east-1",
"endpointOverride": "http://localhost:4566",
"accessKeyId": "test",
"secretAccessKey": "test",
"verifySsl": false
}
```

## Configuration reference

- `region` — AWS region; defaults to `us-east-1`.
- `requestQueueUrl` — required request queue URL (`queueUrl` and `queue` aliases
are accepted).
- `replyQueueUrl` — required by an RPC client; optional on the gateway because
each request supplies its reply destination.
- `rpcTimeoutMs` — RPC response timeout; defaults to `30000`.
- `waitTimeSeconds` — SQS long-poll duration, 1–20; defaults to `1`.
- `visibilityTimeoutSeconds` — per-receive visibility timeout, 1–43200;
defaults to `30`. Set it longer than the maximum call execution time.
- `oneWay` — process without a response; defaults to `false`.
- `messageGroupId` — FIFO message group; defaults to `graftcode`.
- `endpointOverride` — custom SQS endpoint, such as LocalStack.
- `verifySsl` — TLS certificate verification; defaults to `true`.
- `accessKeyId`, `secretAccessKey`, `sessionToken` — optional explicit
credentials; otherwise the AWS SDK provider chain is used.
23 changes: 23 additions & 0 deletions sqs/SqsPlugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
set(target_name SqsPlugin)

add_library(${target_name} SHARED
"SqsConfig.cpp"
"SqsUtil.cpp"
"SqsClient.cpp"
"TransportSqs.cpp"
"SqsServer.cpp"
)

find_package(aws-cpp-sdk-core CONFIG REQUIRED)
find_package(aws-cpp-sdk-sqs CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)

target_include_directories(${target_name} PUBLIC
"${CMAKE_SOURCE_DIR}/GraftcodePluginsInterfaces"
)

target_link_libraries(${target_name} PUBLIC
aws-cpp-sdk-sqs
aws-cpp-sdk-core
nlohmann_json::nlohmann_json
)
Loading
Loading