<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Youcef's blog]]></title><description><![CDATA[Youcef's blog]]></description><link>https://dev.laidani.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 21:58:08 GMT</lastBuildDate><atom:link href="https://dev.laidani.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[ChatGPT OpenAPI Swagger]]></title><description><![CDATA[Being a developer, I find Swagger to be an excellent tool for APIs. I decided to create an open-source project that lists all the ChatGPT APIs in one Swagger file for easy access.
GitHub repository
https://github.com/laidani/ChatGPT-OpenAPI-Swagger]]></description><link>https://dev.laidani.com/chatgpt-openapi-swagger</link><guid isPermaLink="true">https://dev.laidani.com/chatgpt-openapi-swagger</guid><category><![CDATA[chatgpt]]></category><category><![CDATA[swagger]]></category><category><![CDATA[api]]></category><category><![CDATA[OpenApi]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Youcef Laidani]]></dc:creator><pubDate>Sun, 05 Feb 2023 09:21:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675588751468/26e7f47b-0969-41b3-b686-ce9a2b30c45e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Being a developer, I find Swagger to be an excellent tool for APIs. I decided to create an open-source project that lists all the ChatGPT APIs in one Swagger file for easy access.</p>
<h3 id="heading-github-repository">GitHub repository</h3>
<p><a target="_blank" href="https://github.com/laidani/ChatGPT-OpenAPI-Swagger">https://github.com/laidani/ChatGPT-OpenAPI-Swagger</a></p>
]]></content:encoded></item><item><title><![CDATA[Chat with ChatGPT using AWS Lambda and Java]]></title><description><![CDATA[Are you interested in using the ChatGPT library to build a chatbot or natural language processing application?
Follow along as we walk through creating an AWS Lambda function using Java. By the end of this tutorial, you will have a fully-functioning ...]]></description><link>https://dev.laidani.com/chat-with-chatgpt-using-aws-lambda-and-java</link><guid isPermaLink="true">https://dev.laidani.com/chat-with-chatgpt-using-aws-lambda-and-java</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Java]]></category><category><![CDATA[chatgpt]]></category><dc:creator><![CDATA[Youcef Laidani]]></dc:creator><pubDate>Sun, 08 Jan 2023 12:21:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673247664146/3992ac15-2d92-4e57-a274-3a838428125f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Are you interested in using the ChatGPT library to build a chatbot or natural language processing application?</p>
<p>Follow along as we walk through creating an AWS Lambda function using Java. By the end of this tutorial, you will have a fully-functioning Java-based AWS Lambda that integrates with the ChatGPT library.</p>
<h3 id="heading-chatgpt-token">ChatGPT Token</h3>
<p>To use the ChatGPT API, you need to create a token:</p>
<ul>
<li><p>Login to <a target="_blank" href="https://openai.com/api/">https://openai.com/api/</a></p>
</li>
<li><p>And create a token</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673179388534/8ec9aa55-9526-4951-a229-b8b628e9cda0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-aws-lambda-function-code">AWS Lambda function code</h3>
<p>This article will explore how to integrate ChatGPT into your application using the openai-java library. This library, developed by TheoKanning and available on GitHub at <a target="_blank" href="https://github.com/TheoKanning/openai-java"><strong>https://github.com/TheoKanning/openai-java</strong></a>, provides a convenient way to access the ChatGPT API and take advantage of its natural language processing capabilities in your own projects.</p>
<ul>
<li><p>Create a new maven project</p>
</li>
<li><p>In <strong>pom.xml</strong> add</p>
</li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.theokanning.openai-gpt3-java<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>client<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.18.24<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<ul>
<li>Create a class <code>TalkToChatGPT.java</code></li>
</ul>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> com.theokanning.openai.OpenAiService;
<span class="hljs-keyword">import</span> com.theokanning.openai.completion.CompletionChoice;
<span class="hljs-keyword">import</span> com.theokanning.openai.completion.CompletionRequest;
<span class="hljs-keyword">import</span> com.theokanning.openai.completion.CompletionResult;
<span class="hljs-keyword">import</span> lombok.extern.slf4j.Slf4j;

<span class="hljs-keyword">import</span> java.util.List;
<span class="hljs-keyword">import</span> java.util.Optional;
<span class="hljs-keyword">import</span> java.util.stream.Collectors;

