Skip to content

Commit

Permalink
feat(billing): add support for customer and user management (#18)
Browse files Browse the repository at this point in the history
- Add `createCustomerFunction` and `deleteCustomerFunction` properties
  to the `IBilling` interface, representing functions to create and
delete customers (entities with zero or more users).
- Add optional `createUserFunction` and `deleteUserFunction` properties
  to the `IBilling` interface, representing functions to create and
delete users belonging to a customer.
- Add `IFunctionTrigger` and `IFunctionSchedule` interfaces to support
  triggering functions based on events or schedules, respectively.
- Update the `BillingProvider` construct to create event targets for the
  new customer and user management functions, and to optionally create a
scheduled event for the `putUsageFunction`.
- Update documentation for the `IBilling` interface, `DetailType` enum,
  and other related components.

This change allows for more granular control over customer and user
management in the billing module, enabling integration with
application-specific requirements.
  • Loading branch information
suhussai authored Mar 27, 2024
1 parent 6e7e5b7 commit 4d325b3
Show file tree
Hide file tree
Showing 8 changed files with 637 additions and 165 deletions.
348 changes: 276 additions & 72 deletions API.md

Large diffs are not rendered by default.

36 changes: 16 additions & 20 deletions docs/public/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,16 +551,15 @@ The control plane emits this event any time it onboards a new tenant. This event

```json
{
"Source": "sbt-control-plane-api",
"DetailType": "Onboarding",
"Detail": {
"source": "sbt-control-plane-api",
"detail-type": "Onboarding",
"detail": {
"tenantId": "guid string",
"tenantStatus": "see notes",
"tenantName": "tenant name",
"email": "[email protected]",
"isActive": "boolean"
},
"EventBusName": "sbt-event-bus"
}
}
```

Expand All @@ -576,13 +575,12 @@ Upon successful tenant provisioning, the Serverless SaaS reference architecture

```json
{
"Source": "sbt-application-plane-api",
"DetailType": "Onboarding",
"Detail": {
"source": "sbt-application-plane-api",
"detail-type": "Onboarding",
"detail": {
"tenantConfig": "json string - see notes",
"tenantStatus": "Complete",
},
"EventBusName": "sbt-event-bus"
}
}
```

Expand All @@ -594,13 +592,12 @@ The control plane emits this event any time it offboards a tenant. At a minimum

```json
{
"Source": "sbt-control-plane-api",
"DetailType": "Offboarding",
"Detail": {
"source": "sbt-control-plane-api",
"detail-type": "Offboarding",
"detail": {
"tenantId": "string",
"tier": "string",
},
"EventBusName": "sbt-event-bus"
}
}
```

Expand All @@ -612,12 +609,11 @@ The application plane emits this event upon completion of offboarding. Similar t

```json
{
"Source": "sbt-application-plane-api",
"DetailType": "Offboarding",
"Detail": {
"source": "sbt-application-plane-api",
"detail-type": "Offboarding",
"detail": {
"tenantStatus": "Deleted",
},
"EventBusName": "sbt-event-bus"
}
}
```

Expand Down
1 change: 0 additions & 1 deletion resources/functions/tenant-management/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
CORSConfig)
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.event_handler.exceptions import (
BadRequestError,
InternalServerError,
NotFoundError,
)
Expand Down
24 changes: 12 additions & 12 deletions src/control-plane/auth/auth-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,60 +11,60 @@ export interface IAuth {
/**
* Authorizer referenced by the ControlPlaneAPI
*/
authorizer: IAuthorizer;
readonly authorizer: IAuthorizer;

/**
* Contains any information relevant to the IDP implementation required by the Authorizer and User Function implementations
*/
controlPlaneIdpDetails: any;
readonly controlPlaneIdpDetails: any;

/**
* Authorization server Url
*/
authorizationServer: string;
readonly authorizationServer: string;

/**
* The OAuth clientId for the identity provider
*/
clientId: string;
readonly clientId: string;

/**
* OpenID configuration Url
*/
wellKnownEndpointUrl: string;
readonly wellKnownEndpointUrl: string;

/**
* Function referenced by the ControlPlaneAPI -- POST /users
*/
createUserFunction: IFunction;
readonly createUserFunction: IFunction;

/**
* Function referenced by the ControlPlaneAPI -- GET /users
*/
fetchAllUsersFunction: IFunction; // use 'fetch*' instead of 'get*' to avoid error JSII5000
readonly fetchAllUsersFunction: IFunction; // use 'fetch*' instead of 'get*' to avoid error JSII5000

/**
* Function referenced by the ControlPlaneAPI -- GET /user/{username}
*/
fetchUserFunction: IFunction; // use 'fetch*' instead of 'get*' to avoid error JSII5000
readonly fetchUserFunction: IFunction; // use 'fetch*' instead of 'get*' to avoid error JSII5000

/**
* Function referenced by the ControlPlaneAPI -- PUT /user/{username}
*/
updateUserFunction: IFunction;
readonly updateUserFunction: IFunction;

/**
* Function referenced by the ControlPlaneAPI -- DELETE /user/{username}
*/
deleteUserFunction: IFunction;
readonly deleteUserFunction: IFunction;

/**
* Function referenced by the ControlPlaneAPI -- PUT /user/{username}/disable
*/
disableUserFunction: IFunction;
readonly disableUserFunction: IFunction;

/**
* Function referenced by the ControlPlaneAPI -- PUT /user/{username}/enable
*/
enableUserFunction: IFunction;
readonly enableUserFunction: IFunction;
}
95 changes: 83 additions & 12 deletions src/control-plane/auth/cognito-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,99 @@ import { NagSuppressions } from 'cdk-nag';
import { Construct } from 'constructs';
import { IAuth } from './auth-interface';

