33 |

38 |
Your Image Is Ready!
39 |
Hello,
40 |
41 | We're excited to let you know that your image has been processed and is
42 | now attached to this email.
43 |
44 |
45 |
Please check the attachment to view it.
46 |
47 |
48 | Made with ❤️ by
49 | Lambda Forge
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/docs/examples/infra/services/dynamodb.py:
--------------------------------------------------------------------------------
1 | from aws_cdk import aws_dynamodb as dynamodb
2 | from aws_cdk import aws_lambda as lambda_
3 | from aws_cdk import aws_lambda_event_sources as event_source
4 |
5 | from lambda_forge.trackers import invoke, trigger
6 |
7 |
8 | class DynamoDB:
9 | def __init__(self, scope, context) -> None:
10 |
11 | self.numbers_table = dynamodb.Table.from_table_arn(
12 | scope,
13 | "NumbersTable",
14 | context.resources["arns"]["numbers_table"],
15 | )
16 |
17 | self.urls_table = dynamodb.Table.from_table_arn(
18 | scope,
19 | "UrlsTable",
20 | context.resources["arns"]["urls_table"],
21 | )
22 |
23 | self.auth_table = dynamodb.Table.from_table_arn(
24 | scope,
25 | "AuthTable",
26 | context.resources["arns"]["auth_table"],
27 | )
28 |
29 | self.books_table = dynamodb.Table.from_table_arn(
30 | scope,
31 | "BooksTable",
32 | "arn:aws:dynamodb:us-east-2:211125768252:table/Books",
33 | )
34 |
35 | self.posts_table = dynamodb.Table.from_table_arn(
36 | scope,
37 | "PostsTable",
38 | "arn:aws:dynamodb:us-east-2:211125768252:table/Dev-Blog-Posts",
39 | )
40 |
41 | @trigger(service="dynamodb", trigger="table", function="function")
42 | def create_trigger(self, table: str, function: lambda_.Function) -> None:
43 | table_instance = getattr(self, table)
44 | dynamo_event_stream = event_source.DynamoEventSource(
45 | table_instance, starting_position=lambda_.StartingPosition.TRIM_HORIZON
46 | )
47 | function.add_event_source(dynamo_event_stream)
48 |
49 | @invoke(service="dynamodb", resource="table", function="function")
50 | def grant_write(self, table: str, function: lambda_.Function) -> None:
51 | table_instance = getattr(self, table)
52 | table_instance.grant_write_data(function)
53 |
--------------------------------------------------------------------------------
/deploy.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import shutil
4 | import subprocess
5 |
6 |
7 | def read_version():
8 | """Reads the version from setup.py."""
9 | with open("setup.py", "r") as file:
10 | content = file.read()
11 | version_match = re.search(r"version=['\"]([^'\"]+)['\"]", content)
12 | if version_match:
13 | return version_match[1]
14 | return None
15 |
16 |
17 | def increment_version(version):
18 | """Increments the patch number in the version."""
19 | major, minor, patch = map(int, version.split("."))
20 | return f"{major}.{minor}.{patch + 1}"
21 |
22 |
23 | def update_setup_py(new_version):
24 | """Updates the setup.py file with the new version."""
25 | with open("setup.py", "r") as file:
26 | content = file.read()
27 | content = re.sub(
28 | r"(version=['\"])([^'\"]+)(['\"])", rf"\g<1>{new_version}\3", content
29 | )
30 | with open("setup.py", "w") as file:
31 | file.write(content)
32 |
33 |
34 | def build_and_upload():
35 | # Check if the dist directory exists and remove it
36 | dist_path = "dist"
37 | if os.path.exists(dist_path) and os.path.isdir(dist_path):
38 | shutil.rmtree(dist_path)
39 | print(f"Removed existing {dist_path} directory.")
40 |
41 | """Builds the package and uploads it to TestPyPI."""
42 | subprocess.run(["python", "setup.py", "sdist", "bdist_wheel"], check=True)
43 | subprocess.run(["twine", "upload", "--repository", "pypi", "dist/*"], check=True)
44 |
45 |
46 | def main():
47 | current_version = read_version()
48 | if current_version is None:
49 | raise Exception("Could not read the current version from setup.py.")
50 | new_version = increment_version(current_version)
51 | print(f"Updating version: {current_version} -> {new_version}")
52 | update_setup_py(new_version)
53 | print("Building and uploading the package to TestPyPI...")
54 | build_and_upload()
55 | print("Done.")
56 |
57 |
58 | if __name__ == "__main__":
59 | main()
60 |
--------------------------------------------------------------------------------
/docs/demo/infra/stacks/prod_stack.py:
--------------------------------------------------------------------------------
1 | import aws_cdk as cdk
2 | from aws_cdk import aws_codebuild as codebuild
3 | from aws_cdk import pipelines
4 | from aws_cdk.pipelines import CodePipelineSource
5 | from constructs import Construct
6 | from infra.stages.deploy import DeployStage
7 |
8 | from lambda_forge.constants import ECR
9 | from lambda_forge.context import context
10 | from lambda_forge.steps import CodeBuildSteps
11 |
12 |
13 | @context(stage="Prod", resources="prod")
14 | class ProdStack(cdk.Stack):
15 | def __init__(self, scope: Construct, context, **kwargs) -> None:
16 | super().__init__(scope, context.create_id("Stack"), **kwargs)
17 |
18 | source = CodePipelineSource.git_hub(
19 | f"{context.repo['owner']}/{context.repo['name']}", "main"
20 | )
21 |
22 | pipeline = pipelines.CodePipeline(
23 | self,
24 | "Pipeline",
25 | pipeline_name=context.create_id("Pipeline"),
26 | synth=pipelines.ShellStep("Synth", input=source, commands=["cdk synth"]),
27 | code_build_defaults=pipelines.CodeBuildOptions(
28 | build_environment=codebuild.BuildEnvironment(
29 | build_image=codebuild.LinuxBuildImage.from_docker_registry(
30 | ECR.LATEST
31 | ),
32 | )
33 | ),
34 | )
35 |
36 | steps = CodeBuildSteps(self, context, source=source)
37 |
38 | # pre
39 | unit_tests = steps.unit_tests()
40 | integration_tests = steps.integration_tests()
41 |
42 | # post
43 | diagram = steps.diagram()
44 | redoc = steps.redoc()
45 | swagger = steps.swagger()
46 |
47 | pipeline.add_stage(
48 | DeployStage(self, context),
49 | pre=[
50 | unit_tests,
51 | integration_tests,
52 | ],
53 | post=[
54 | diagram,
55 | redoc,
56 | swagger,
57 | ],
58 | )
59 |
--------------------------------------------------------------------------------
/lambda_forge/logs/tui/api/_test_data.py:
--------------------------------------------------------------------------------
1 | log_groups = [
2 | ("/aws/lambda/Live-Telegram-AskQuestion", "Live Telegram AskQuestion"),
3 | ("/aws/lambda/Live-Whatsapp-AskQuestion", "Live WhatsApp AskQuestion"),
4 | ("/aws/lambda/Live-Instagram-AskQuestion", "Live Instagram AskQuestion"),
5 | ("/aws/lambda/Live-Telegram-AskQuestion-2", "Live Telegram AskQuestion 2"),
6 | ("/aws/lambda/Live-Whatsapp-AskQuestion-2", "Live WhatsApp AskQuestion 2"),
7 | ]
8 |
9 | cloudwatch_logs = [
10 | {
11 | "timestamp": "1723345218900",
12 | "message": "INIT_START Runtime Version: python:3.9.v55 Runtime Version ARN: arn:aws:lambda:us-east-2::runtime:be9e7121d3264b1e86158b38dbbb656c23dff979eb481793ee37b9e2b79fda22",
13 | },
14 | {
15 | "timestamp": "1723345219013",
16 | "message": "START RequestId: eb5ea470-b2c6-4739-9c47-cc770c36fea6 Version: $LATEST",
17 | },
18 | {
19 | "timestamp": "1723345220026",
20 | "message": "END RequestId: eb5ea470-b2c6-4739-9c47-cc770c36fea6",
21 | },
22 | {
23 | "timestamp": "1723345220026",
24 | "message": "REPORT RequestId: eb5ea470-b2c6-4739-9c47-cc770c36fea6 Duration: 1013.31 ms Billed Duration: 1014 ms Memory Size: 128 MB Max Memory Used: 46 MB Init Duration: 111.94 ms",
25 | },
26 | {
27 | "timestamp": "1723345237339",
28 | "message": "START RequestId: a719c94f-0532-4394-b47d-28595a7b3105 Version: $LATEST",
29 | },
30 | {
31 | "timestamp": "1723345238004",
32 | "message": "END RequestId: a719c94f-0532-4394-b47d-28595a7b3105",
33 | },
34 | {
35 | "timestamp": "1723345238004",
36 | "message": "REPORT RequestId: a719c94f-0532-4394-b47d-28595a7b3105 Duration: 665.52 ms Billed Duration: 666 ms Memory Size: 128 MB Max Memory Used: 47 MB",
37 | },
38 | {
39 | "timestamp": "1723345616747",
40 | "message": "[ERROR] 2024-08-11T03:06:56.747Z a719c94f-0532-4394-b47d-28595a7b3105 Disconnect timed out",
41 | },
42 | ] * 2
43 |
--------------------------------------------------------------------------------