Search code examples
amazon-web-servicesaws-api-gatewayvelocityjsonpath

Map the context.requestTimeEpoch to a custom date format string in AWS API Gateway mapping template


I'm trying to set up an API Gateway with a single resource method (POST) that simply accepts JSON and writes it to an S3 bucket.

I've gotten the flow working (the JSON in the POST body is written to an S3 object) after asking this question and getting help with dynamically determining an object key for the S3 object. The answer to that question gives me a unique object-key for each object by using the context.requestTimeEpoch and context.requestId variables that are available in the mapping template.

However, this results in a flat structure. All the objects (files) are in a single directory and have unwieldy names.

I was hoping that I would somehow be able to convert the requestTimeEpoch value from milliseconds to a date string (yyyy/MM/dd/hh_mm_ss_sss) and use that instead of using the millisecond value directly in the object key.

But no matter how hard I look in the documentation for AWS API Gateway mapping templates, VLT or JSONPath, I'm unable to find any information on how I would be able to achieve this.

Is this something that I should be able to do or is chasing after this a lost cause?


Solution

  • Here is one way to do it. Still using the requestTime for most of it, and only using the requestTimeEpoch for the milliseconds since they aren't present in the requestTime variable.

    In the Integration Request I needed to break out the date parts because trying to set them via the mapping template would always convert "/" to "%2f". URL Decoding them didn't help.

    Set your Action path like this:

    [your_s3_bucket]/{year}/{month}/{day}/{time}_{rid}.json
    

    Still in the Integration Request, set up your URL Path Parameters to match:

    NAME  : MAPPED FROM
    time  : context.requestTimeEpoch         
    rid   : context.requestId        
    year  : ''       
    month : ''       
    day   : ''
    

    Now for the mapping template:

    ## The context.RequestTime variable looks like this: "26/Nov/2022:13:27:01 +0000"
    ## Split the string and pull out just the first part (date)
    #set($all_parts = $context.requestTime.split(':'))
    ## We end up with this array:
    ##   [0] = 26/Nov/2022
    ##   [1] = 13
    ##   [2] = 27
    ##   [3] = 01 +0000
    
    ## Break out date part into day/month/year
    #set($date_parts = $all_parts[0].split('/'))
    #set($day = $date_parts[0])
    #set($month_name = $date_parts[1])
    #set($year = $date_parts[2])
    
    ## Set an array to convert month names to month numbers
    #set($months = {'Jan':'01', 'Feb':'02', 'Mar':'03', 'Apr':'04', 'May':'05', 'June':'06', 'Jul':'07', 'Aug':'08', 'Sep':'09', 'Oct':'10', 'Nov':'11', 'Dec':'12'})
    #set($month = $months.get($month_name))
    
    ## Break up the time part into hours/mins/seconds
    #set($hours = $all_parts[1])
    #set($minutes = $all_parts[2])
    #set($seconds = $all_parts[3].split(' ')[0]) #remove the timezone part
    
    ## Get milliseconds from timestamp (last 3 digits)
    #set($timestamp = $context.requestTimeEpoch)
    #set($milliseconds = $timestamp % 1000)
    #set($time = "${hours}_${minutes}_${seconds}_${milliseconds}")
    
    ## Update the path to use the new variables
    #set($context.requestOverride.path.year = $year)
    #set($context.requestOverride.path.month = $month)
    #set($context.requestOverride.path.day = $day)
    #set($context.requestOverride.path.time = $time)
    

    In my test, it produced the following path that for S3:

    [s3_bucket_name]/2022/11/26/15_17_58_868_21710d19-892a-4d67-8518-ddba7fd9566f.json