# Event Service

This guide provides step-by-step instructions for creating a new APIM API to publish events to Azure Service Bus Topics using the SAIF platform templates.

!!! tip "Aspire Publish Alternative"
    Starting in Forge 3.3, if your project uses an Aspire AppHost, you can register event services with `builder.AddEventService("events")` from the `SAIF.Platform.Aspire.Hosting` package and generate pipeline YAML automatically. See [Aspire Publish](../aspire-publish.md) for details.

[TOC]

## 📚 Useful Resources

- [SAIF CLI Documentation](../install-saif-cli.md)
- [TypeSpec Documentation](../../../reference/tools/typespec.md)
- [TypeSpec Official Documentation](https://typespec.io/)
- [Azure API Management Documentation](https://docs.microsoft.com/en-us/azure/api-management/)
- [Azure Service Bus Documentation](https://docs.microsoft.com/en-us/azure/service-bus-messaging/)

## 📋 Prerequisites

- ✅ **SAIF CLI** and **TypeSpec** installed and updated
- ✅ **Visual Studio Code** with TypeSpec extension
- ✅ **Access to SAIF Azure DevOps organization**
- ✅ **Permissions** for creating pipelines and Azure resources

## ⚡ Step 1: Create Your Project

To create a new event service project, run the following command in your project root directory:

```bash
saif new saif-event-service
```

### 🔧 Command Options

You can provide all parameters upfront to skip the interactive prompts:

```bash
saif new saif-event-service --name <your-app-name> --owner <owner-name>
```

**Parameters:**

- **`--name`**: Your application name (will be used in project naming)
- **`--owner`**: The team or individual responsible for the service

### 💡 Example

```bash
saif new saif-event-service --name myapp --owner Platform
```

This creates an event service with:

- **Project ID**: `events-myapp` (automatically generated from "events" + name)
- **Application Name**: `myapp`
- **Owner**: `Platform`

### ✅ Success Indicators

After running the command, you should see:

- ✅ New project folder created with your app name
- ✅ Files generated successfully message
- ✅ No error messages in the terminal

### 🎯 What Happens Next?

1. **Navigate into your new project directory**: `cd <your-project-name>`
2. **Verify the project structure** matches the layout below
3. **Proceed to Step 2** to configure security keys

<details>
<summary>📁 Click to see detailed project structure</summary>

```
your-project/
├── .azdo/                              # Azure Pipelines configuration
│   ├── azure-pipelines.yml             # Main deployment pipeline
│   ├── azure-pipelines-pr.yml          # Pull request validation pipeline
│   └── vars/                           # Pipeline variables
├── infra/                              # Infrastructure as Code
│   ├── bootstrap/                      # Environment bootstrapping
│   └── event/                          # Event service infrastructure
│       └── openapi/                    # Generated OpenAPI specifications
├── src/                                # Source code
│   └── SAIF.[AppName].TypeSpec/        # TypeSpec project for API definitions
└── .gitignore
```

**Key Components:**

- **🔄 `.azdo`**: Contains all Azure Pipelines for PR validation and deployment
- **🏗️ `infra`**: Contains Terraform configurations for APIM API, Service Bus Topics, and OpenAPI specs
- **💻 `src`**: Contains TypeSpec project for defining API operations and event models

</details>

## 🗝️ Step 2: Configure Security Keys

### 🔑 What You Need to Provide

Your event service API requires **subscription key authentication** to protect your Service Bus topics. You need to bring a **primary key for each environment** (TEST, QA, UAT, PROD) that will serve as the authentication token for consuming applications.

⚠️ **Required**: You must provide primary keys for each environment before deployment, or the infrastructure deployment will fail.

### 🔑 Generate and Configure Primary Key (Recommended)

If you need to generate secure primary keys, use Keeper's password generator. Be sure it only contains alphanumerics (hyphens and underscores are allowed, but the pipeline is sensitive to symbols). It is recommended to use a key length of at least 32 characters, maximum 256.

1. **Configure Azure DevOps Variable Group:**
   - Navigate to **Azure DevOps** → **Pipelines** → **Library**
   - There should already be a group created by the SAIF CLI named after your project ID (e.g., `events-myapp`), with the environment-specific variables pre-defined.
   - Add a different primary key for each environment.

⚠️ **Critical**: If you rename or use a different variable group, its name must match the `secretsVariableGroupName` value in your `.azdo/vars/event-service.yml` file.

### ✅ Success Indicators

After configuring security keys, you should have:

- ✅ Variable group created in Azure DevOps Library
- ✅ Primary keys added for TEST, QA, UAT, and PROD environments (marked as secret)

### 🎯 What Happens Next?

1. **Your keys are stored securely** in Azure DevOps for the deployment pipeline to use
2. **Proceed to Step 3** to define your event structure with TypeSpec

> 📝 **Note**: All SAIF event service APIs use mandatory subscription key authentication. Every API call must include a valid subscription key in the `Ocp-Apim-Subscription-Key` header.

## 💻 Step 3: Define Your Events with TypeSpec

### 🎯 What is TypeSpec?

TypeSpec is a language for defining APIs and data models. It will automatically generate the OpenAPI specifications that configure your event service. Think of it as describing what your events look like so the system can create the right APIs.

### 📂 Open Your TypeSpec Project

```bash
cd src/SAIF.[YourAppName].TypeSpec
code .  # Opens in Visual Studio Code
```

### 🏗️ Understanding the TypeSpec Template

Here's what a complete TypeSpec service looks like with explanations:

```typescript
import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi";
import "@typespec/openapi3";
import "@typespec/versioning";
import "@saif/platform-typespec";

using TypeSpec.Http;
using TypeSpec.Rest;
using SAIF.Platform;

@service({
  title: "Your Event Service",        // 👈 This appears in your API documentation
  version: "1.0"                    // 👈 Version your API for consumers
})
@useAuth(ApiKeyAuth<ApiKeyLocation.header, "Ocp-Apim-Subscription-Key">)  // 👈 Requires subscription key
namespace YourEventService;

// 👇 Automatically creates REST endpoints
@autoRoute
interface UserCreatedTopic extends Topic<UserCreatedEvent> {}

// 👇 Define your event structure - customize this for your use case
model UserCreatedEvent is Event<"UserCreated"> {
  userId: string;          // Required field
  email: string;           // Required field
  firstName?: string;      // Optional field (note the ?)
  lastName?: string;       // Optional field
}
```

**Key Concepts:**

- **🎯 Each event type maps to one Service Bus topic** (e.g. `UserCreatedTopic` → `UserCreated` topic)
- **🔐 The `@useAuth` decorator makes subscription keys mandatory** for all operations
- **⚡ The `@autoRoute` decorator creates REST endpoints** from your interface

### 🔄 Build and Validate

```bash
npm install    # Install dependencies
npm run build  # Validate and generate OpenAPI specs
```

### ✅ Success Indicators

After building your TypeSpec definitions, you should see:

- ✅ No compilation errors in the terminal
- ✅ OpenAPI files generated in the `openapi/` folder
- ✅ TypeSpec validation passes without warnings

### 🎯 What Happens Next?

1. **Your TypeSpec is compiled** into OpenAPI specifications
2. **These specs define your API structure** for the deployment pipeline
3. **Proceed to Step 4** to deploy your event service

## 🚀 Step 4: Deploy Your Event Service

### 🎯 What This Step Does

The deployment pipeline will take your TypeSpec definitions and security keys to create a fully functional event service in Azure, including the API endpoints and Service Bus topics.

1. **Navigate to Azure DevOps Pipelines**
2. **Run the `events-<project-name>` pipeline** (e.g., `events-myapp`)

The pipeline will:

- ✅ Compile TypeSpec and generate OpenAPI specifications
- ✅ Deploy OpenAPI specifications to a dedicated OpenAPI storage account
- ✅ Deploy APIM API with auto-generated operations
- ✅ Create Service Bus Topics for event publishing
- ✅ Configure security policies and activate subscription keys

### ✅ Success Indicators

A successful deployment should show:

- ✅ All pipeline stages completed without errors
- ✅ Green checkmarks for each deployment environment
- ✅ No failed tasks in the pipeline logs

### 🌐 Access Your OpenAPI Specifications

After deployment, specifications are available at:

```
https://openapi.saif.com/<project-id>/<environment>/openapi.v<version-number>.yaml
```

### 🧪 Test Your API

```bash
curl -X POST "https://app-int-<environment>.saif.com/api/event/<api-name>/<topic-name>" \
  -H "Ocp-Apim-Subscription-Key: your-primary-key" \
  -H "Content-Type: application/json" \
  -H "X-Event-Type: UserCreated" \
  -d '{<your-request-body>}'
```

### 💡 Example

```bash
curl -X POST "https://app-int-test.saif.com/api/event/events-myapp/UserCreated" \
  -H "Ocp-Apim-Subscription-Key: 1234-45678" \
  -H "Content-Type: application/json" \
  -H "X-Event-Type: UserCreated" \
  -d '{"userId": "123", "email": "user@example.com"}'
```

### 🎯 What Happens Next?

1. **Your event service is live** and ready to receive requests from the API
2. **Share your OpenAPI specs** with teams that need to consume your events
3. **Monitor your Service Bus topics** for incoming events
4. **Distribute subscription keys securely** to authorized consuming teams

### 📊 Visual Flow

```
TypeSpec Definition → OpenAPI Specs → APIM API → Service Bus Topics → Event Consumers
     (Step 3)           (Pipeline)     (Azure)         (Azure)         (Your Teams)
```

## 🔧 Troubleshooting

### ❌ Deployment fails with "primary_key not found"

- **Solution**: Add `primary_key` secret to Azure DevOps variable group
- **Check**: Variable group name exactly matches `secretsVariableGroupName` in `.azdo/vars/event-service.yml`
- **Verify**: Use environment-specific naming: `[TEST]primary_key`, ..., `[PROD]primary_key`

### ❌ API calls return 401 Unauthorized

- **Solution**: Include `Ocp-Apim-Subscription-Key` header with correct subscription key value

### ❌ API calls return 403 Forbidden

- **Solution**: Verify Service Bus topic exists and is not disabled
- **Check**: Ensure topic has not been disabled due to quota limits or administrative action

### ❌ TypeSpec compilation errors

- **Solution**: Run `npm install` and verify TypeSpec syntax follows SAIF platform conventions

---

## 📚 Related Documentation

- [Event Orchestration Foundry Example](../../../foundry/event-orchestration.md) - Working example of the saga pattern for distributed event-driven transactions