<span class="hljs-meta">@Slf4j</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TalkToChatGPT</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String MODEL = <span class="hljs-string">"text-davinci-003"</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String TOKEN = <span class="hljs-string">"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> OpenAiService OPEN_AI_SERVICE = <span class="hljs-keyword">new</span> OpenAiService(TOKEN);

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">handleRequest</span><span class="hljs-params">(Event event)</span> </span>{
        <span class="hljs-keyword">final</span> String question = event.getBody();
        <span class="hljs-keyword">return</span> getChatGPTResponse(question);
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">getChatGPTResponse</span><span class="hljs-params">(String question)</span> </span>{
        CompletionRequest completionRequest = getCompletionRequest(question);
        <span class="hljs-keyword">return</span> Optional.ofNullable(OPEN_AI_SERVICE.createCompletion(completionRequest))
                .map(CompletionResult::getChoices)
                .stream()
                .flatMap(List::stream)
                .map(CompletionChoice::getText)
                .map(<span class="hljs-keyword">this</span>::removeSpaces)
                .collect(Collectors.joining());
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> CompletionRequest <span class="hljs-title">getCompletionRequest</span><span class="hljs-params">(String question)</span> </span>{
        <span class="hljs-keyword">return</span> CompletionRequest.builder()
                .model(MODEL)
                .prompt(question)
                .maxTokens(<span class="hljs-number">1000</span>)
                .build();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">removeSpaces</span><span class="hljs-params">(String text)</span> </span>{
        <span class="hljs-keyword">return</span> text.replace(<span class="hljs-string">"\n"</span>, <span class="hljs-string">""</span>).trim();
    }
}
</code></pre>
<ul>
<li><p>You can find the complete project at this link <a target="_blank" href="https://github.com/laidani/AWS-Lambda-ChatGPT">https://github.com/laidani/AWS-Lambda-ChatGPT</a></p>
</li>
<li><p>Before we can upload and run our code on AWS Lambda, we need to create a <strong>.jar</strong> file containing all of the necessary code and dependencies. To do this, we can use the <code>mvn clean package</code> command.</p>
</li>
</ul>
<h3 id="heading-aws-lambda-function">AWS Lambda function</h3>
<h4 id="heading-create-a-new-lambda-function">Create a new Lambda function</h4>
<p>To proceed, log in to your AWS account and create a new Lambda function as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673171909957/0238af94-6b08-4e8d-aa57-d1e8f1c9a3e4.png" alt class="image--center mx-auto" /></p>
<p>To test the function, we can enable the function URL:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673246823759/14fd5071-5389-4902-b6af-c203706a0976.png" alt class="image--center mx-auto" /></p>
<p>This will allow us to send requests to the function and receive responses from it, allowing us to verify that it is working as intended.</p>
<p>To ensure that ChatGPT has enough time to generate a response, it is advisable to set the timeout for the function to a value greater than 15 seconds(default timeout of lambda function).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673172936569/bc31034d-367d-48f6-9da7-42efb6397ce1.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-upload-the-jar-file-to-the-lambda-function">Upload the .jar file to the Lambda function</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673173150558/1033f9ad-20e3-4016-b233-3d51f59f7588.png" alt class="image--center mx-auto" /></p>
<blockquote>
<h4 id="heading-ensure-that-the-handler-field-in-your-lambda-function-points-to-the-correct-java-function-by-specifying-the-fully-qualified-class-name-followed-by-and-the-function-name-for-example-packagednamehttppackagednameclassnamefunctionname"><strong>Ensure that the Handler field in your Lambda function points to the correct Java function by specifying the fully qualified class name followed by</strong> <code>::</code> <strong>and the function name, for example:</strong> <a target="_blank" href="http://packaged.name"><code>packaged.name</code></a><code>.ClassName::functionName</code><strong>.</strong></h4>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673173216419/131286ec-3dd6-482a-884c-201a427743aa.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-test-lambda-function">Test Lambda function</h3>
<p>To test the functionality of our AWS Lambda function, we can use the following curl command:</p>
<pre><code class="lang-bash">$ curl --location --request POST <span class="hljs-string">'https://luqp5lgsrvcdxu5dxf3aa67iee0spbbm.lambda-url.eu-west-1.on.aws/'</span> \
--header <span class="hljs-string">'Content-Type: text/plain'</span> \
--data-raw <span class="hljs-string">'How many planets are in the Solar System?'</span>

