From 3e3e8d3c970bf1b4d9a4118b817b9580a828fff1 Mon Sep 17 00:00:00 2001 From: ea-mtenhoor <42071768+ea-mtenhoor@users.noreply.github.com> Date: Mon, 2 Jun 2025 16:08:24 -0500 Subject: [PATCH] swagger updates --- templates/1-StartingBlocks-Main-Template.yml | 7 +++- templates/x-lambda-core-functions.yml | 26 +++++++++++- templates/x-swagger.yml | 42 ++++++++++++++++++-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/templates/1-StartingBlocks-Main-Template.yml b/templates/1-StartingBlocks-Main-Template.yml index 8704a1c..7ca0712 100644 --- a/templates/1-StartingBlocks-Main-Template.yml +++ b/templates/1-StartingBlocks-Main-Template.yml @@ -530,6 +530,7 @@ Resources: SNSTopicArn: !Ref SNSTopicArn SlackWebhookUrl: !Ref SlackWebhookUrl KmsKeyId: !GetAtt 'SharedResourcesStack.Outputs.KeyId' + DeploySwagger: !Ref 'DeploySwagger' DatabaseStack: Type: AWS::CloudFormation::Stack @@ -725,12 +726,16 @@ Resources: HostedZoneId: !Ref 'HostedZoneId' S3SourceBucket: !Ref 'S3SourceBucket' DomainName: !Ref 'DomainName' - SwaggerDefaultURL: !Sub 'https://swaggerui.${DomainName}/metadata/data/v3/resources/swagger.json' + SwaggerDefaultURL: !If + - UseDefaultWebApiZip + - !Sub 'https://api.ed-fi.org/v${EdFiApiVersion}/api/metadata/data/v3/resources/swagger.json' + - '' CRHelperLambdaLayer: !GetAtt 'LambdaCoreStack.Outputs.CRHelperLambdaLayer' Partner: !Ref 'Partner' LambdaDefaultSGID: !GetAtt 'SharedResourcesStack.Outputs.LambdaDefaultSGID' PrivateSubnet1Id: !Ref 'PrivateSubnet1Id' PrivateSubnet2Id: !Ref 'PrivateSubnet2Id' + KmsKeyId: !GetAtt 'SharedResourcesStack.Outputs.KeyId' StateMachineStack: Type: AWS::CloudFormation::Stack diff --git a/templates/x-lambda-core-functions.yml b/templates/x-lambda-core-functions.yml index 8cd8dd5..52198a1 100755 --- a/templates/x-lambda-core-functions.yml +++ b/templates/x-lambda-core-functions.yml @@ -92,6 +92,9 @@ Parameters: KmsKeyId: Description: The KMS Key ID used to encrypt resources in this deployment. Type: String + DeploySwagger: + Type: String + Description: If enabled, Swagger UI will be deployed to swagger-ui.{DomainName} Conditions: UseAdminApi: !Equals [!Ref 'AdminInterface', "Ed-Fi Admin API"] @@ -728,6 +731,11 @@ Resources: - kms:Decrypt Resource: - !Sub 'arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${KmsKeyId}' + - Effect: Allow + Action: + - lambda:InvokeFunction + Resource: + - !Sub 'arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${EnvLabel}-SwaggerUpdate' TenantManagementFunction: Type: AWS::Lambda::Function @@ -753,6 +761,7 @@ Resources: Variables: ENVLABEL: !Ref EnvLabel TENANCY_MODE: !Ref EdFiTenancyMode + DEPLOY_SWAGGER: !Ref 'DeploySwagger' Code: ZipFile: | import json @@ -1055,7 +1064,13 @@ Resources: ) return response['Command']['Status'] - + + def update_swagger(env_label): + lambda_client = boto3.client('lambda') + lambda_response = lambda_client.invoke( + FunctionName=f'{env_label}-SwaggerUpdate', + InvocationType='Event' + ) def lambda_handler(event, context): conn = None @@ -1069,6 +1084,7 @@ Resources: env_label = os.environ['ENVLABEL'] secret_name = env_label + '-AuroraMasterSecret' + deploy_swagger = os.environ['DEPLOY_SWAGGER'] # Concatenate "-tenants" to env_label for the DynamoDB table name table_name = env_label + '-tenants' @@ -1110,6 +1126,10 @@ Resources: # Add item to DynamoDB response = add_item(table, tenant_name, allowed_ed_orgs) + # Update Swagger if needed + if deploy_swagger == 'True': + update_swagger(env_label) + print("Item added successfully, and template databases cloned") return "Item added successfully, and template databases cloned" elif action == 'Remove': @@ -1131,6 +1151,10 @@ Resources: # Delete tenant-specific databases delete_tenant_databases(conn, tenant_name) + + # Update Swagger if needed + if deploy_swagger == 'True': + update_swagger(env_label) print("Item removed successfully, and tenant-specific databases deleted") return "Item removed successfully, and tenant-specific databases deleted" diff --git a/templates/x-swagger.yml b/templates/x-swagger.yml index 1f4f258..773f9e2 100644 --- a/templates/x-swagger.yml +++ b/templates/x-swagger.yml @@ -37,6 +37,9 @@ Parameters: PrivateSubnet2Id: Type: AWS::EC2::Subnet::Id Description: ID of the private subnet 2 in Availability Zone 2 (e.g., subnet-a0246dcd) + KmsKeyId: + Description: The KMS Key ID used to encrypt dynamodb. + Type: String Resources: S3SwaggerFEBucket: Type: AWS::S3::Bucket @@ -388,6 +391,16 @@ Resources: Resource: - !Sub 'arn:${AWS::Partition}:s3:::${S3SwaggerFEBucket}' - !Sub 'arn:${AWS::Partition}:s3:::${S3SwaggerFEBucket}/*' + - Effect: Allow + Action: + - dynamodb:Scan + Resource: + - !Sub 'arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${EnvLabel}-tenants*' + - Effect: Allow + Action: + - kms:Decrypt + Resource: + - !Sub 'arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${KmsKeyId}*' Roles: - !Ref 'SwaggerLambdaRole' SwaggerLambdaAWSCLILayer: @@ -417,6 +430,7 @@ Resources: - !Ref SwaggerLambdaAWSCLILayer - !Ref 'CRHelperLambdaLayer' ReservedConcurrentExecutions: 1 + FunctionName: !Sub '${EnvLabel}-SwaggerUpdate' Description: Updates and deploys Swagger UI in S3 Handler: index.lambda_handler Environment: @@ -424,6 +438,8 @@ Resources: S3_BUCKET: !Ref S3SwaggerFEBucket CF_DISTRIBUTION: !Ref CloudFront DEFAULT_URL: !Ref SwaggerDefaultURL + DOMAIN_NAME: !Ref DomainName + ENV_LABEL: !Ref EnvLabel Runtime: python3.11 MemorySize: 128 Role: !GetAtt 'SwaggerLambdaRole.Arn' @@ -444,11 +460,15 @@ Resources: import boto3 from zipfile import ZipFile from crhelper import CfnResource - + from datetime import datetime + helper = CfnResource() def lambda_handler(event, context): - helper(event, context) + if "StackId" in event: + helper(event, context) + else: + create(event, context) @helper.create @helper.update @@ -457,7 +477,23 @@ Resources: the_bucket = os.environ['S3_BUCKET'] cf_dist = os.environ['CF_DISTRIBUTION'] swagger_url = os.environ['DEFAULT_URL'] + domain_name = os.environ['DOMAIN_NAME'] + env_label = os.environ['ENV_LABEL'] http = urllib3.PoolManager() + + # Check dynamodb for tenants and replace swagger_url if one exists + ddb_table_name = env_label + '-tenants' + ddb = boto3.resource('dynamodb') + ddb_table = ddb.Table(ddb_table_name) + ddb_response = ddb_table.scan() + ddb_data = ddb_response['Items'] + # Handle pagination for large tables. + while ddb_response.get('LastEvaluatedKey'): + ddb_response = ddb_table.scan(ExclusiveStartKey=ddb_response['LastEvaluatedKey']) + ddb_data.extend(ddb_response['Items']) + if len(ddb_data) > 0: + tenant = ddb_data[0]['Name'] + swagger_url = f'https://swaggerui.{domain_name}/{tenant}/metadata/data/v3/resources/swagger.json' print(swagger_url) url = 'https://github.com/swagger-api/swagger-ui/releases/latest' @@ -513,7 +549,7 @@ Resources: '/*', ] }, - 'CallerReference': v + 'CallerReference': str(datetime.now()) } )