Skip to content

Commit d6dc6a3

Browse files
SRTP-769-implement-api-send-gpd-message
1 parent 4915ed0 commit d6dc6a3

File tree

7 files changed

+157
-1
lines changed

7 files changed

+157
-1
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Deploy on AKS
2+
3+
on:
4+
push:
5+
branches:
6+
- '**'
7+
workflow_dispatch:
8+
inputs:
9+
environment:
10+
required: true
11+
type: string
12+
default: dev
13+
description: env
14+
15+
env:
16+
NAMESPACE: afm
17+
APP_NAME: rtp-platform-qa
18+
19+
permissions:
20+
id-token: write
21+
contents: read
22+
23+
jobs:
24+
deploy:
25+
runs-on: [ self-hosted, self-hosted-job, "${{ github.event.inputs.environment || 'dev' }}" ]
26+
environment: ${{ github.event.inputs.environment || 'dev' }}
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- name: Check runner environment
31+
run: |
32+
echo "🏃 Running on environment: ${{ github.event.inputs.environment || 'dev' }}"
33+
echo "📦 Namespace: ${{ env.NAMESPACE }}"
34+
echo "📦 App: ${{ env.APP_NAME }}"

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ celerybeat.pid
122122
*.sage.py
123123

124124
# Environments
125-
.env
125+
gpd-test/.env
126126
.venv
127127
env/
128128
venv/

gpd-test/dto.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from enum import Enum
2+
from pydantic import BaseModel, Field
3+
4+
class RTPOperationCode(str, Enum):
5+
CREATE = "CREATE"
6+
UPDATE = "UPDATE"
7+
DELETE = "DELETE"
8+
9+
class PaymentPositionStatus(str, Enum):
10+
CREATED = "CREATED"
11+
SENT = "SENT"
12+
ACCEPTED = "ACCEPTED"
13+
14+
class RTPMessage(BaseModel):
15+
id: int
16+
operation: RTPOperationCode
17+
timestamp: int
18+
iuv: str
19+
subject: str
20+
description: str
21+
ec_tax_code: str = Field(..., alias="ec_tax_code")
22+
debtor_tax_code: str = Field(..., alias="debtor_tax_code")
23+
nav: str
24+
due_date: int = Field(..., alias="due_date")
25+
amount: int
26+
status: PaymentPositionStatus
27+
psp_code: str = Field(..., alias="psp_code")
28+
psp_tax_code: str = Field(..., alias="psp_tax_code")
29+
is_partial_payment: bool = Field(..., alias="is_partial_payment")

gpd-test/keyvault.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# from azure.identity import AzureCliCredential
2+
# from azure.keyvault.secrets import SecretClient
3+
# import os
4+
#
5+
# def get_eventhub_connection_string():
6+
# keyvault_name = os.environ["KEYVAULT_NAME"]
7+
# secret_name = os.environ["EVENTHUB_SECRET_NAME"]
8+
#
9+
# keyvault_url = f"https://{keyvault_name}.vault.azure.net"
10+
# credential = AzureCliCredential()
11+
# client = SecretClient(vault_url=keyvault_url, credential=credential)
12+
#
13+
# secret = client.get_secret(secret_name)
14+
# print(f"[KeyVault] Retrieved secret '{secret_name}' from '{keyvault_name}'")
15+
# return secret.value
16+
17+
from azure.identity import DefaultAzureCredential
18+
from azure.keyvault.secrets import SecretClient
19+
import os
20+
21+
def get_eventhub_connection_string():
22+
keyvault_name = os.environ["KEYVAULT_NAME"]
23+
secret_name = os.environ["EVENTHUB_SECRET_NAME"]
24+
25+
keyvault_url = f"https://{keyvault_name}.vault.azure.net"
26+
credential = DefaultAzureCredential()
27+
client = SecretClient(vault_url=keyvault_url, credential=credential)
28+
29+
secret = client.get_secret(secret_name)
30+
print(f"[KeyVault] Retrieved secret '{secret_name}' from '{keyvault_name}'")
31+
return secret.value

gpd-test/main.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from fastapi import FastAPI, HTTPException, Request
2+
from contextlib import asynccontextmanager
3+
from producer import setup_producer
4+
from dto import RTPMessage
5+
from dotenv import load_dotenv
6+
import os, json
7+
8+
load_dotenv()
9+
10+
EVENTHUB_TOPIC = os.environ["EVENTHUB_TOPIC"]
11+
12+
@asynccontextmanager
13+
async def app_lifespan(fastapi_app: FastAPI):
14+
kafka_producer = await setup_producer()
15+
app.state.producer = kafka_producer
16+
yield
17+
await kafka_producer.stop()
18+
19+
app = FastAPI(lifespan=app_lifespan)
20+
21+
@app.post("/send/gpd/message")
22+
async def send_msg(message: RTPMessage, request: Request):
23+
try:
24+
producer = request.app.state.producer
25+
await producer.send_and_wait(
26+
EVENTHUB_TOPIC,
27+
json.dumps(message.model_dump(by_alias=True)).encode("utf-8")
28+
)
29+
return {"status": "ok"}
30+
except Exception as e:
31+
raise HTTPException(status_code=500, detail=str(e))

gpd-test/producer.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from aiokafka import AIOKafkaProducer
2+
import os, ssl
3+
from keyvault import get_eventhub_connection_string
4+
5+
async def setup_producer():
6+
connection_string = get_eventhub_connection_string()
7+
namespace = os.environ["EVENTHUB_NAMESPACE"]
8+
9+
ssl_context = ssl.create_default_context()
10+
ssl_context.check_hostname = True
11+
ssl_context.verify_mode = ssl.CERT_REQUIRED
12+
13+
producer = AIOKafkaProducer(
14+
bootstrap_servers=f"{namespace}.servicebus.windows.net:9093",
15+
security_protocol="SASL_SSL",
16+
sasl_mechanism="PLAIN",
17+
sasl_plain_username="$ConnectionString",
18+
sasl_plain_password=connection_string,
19+
ssl_context=ssl_context,
20+
client_id="python-producer"
21+
)
22+
await producer.start()
23+
return producer

requirements.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ faker==22.5.1
77
requests==2.32.4
88
schwifty==2025.1.0
99
urllib3==2.5.0
10+
11+
python-dotenv~=1.1.1
12+
pydantic~=2.11.7
13+
pytest~=8.4.1
14+
fastapi~=0.116.1
15+
aiokafka~=0.12.0
16+
keyvault~=0.2.1
17+
azure-core~=1.35.0

0 commit comments

Comments
 (0)