$ There are 8 planets <span class="hljs-keyword">in</span> the Solar System: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune.
</code></pre>
<p>Or by using postman:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673176796036/3cb49a34-f932-4b02-b659-5638a80059f5.png" alt class="image--center mx-auto" /></p>
<p>Enjoy ;)</p>
]]></content:encoded></item><item><title><![CDATA[Create an Audio Transcript with Amazon Transcribe, API Gateway, Lambda, S3 and Terraform]]></title><description><![CDATA[Based on this AWS tutorial "Create an Audio Transcript with Amazon Transcribe" we will create all the processes using AWS API Gateway, Lambda, Amazon Transcribe and S3, all with Terraform.
API Gateway endpoints
As the transcription service can take a...]]></description><link>https://dev.laidani.com/create-an-audio-transcript-with-amazon-transcribe-api-gateway-lambda-and-terraform</link><guid isPermaLink="true">https://dev.laidani.com/create-an-audio-transcript-with-amazon-transcribe-api-gateway-lambda-and-terraform</guid><category><![CDATA[aws-translate]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[aws-apigateway]]></category><dc:creator><![CDATA[Youcef Laidani]]></dc:creator><pubDate>Sun, 01 Jan 2023 19:46:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672602215195/85a46c83-aade-42d0-86a9-97cb2d360592.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Based on this AWS tutorial "<a target="_blank" href="https://aws.amazon.com/getting-started/hands-on/create-audio-transcript-transcribe/"><strong>Create an Audio Transcript with Amazon Transcribe</strong></a><strong>"</strong> we will create all the processes using AWS API Gateway, Lambda, Amazon Transcribe and S3, all with Terraform.</p>
<h3 id="heading-api-gateway-endpoints">API Gateway endpoints</h3>
<p>As the transcription service can take a couple of seconds to extract the speech from the audio, we need two endpoints:</p>
<ol>
<li><p>The first one is a POST endpoint that triggers a lambda which will upload the audio file to an S3 bucket and trigger a transcript job, it looks like this <a target="_blank" href="https://xxxxxxx.execute-api.eu-west-1.amazonaws.com/dev/upload">https://xxxxxxx.execute-api.eu-west-1.amazonaws.com/dev</a>/<a target="_blank" href="https://90x7wo15o3.execute-api.eu-west-1.amazonaws.com/dev/upload">upload</a></p>
</li>
<li><p>The second one is a GET endpoint to get the result of transcription, it looks like this <a target="_blank" href="https://90x7wo15o3.execute-api.eu-west-1.amazonaws.com/dev/transcription?code=f55c863e-60b2-4f64-9ed2-bd4b0afe0168.mp3">https://</a><a target="_blank" href="https://xxxxxxx.execute-api.eu-west-1.amazonaws.com/dev/transcription?code=f55c863e-60b2-4f64-9ed2-bd4b0afe0168.mp3">xxxxxxx.execute-api.eu-west-1.amazonaws.com/dev/transcription?code=f55c863e-60b2-4f64-9ed2-bd4b0afe0168.mp3</a></p>
</li>
</ol>
<p>The terraform code of these two endpoints:</p>
<pre><code class="lang-bash">resource <span class="hljs-string">"aws_apigatewayv2_api"</span> <span class="hljs-string">"http_api"</span> {
  name          = <span class="hljs-string">"transcribe_api"</span>
  protocol_type = <span class="hljs-string">"HTTP"</span>
  description   = <span class="hljs-string">"HTTP API to send audio files to Lambda"</span>

  cors_configuration {
    allow_credentials = <span class="hljs-literal">false</span>
    allow_headers     = []
    allow_methods     = [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"POST"</span>]
    allow_origins     = [<span class="hljs-string">"*"</span>]
    expose_headers    = []
    max_age           = 0
  }
}

<span class="hljs-comment"># Upload audio endpoint</span>
resource <span class="hljs-string">"aws_apigatewayv2_integration"</span> <span class="hljs-string">"api_upload"</span> {
  api_id = aws_apigatewayv2_api.http_api.id

  integration_uri  = aws_lambda_function.upload_audit_lambda.invoke_arn
  integration_type = <span class="hljs-string">"AWS_PROXY"</span>
}

resource <span class="hljs-string">"aws_apigatewayv2_route"</span> <span class="hljs-string">"api_upload"</span> {
  api_id    = aws_apigatewayv2_api.http_api.id
  route_key = <span class="hljs-string">"POST /upload"</span>
  target    = <span class="hljs-string">"integrations/<span class="hljs-variable">${aws_apigatewayv2_integration.api_upload.id}</span>"</span>
}

<span class="hljs-comment"># Get transcription endpoint</span>
resource <span class="hljs-string">"aws_apigatewayv2_integration"</span> <span class="hljs-string">"api_transcription"</span> {
  api_id = aws_apigatewayv2_api.http_api.id

  integration_uri  = aws_lambda_function.get_transcription_lambda.invoke_arn
  integration_type = <span class="hljs-string">"AWS_PROXY"</span>
}

resource <span class="hljs-string">"aws_apigatewayv2_route"</span> <span class="hljs-string">"api_transcription"</span> {
  api_id    = aws_apigatewayv2_api.http_api.id
  route_key = <span class="hljs-string">"GET /transcription"</span>
  target    = <span class="hljs-string">"integrations/<span class="hljs-variable">${aws_apigatewayv2_integration.api_transcription.id}</span>"</span>
}

<span class="hljs-comment"># Stage</span>
resource <span class="hljs-string">"aws_apigatewayv2_stage"</span> <span class="hljs-string">"api_stage_dev"</span> {
  api_id      = aws_apigatewayv2_api.http_api.id
  name        = <span class="hljs-string">"dev"</span>
  auto_deploy = <span class="hljs-literal">true</span>

  depends_on = [aws_apigatewayv2_integration.api_upload]
}
</code></pre>
<h3 id="heading-s3-bucket">S3 bucket</h3>
<pre><code class="lang-bash">resource <span class="hljs-string">"aws_s3_bucket"</span> <span class="hljs-string">"transcript_bucket"</span> {
  bucket_prefix = <span class="hljs-string">"transcript-bucket-"</span>
  force_destroy = <span class="hljs-literal">true</span>
}
</code></pre>
<h3 id="heading-lambda-to-upload-audio">Lambda to upload Audio</h3>
<h4 id="heading-python-code">Python code</h4>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> base64
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> uuid