/**
* Properties for the CognitoAuth construct.
*/
export interface CognitoAuthProps {
/**
* The name of the Identity Provider (IdP) for the control plane.
*/
readonly idpName: string;

/**
* The callback URL for the control plane. If not provided, defaults to 'http://localhost'.
* @default - 'http://localhost'
*/
readonly controlPlaneCallbackURL?: string;

/**
* The name of the system admin role.
*/
readonly systemAdminRoleName: string;

/**
* The email address of the system admin.
*/
readonly systemAdminEmail: string;
}

/**
* Constructs for setting up Cognito authentication and user management.
*/
export class CognitoAuth extends Construct implements IAuth {
authorizer: IAuthorizer;
controlPlaneIdpDetails: any;
authorizationServer: string;
clientId: string;
wellKnownEndpointUrl: string;
createUserFunction: IFunction;
fetchAllUsersFunction: IFunction;
fetchUserFunction: IFunction;
updateUserFunction: IFunction;
deleteUserFunction: IFunction;
disableUserFunction: IFunction;
enableUserFunction: IFunction;
/**
* The API Gateway authorizer for authenticating requests.
*/
public readonly authorizer: IAuthorizer;

/**
* The details of the control plane Identity Provider (IdP).
*/
public readonly controlPlaneIdpDetails: any;

/**
* The authorization server for the control plane IdP.
*/
public readonly authorizationServer: string;

/**
* The client ID for the control plane IdP.
*/
public readonly clientId: string;

/**
* The well-known endpoint URL for the control plane IdP.
*/
public readonly wellKnownEndpointUrl: string;

/**
* The Lambda function for creating a user.
*/
public readonly createUserFunction: IFunction;

/**
* The Lambda function for fetching all users.
*/
public readonly fetchAllUsersFunction: IFunction;

/**
* The Lambda function for fetching a user.
*/
public readonly fetchUserFunction: IFunction;

/**
* The Lambda function for updating a user.
*/
public readonly updateUserFunction: IFunction;

/**
* The Lambda function for deleting a user.
*/
public readonly deleteUserFunction: IFunction;

/**
* The Lambda function for disabling a user.
*/
public readonly disableUserFunction: IFunction;

/**
* The Lambda function for enabling a user.
*/
public readonly enableUserFunction: IFunction;

constructor(scope: Construct, id: string, props: CognitoAuthProps) {
super(scope, id);

const defaultControlPlaneCallbackURL = 'http://localhost';

// https://docs.powertools.aws.dev/lambda/python/2.31.0/#lambda-layer
Expand Down Expand Up @@ -145,6 +215,7 @@ export class CognitoAuth extends Construct implements IAuth {
value: this.controlPlaneIdpDetails,
key: 'ControlPlaneIdpDetails',
});

const customAuthorizerFunction = new PythonFunction(this, 'CustomAuthorizerFunction', {
entry: path.join(__dirname, '../../../resources/functions/authorizer'),
runtime: Runtime.PYTHON_3_12,
Expand Down
60 changes: 54 additions & 6 deletions src/control-plane/billing/billing-interface.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,82 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { Schedule } from 'aws-cdk-lib/aws-events';
import { IFunction } from 'aws-cdk-lib/aws-lambda';
import { DetailType } from '../../utils';
import { IDataIngestorAggregator } from '../ingestor-aggregator/ingestor-aggregator-interface';

/**
* Optional interface that allows specifying both
* the function to trigger and the event that will trigger it.
*/
export interface IFunctionTrigger {
/**
* The function definition.
*/
readonly handler: IFunction;

/**
* The detail-type that will trigger the handler function.
*/
readonly trigger: DetailType;
}

/**
* Optional interface that allows specifying both
* the function to trigger and the schedule by which to trigger it.
*/
export interface IFunctionSchedule {
/**
* The function definition.
*/
readonly handler: IFunction;

/**
* The schedule that will trigger the handler function.
*/
readonly schedule: Schedule;
}

/**
* Encapsulates the list of properties for an IBilling construct.
*/
export interface IBilling {
/**
* The function to trigger when creating a new billing user.
* The function to trigger to create a new customer.
* (Customer in this context is an entity that has zero or more Users.)
*/
createCustomerFunction: IFunction | IFunctionTrigger;

/**
* The function to trigger to delete a customer.
* (Customer in this context is an entity that has zero or more Users.)
*/
deleteCustomerFunction: IFunction | IFunctionTrigger;

/**
* The function to trigger to create a new user.
* (User in this context is an entity that belongs to a Customer.)
*/
createUserFunction: IFunction;
createUserFunction?: IFunction | IFunctionTrigger;

/**
* The function to trigger when deleting a billing user.
* The function to trigger to delete a user.
* (User in this context is an entity that belongs to a Customer.)
*/
deleteUserFunction: IFunction;
deleteUserFunction?: IFunction | IFunctionTrigger;

/**
* The IDataIngestorAggregator responsible for accepting and aggregating
* the raw billing data.
*/
ingestor: IDataIngestorAggregator;
ingestor?: IDataIngestorAggregator;

/**
* The function responsible for taking the aggregated data and pushing
* that to the billing provider.
*/
putUsageFunction: IFunction;
putUsageFunction?: IFunction | IFunctionSchedule;

/**
* The function to trigger when a webhook request is received.
Expand Down
Loading

0 comments on commit 4d325b3

Please sign in to comment.