s3_client = boto3.client(<span class="hljs-string">'s3'</span>)
transcribe = boto3.client(<span class="hljs-string">'transcribe'</span>)
bucket_name = os.environ[<span class="hljs-string">'S3_BUCKET_NAME'</span>]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    body = event[<span class="hljs-string">'body'</span>]
    body_bytes = bytes(body, <span class="hljs-string">'utf-8'</span>)

    body_base64 = base64.b64decode(body_bytes)

    name = str(uuid.uuid4()) + <span class="hljs-string">'.mp3'</span>
    s3_client.put_object(Bucket=bucket_name, Body=body_base64, Key=name)
    start_transcript_job(name)

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">"headers"</span>: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
        },
        <span class="hljs-string">"body"</span>: json.dumps({
            <span class="hljs-string">"code "</span>: name
        })
    }


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start_transcript_job</span>(<span class="hljs-params">key</span>):</span>
    job_name = key
    job_uri = <span class="hljs-string">f"s3://<span class="hljs-subst">{bucket_name}</span>/<span class="hljs-subst">{key}</span>"</span>
    transcribe.start_transcription_job(
        TranscriptionJobName=job_name,
        Media={<span class="hljs-string">'MediaFileUri'</span>: job_uri},
        MediaFormat=<span class="hljs-string">'mp3'</span>,
        LanguageCode=<span class="hljs-string">'en-US'</span>
    )
</code></pre>
<p>This lambda will be triggered by the first POST endpoint mentioned before, it will then:</p>
<ol>
<li><p>Extract the audio file from the body of the request <code>event['body']</code>.</p>
</li>
<li><p>Upload the file to S3 with a UUID, for example <code>0fc6f6de-0634-4a97-9734-b005f5cb1595.mp3</code></p>
</li>
<li><p>Start a new transcript job</p>
</li>
<li><p>and finally, return the name of the user so that it can be used to get the transcription with another endpoint.</p>
</li>
</ol>
<h4 id="heading-terraform">Terraform</h4>
<pre><code class="lang-bash"><span class="hljs-comment"># Lambda</span>
resource <span class="hljs-string">"aws_lambda_permission"</span> <span class="hljs-string">"upload_audit_lambda_permission"</span> {
  statement_id  = <span class="hljs-string">"AllowExecutionFromAPIGateway"</span>
  action        = <span class="hljs-string">"lambda:InvokeFunction"</span>
  function_name = aws_lambda_function.upload_audit_lambda.function_name
  principal     = <span class="hljs-string">"apigateway.amazonaws.com"</span>

  source_arn = <span class="hljs-string">"<span class="hljs-variable">${aws_apigatewayv2_api.http_api.execution_arn}</span>/*"</span>
}

resource <span class="hljs-string">"aws_lambda_function"</span> <span class="hljs-string">"upload_audit_lambda"</span> {
  filename         = local.upload_audio_lambda_file_name
  function_name    = local.upload_audio_function_name
  role             = aws_iam_role.upload_audio_role.arn
  handler          = <span class="hljs-string">"lambda_upload_audio.lambda_handler"</span>
  runtime          = <span class="hljs-string">"python3.8"</span>
  timeout          = 25
  source_code_hash = filebase64sha256(local.upload_audio_lambda_file_name)

  environment {
    variables = {
      S3_BUCKET_NAME = aws_s3_bucket.transcript_bucket.bucket
    }
  }
}

resource <span class="hljs-string">"aws_iam_role"</span> <span class="hljs-string">"upload_audio_role"</span> {
  name = <span class="hljs-string">"<span class="hljs-variable">${local.upload_audio_function_name}</span>_role"</span>

  assume_role_policy = &lt;&lt;POLICY
{
  <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-string">"Statement"</span>: [
    {
      <span class="hljs-string">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>,
      <span class="hljs-string">"Principal"</span>: {
        <span class="hljs-string">"Service"</span>: <span class="hljs-string">"lambda.amazonaws.com"</span>
      },
      <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-string">"Sid"</span>: <span class="hljs-string">""</span>
    }
  ]
}
POLICY
}

resource <span class="hljs-string">"aws_iam_policy"</span> <span class="hljs-string">"upload_audit_lambda_policy"</span> {
  name        = <span class="hljs-string">"<span class="hljs-variable">${local.upload_audio_function_name}</span>-policy"</span>
  description = <span class="hljs-string">"Policy for <span class="hljs-variable">${local.upload_audio_function_name}</span>"</span>

  policy = jsonencode({
    <span class="hljs-string">"Version"</span> : <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-string">"Statement"</span> : [
      {
        <span class="hljs-string">"Effect"</span> : <span class="hljs-string">"Allow"</span>,
        <span class="hljs-string">"Action"</span> : [
          <span class="hljs-string">"logs:CreateLogStream"</span>,
          <span class="hljs-string">"logs:PutLogEvents"</span>,
          <span class="hljs-string">"logs:CreateLogGroup"</span>
        ],
        <span class="hljs-string">"Resource"</span> : <span class="hljs-string">"arn:aws:logs:*:*:*"</span>
      },
      {
        <span class="hljs-string">"Effect"</span> : <span class="hljs-string">"Allow"</span>,
        <span class="hljs-string">"Action"</span> : [<span class="hljs-string">"s3:*"</span>],
        <span class="hljs-string">"Resource"</span> : <span class="hljs-string">"<span class="hljs-variable">${aws_s3_bucket.transcript_bucket.arn}</span>/*"</span>
      },
      {
        <span class="hljs-string">"Action"</span> : [<span class="hljs-string">"transcribe:*"</span>],
        <span class="hljs-string">"Resource"</span> : <span class="hljs-string">"*"</span>,
        <span class="hljs-string">"Effect"</span> : <span class="hljs-string">"Allow"</span>
      }
    ]
  })
}

resource <span class="hljs-string">"aws_iam_role_policy_attachment"</span> <span class="hljs-string">"upload_audio_role_policy_attachment"</span> {
  role       = aws_iam_role.upload_audio_role.name
  policy_arn = aws_iam_policy.upload_audit_lambda_policy.arn
}

locals {
  upload_audio_function_name    = <span class="hljs-string">"Upload_Audio_Lambda"</span>
  upload_audio_lambda_file_name = <span class="hljs-string">"lambda_upload_audio.zip"</span>
}
</code></pre>
<h3 id="heading-lambda-to-get-the-transcription">Lambda to get the transcription</h3>
<h4 id="heading-python-code-1">Python code</h4>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> urllib.request

transcribe = boto3.client(<span class="hljs-string">'transcribe'</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    job_name = event[<span class="hljs-string">'queryStringParameters'</span>][<span class="hljs-string">'code'</span>]
    transcript = get_transcription(job_name)

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">"headers"</span>: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/html"</span>
        },
        <span class="hljs-string">"body"</span>: transcript
    }

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_transcription</span>(<span class="hljs-params">job_name</span>):</span>
    status = transcribe.get_transcription_job(TranscriptionJobName=job_name)
    <span class="hljs-keyword">if</span> status[<span class="hljs-string">'TranscriptionJob'</span>][<span class="hljs-string">'TranscriptionJobStatus'</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'COMPLETED'</span>, <span class="hljs-string">'FAILED'</span>]:
        s3_url = status[<span class="hljs-string">'TranscriptionJob'</span>][<span class="hljs-string">'Transcript'</span>][<span class="hljs-string">'TranscriptFileUri'</span>]
        <span class="hljs-keyword">with</span> urllib.request.urlopen(s3_url) <span class="hljs-keyword">as</span> url:
            response = url.read()
            json_response = json.loads(response.decode(<span class="hljs-string">'utf-8'</span>))
            <span class="hljs-keyword">return</span> json_response[<span class="hljs-string">'results'</span>][<span class="hljs-string">'transcripts'</span>][<span class="hljs-number">0</span>][<span class="hljs-string">'transcript'</span>]
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Code not exist, or transcription result not ready.'</span>
</code></pre>
<p>This lambda will be triggered by the second GET endpoint mentioned before, it will then:</p>
<ol>
<li><p>Extract the job code, for example <code>f55c863e-60b2-4f64-9ed2-bd4b0afe0168.mp3</code></p>
</li>
<li><p>Make a <code>get_transcription_job</code>, if the status is COMPLETE it simply returns the transcription result in the body.</p>
</li>
</ol>
<h4 id="heading-terraform-1">Terraform</h4>
<pre><code class="lang-bash"><span class="hljs-comment"># Lambda</span>
resource <span class="hljs-string">"aws_lambda_permission"</span> <span class="hljs-string">"get_transcription_lambda_permission"</span> {
  statement_id  = <span class="hljs-string">"AllowExecutionFromAPIGateway"</span>
  action        = <span class="hljs-string">"lambda:InvokeFunction"</span>
  function_name = aws_lambda_function.get_transcription_lambda.function_name
  principal     = <span class="hljs-string">"apigateway.amazonaws.com"</span>

  source_arn = <span class="hljs-string">"<span class="hljs-variable">${aws_apigatewayv2_api.http_api.execution_arn}</span>/*"</span>
}

resource <span class="hljs-string">"aws_lambda_function"</span> <span class="hljs-string">"get_transcription_lambda"</span> {
  filename         = local.transcription_lambda_file_name
  function_name    = local.transcription_function_name
  role             = aws_iam_role.upload_audio_role.arn
  handler          = <span class="hljs-string">"lambda_get_transcription.lambda_handler"</span>
  runtime          = <span class="hljs-string">"python3.8"</span>
  timeout          = 25
  source_code_hash = filebase64sha256(local.transcription_lambda_file_name)
}

resource <span class="hljs-string">"aws_iam_role"</span> <span class="hljs-string">"transcription_role"</span> {
  name = <span class="hljs-string">"<span class="hljs-variable">${local.transcription_function_name}</span>_role"</span>

  assume_role_policy = &lt;&lt;POLICY
{
  <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-string">"Statement"</span>: [
    {
      <span class="hljs-string">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>,
      <span class="hljs-string">"Principal"</span>: {
        <span class="hljs-string">"Service"</span>: <span class="hljs-string">"lambda.amazonaws.com"</span>
      },
      <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-string">"Sid"</span>: <span class="hljs-string">""</span>
    }
  ]
}
POLICY
}

resource <span class="hljs-string">"aws_iam_policy"</span> <span class="hljs-string">"transcription_lambda_policy"</span> {
  name        = <span class="hljs-string">"<span class="hljs-variable">${local.transcription_function_name}</span>-policy"</span>
  description = <span class="hljs-string">"Policy for <span class="hljs-variable">${local.upload_audio_function_name}</span>"</span>

  policy = jsonencode({
    <span class="hljs-string">"Version"</span> : <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-string">"Statement"</span> : [
      {
        <span class="hljs-string">"Effect"</span> : <span class="hljs-string">"Allow"</span>,
        <span class="hljs-string">"Action"</span> : [
          <span class="hljs-string">"logs:CreateLogStream"</span>,
          <span class="hljs-string">"logs:PutLogEvents"</span>,
          <span class="hljs-string">"logs:CreateLogGroup"</span>
        ],
        <span class="hljs-string">"Resource"</span> : <span class="hljs-string">"arn:aws:logs:*:*:*"</span>
      },
      {
        <span class="hljs-string">"Action"</span> : [<span class="hljs-string">"transcribe:*"</span>],
        <span class="hljs-string">"Resource"</span> : <span class="hljs-string">"*"</span>,
        <span class="hljs-string">"Effect"</span> : <span class="hljs-string">"Allow"</span>
      }
    ]
  })
}

resource <span class="hljs-string">"aws_iam_role_policy_attachment"</span> <span class="hljs-string">"transcription_role_policy_attachment"</span> {
  role       = aws_iam_role.transcription_role.name
  policy_arn = aws_iam_policy.transcription_lambda_policy.arn
}

locals {
  transcription_function_name    = <span class="hljs-string">"Get_Transcription_Lambda"</span>
  transcription_lambda_file_name = <span class="hljs-string">"lambda_get_transcription.zip"</span>
}
</code></pre>
<h3 id="heading-give-it-a-try">Give it a try</h3>
<p>To run all this, just follow these steps:</p>
<pre><code class="lang-bash">$ git <span class="hljs-built_in">clone</span> https://github.com/laidani/Create-an-Audio-Transcript-with-Amazon-Transcribe-API-Gateway-Lambda-and-Terraform
$ <span class="hljs-built_in">cd</span> Create-an-Audio-Transcript-with-Amazon-Transcribe-API-Gateway-Lambda-and-Terraform

$ terraform init
$ terraform apply --auto-approve
</code></pre>
<p>In the end, you will get the API URL</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672600455890/5d91f786-b9f9-44d1-9ae6-f877f74f5402.png" alt class="image--center mx-auto" /></p>
<p>Now you can use postman for example to upload an audio file:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672600434273/e7f96b76-ca4b-4d3c-a31a-6a4d4c0506ae.png" alt class="image--center mx-auto" /></p>
<p>And in a few seconds, you can call the GET transcription endpoint</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672600488612/3e72a25f-0ef1-42fe-b88e-496ee5fc4da8.png" alt class="image--center mx-auto" /></p>
<p>To destroy everything you just execute this command:</p>
<p><code>terraform apply --destroy --auto-approve</code></p>
<h3 id="heading-github-repository">GitHub repository</h3>
<p>You can find all the code written in this tutorial at <a target="_blank" href="https://github.com/laidani/Create-an-Audio-Transcript-with-Amazon-Transcribe-API-Gateway-Lambda-and-Terraform">https://github.com/laidani/Create-an-Audio-Transcript-with-Amazon-Transcribe-API-Gateway-Lambda-and-Terraform</a></p>
<p>Enjoy :)</p>
]]></content:encoded></item><item><title><![CDATA[Create a notification system using AWS Lambda, SNS, and Event Bridge]]></title><description><![CDATA[Introduction
In this article, we will create a process to get a notification if the price of silver or gold is below x price. This example can be used with any service, for example, to check airline prices or dates, amazon product prices, appointment...]]></description><link>https://dev.laidani.com/aws-sample-using-lambda-sns-eventbridge</link><guid isPermaLink="true">https://dev.laidani.com/aws-sample-using-lambda-sns-eventbridge</guid><category><![CDATA[AWS]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Youcef Laidani]]></dc:creator><pubDate>Sun, 16 Oct 2022 09:06:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665943598202/Zg7wJ7NgI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In this article, we will create a process to get a notification if the price of silver or gold is below x price. This example can be used with any service, for example, to check airline prices or dates, amazon product prices, appointments, etc...</p>
<h1 id="heading-architecture">Architecture</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665907710576/oPJr2MyIa.png" alt="Architecture.png" /></p>
<p>The idea here is to start a Lambda using EventBridge, this Lambda will make an HTTP query, in this example, we use an endpoint from <a target="_blank" href="https://data-asg.goldprice.org/dbXRates/EUR">goldprice.org</a>. If the price of gold is below 1500 EUR, the lambda will publish a notification in an SNS topic, and the topic will send an email, SMS, or both.</p>
<h1 id="heading-lambda">Lambda</h1>
<p>The lambda is made using NodeJS, to create this you need to install <a target="_blank" href="https://nodejs.org/en/download/">NodeJS</a>, then create a project using:</p>
<pre><code class="lang-bash">npm init
</code></pre>
<p>and follow the instructions.</p>
<h2 id="heading-lambda-code">Lambda code</h2>
<p>After the project is created, create a file index.js then copy and paste the following code:</p>
<pre><code class="lang-bash">const request = require(<span class="hljs-string">'request-promise'</span>);
const aws = require(<span class="hljs-string">"aws-sdk"</span>);
const sns = new aws.SNS();

const priceAlert = 1500;

const options = {
    <span class="hljs-string">'method'</span>: <span class="hljs-string">'GET'</span>,
    <span class="hljs-string">'url'</span>: <span class="hljs-string">'https://data-asg.goldprice.org/dbXRates/EUR'</span>,
    resolveWithFullResponse: <span class="hljs-literal">true</span>
};

<span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">check_gold_price</span></span>() {
    <span class="hljs-built_in">return</span> request(options);
}

<span class="hljs-keyword">function</span> send_message(price) {
    const snsParams = {
        TopicArn: <span class="hljs-string">"arn:aws:sns:&lt;Region&gt;:&lt;AccountId&gt;:&lt;TopicName&gt;"</span>,
        Subject: <span class="hljs-string">"Gold price"</span>,
        Message: `Hey, the price of gold is <span class="hljs-variable">${price}</span>`
    };
    <span class="hljs-built_in">let</span> response = sns.publish(snsParams).promise();

    response.then(<span class="hljs-function"><span class="hljs-title">function</span></span> () {
        console.log(<span class="hljs-string">'Message has been sent'</span>);
    }).catch(<span class="hljs-keyword">function</span> (err) {
        console.error(err, err.stack);
    });
}

exports.handler = () =&gt; {
    check_gold_price().<span class="hljs-keyword">then</span>(response =&gt; {
        <span class="hljs-keyword">if</span> (response.statusCode === 200 &amp;&amp; response.body) {
            <span class="hljs-built_in">let</span> json = JSON.parse(response.body);
            <span class="hljs-built_in">let</span> price = json.items[0]?.xauPrice;
            <span class="hljs-keyword">if</span> (price &lt; priceAlert) {
                send_message(price);
            }
        }
    });
};
</code></pre>
<p>To install the missing modules, execute this command.</p>
<pre><code class="lang-bash">npm install
</code></pre>
<h2 id="heading-lambda-package">Lambda package</h2>
<p>there are many ways to deploy your code to Lambda, in this article we will use the simple one, we will zip our code and upload it to the lambda.</p>
<p>As you have seen in the code of our Lambda, we use <a target="_blank" href="https://aws.amazon.com/sdk-for-javascript/">aws-sdk</a>, this module is really big(&gt;80MB), and we don't need it to be packaged with the other modules, because the Lambda runtime has already this module. to remove this module we can just execute:</p>
<pre><code class="lang-bash">npm uninstall aws-sdk
</code></pre>
<p>We need to zip the code like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665861537992/3-IF7q4Nn.png" alt="zip.png" /></p>
<h2 id="heading-create-aws-lambda-and-upload-the-zip-file">Create AWS Lambda and upload the zip file</h2>
<blockquote>
<p>I assume you already have an AWS account, if not you can create one here https://aws.amazon.com/</p>
</blockquote>
<p>In this article, we will create a Lambda from the AWS console, to do this:</p>
<ul>
<li><p>Login to your AWS Account</p>
</li>
<li><p>Search Lambda</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665861895442/cmsze7I9e.png" alt="search lambda.png" /></p>
<ul>
<li><p>Select Functions, and then click Create function button</p>
</li>
<li><p>Then fill in the information, and in the end click Create function button</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665862070630/PwqeiXVSg.png" alt="create new AWS Lambda.png" /></p>
<ul>
<li>Upload the zip file</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665862239361/Y7z0x_20U.png" alt="Upload zip file.png" /></p>
<p>The Lambda will look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665862403513/TOztnh9jm.png" alt="Lambda code.png" /></p>
<h1 id="heading-create-sns-topic">Create SNS Topic</h1>
<p>To create an SNS Topic:</p>
<ul>
<li><p>Search SNS in the AWS console</p>
</li>
<li><p>Select Simple Notification Service</p>
</li>
<li><p>Select Topics and then click Create Topic button</p>
</li>
<li><p>Choose a Standard topic and give a name to the topic like this</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665862977126/ha3kTDeEL.png" alt="TopicSNS.png" /></p>
<ul>
<li>Click at the end Create topic button</li>
</ul>
<h2 id="heading-sns-subscriptions">SNS Subscriptions</h2>
<p>In this step, we will add an email to test the process, to do this:</p>
<ul>
<li>Amazon SNS &gt; Subscriptions &gt; Create subscription</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665909248307/A5Y6jpkP5.png" alt="Add SNS Email Subscription.png" /></p>
<p>In this example, we add only an email, you can add a phone number, and many other services to receive notifications.</p>
<p>Select the ARN of the topic and put it in TopicArn like this:</p>
<pre><code class="lang-bash">    const snsParams = {
        TopicArn: <span class="hljs-string">"arn:aws:sns:eu-west-1:1222331122:MyTopic"</span>,
        Subject: <span class="hljs-string">"Gold price"</span>,
        Message: `Hey, the price of gold is <span class="hljs-variable">${price}</span>`
    };
</code></pre>
<blockquote>
<p>Note: this is not the recommended way, and it is really bad in some cases, to do things better, you must use Environment variables, by Secret Manager, or KMS, it depends on your variables.</p>
</blockquote>
<p>When you test the Lambda you will get this error:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665863471793/712F7XrCX.png" alt="Error.png" /></p>
<p>To fix this error, you have to give your Lambda the right to publish SNS notifications, to do this:</p>
<ul>
<li>Select the Lambda's role from Lambda &gt; Configuration &gt; Permissions</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665863649238/mi4XdXUzk.png" alt="Change Lambda role.png" /></p>
<ul>
<li>Create a new Policy that allows only sns:Publish and link it to this role</li>
</ul>
<pre><code class="lang-bash">{
    <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-string">"Statement"</span>: [
        {
            <span class="hljs-string">"Sid"</span>: <span class="hljs-string">"VisualEditor0"</span>,
            <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-string">"Action"</span>: <span class="hljs-string">"sns:Publish"</span>,
            <span class="hljs-string">"Resource"</span>: <span class="hljs-string">"arn:aws:sns:eu-west-1:1222331122:MyTopic"</span>
        }
    ]
}
</code></pre>
<p>Test again, and everything will work correctly, and you will receive an email and SMS that looks like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665907878189/LdAGgNtIm.png" alt="Email.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665907946064/wiV9eKrZh.png" alt="SMS.png" /></p>
<blockquote>
<p>Note: you need also to give the Lambda enough timeout, you can configure this on Lambda &gt; Configuration &gt; General configuration &gt; Edit &gt; and change the Timeout to 30sec for example.</p>
</blockquote>
<h1 id="heading-eventbridge">EventBridge</h1>
<p>Until now everything is good, we will just configure the EventBridge to launch the Lambda each period of time, to configure this:</p>
<ul>
<li><p>Search EventBrdidge in the AWS console</p>
</li>
<li><p>Select Amazon EventBridge</p>
</li>
<li><p>Select Rules then click Create rule button</p>
</li>
<li><p>Give a name to this rule and choose the Schedule rule type and click the next button</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665908503995/865_q0dza.png" alt="EventBridge1.png" /></p>
<ul>
<li><p>Choose "A schedule that runs at a regular rate, such as every 10 minutes" as the schedule pattern</p>
</li>
<li><p>For the "Rate expression" for test purposes we use 5min and click the next button</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665908657637/nsPUqAMO2.png" alt="EventBridge2.png" /></p>
<ul>
<li>For "Target" select Lambda, and then choose the name of Lambda that we have created</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665908778548/4qXFRrhfL.png" alt="EventBridge3.png" /></p>
<ul>
<li>Click next &gt; next &gt; Create rule in the end</li>
</ul>
<p>In this stage, the Lambda will be executed each 5min, if the price is below 1500 EUR, a notification will be sent via SNS.</p>
<p>That's it.</p>
<h1 id="heading-terraform">Terraform</h1>
<p>You found the terraform for this sample here: <a target="_blank" href="https://github.com/laidani/Notification-system-using-AWS-lambda-SNS-EventBridge-and-Terraform">https://github.com/laidani/Notification-system-using-AWS-lambda-SNS-EventBridge-and-Terraform</a></p>
]]></content:encoded></item></channel></rss>