gatefold
Version:
The compact URL shortener. Developed at Cimpress.
770 lines (768 loc) • 26.4 kB
JSON
{
"swagger": "2.0",
"info": {
"description": "A simple service that provide URL shortening capabilities through the $GATEFOLD_DOMAIN domain. \n\n A longer URL can be shortened to the following form: https://$GATEFOLD_DOMAIN/[uniqueId]. When accessed, this short URL will automatically redirect to the original using a standard HTTP 302 response with a properly set Location header.\n\nHappy shortening!",
"version": "$GATEFOLD_VERSION",
"title": "Gatefold"
},
"host": "$GATEFOLD_DOMAIN",
"schemes": [
"https"
],
"paths": {
"/": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Request a short URL within the $GATEFOLD_DOMAIN domain.",
"parameters": [
{
"in": "body",
"name": "PostRequest",
"required": true,
"schema": {
"$ref": "#/definitions/PostRequest"
}
}
],
"responses": {
"201": {
"description": "Shortened version created successfully",
"schema": {
"$ref": "#/definitions/PostPutResponse"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Location": {
"type": "string"
}
}
},
"500": {
"description": "Internal server error",
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
},
"503": {
"description": "Service temporarily unavailable",
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-request-validator": "Validate body",
"x-amazon-apigateway-integration": {
"credentials": { "Fn::GetAtt": [ "GatefoldAPIRole", "Arn" ] },
"uri": "arn:aws:apigateway:eu-west-1:dynamodb:action/UpdateItem",
"responses": {
"4\\d{2}": {
"statusCode": "503",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
},
"5\\d{2}": {
"statusCode": "500",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
},
"2\\d{2}": {
"statusCode": "201",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'",
"method.response.header.Location": "integration.response.body.Attributes.ShortUrl.S"
},
"responseTemplates": {
"application/json": "#set($short = $input.json(\"$.Attributes.ShortUrl.S\").replace('\"', \"\"))\n{\n \"longUrl\": $input.json(\"$.Attributes.LongUrl.S\"),\n \"shortUrl\": \"$short\",\n \"token\": $input.json(\"$.Attributes.Token.S\")\n}"
}
}
},
"passthroughBehavior": "never",
"httpMethod": "POST",
"requestTemplates": {
"application/json": "#set ($guid = $context.requestId)\n#set ($suffix1 = $guid.substring(0,4))\n#set ($suffix2 = $guid.substring(24,28))\n#set ($short = \"https://$GATEFOLD_DOMAIN/$suffix1$suffix2\")\n#set ($token = $guid.substring(0))\n#set ($curTime = $context.requestTimeEpoch / 1000)\n#set ($ttl = $curTime + 86400 * $GATEFOLD_TTL)\n{\n \"TableName\": \"gatefold-$GATEFOLD_DOMAIN\",\n \"Key\": {\n \"ShortUrl\": {\n \"S\": \"$short\"\n }\n },\n \"ExpressionAttributeNames\": {\n \"#Token\": \"Token\"\n },\n \"ExpressionAttributeValues\": {\n \":longUrl\": {\n \"S\": $input.json(\"$.longUrl\")\n },\n \":token\": {\n \"S\": \"$token\"\n },\n \":currentTime\": {\n \"N\": \"$curTime\"\n },\n \":expiresAt\": {\n \"N\": \"$ttl\"\n }\n },\n \"UpdateExpression\": \"SET LongUrl = :longUrl, #Token = :token, CreatedAt = :currentTime, ModifiedAt = :currentTime, ExpiresAt = :expiresAt\",\n \"ConditionExpression\": \"attribute_not_exists(ShortUrl)\",\n \"ReturnValues\": \"ALL_NEW\"\n}"
},
"type": "aws"
}
},
"options": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"passthroughBehavior": "when_no_match",
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"type": "mock"
}
}
},
"/swagger": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"description": "Produces the Gatefold API definition.",
"responses": {
"200": {
"description": "OpenAPI 2.0 (Swagger) Gatefold API definition",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": ""
}
}
},
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"passthroughBehavior": "when_no_match",
"type": "mock"
}
},
"options": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"passthroughBehavior": "when_no_match",
"type": "mock"
}
}
},
"/livecheck": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"description": "Provide a healthcheck interface.",
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"passthroughBehavior": "when_no_match",
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"type": "mock"
}
},
"options": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"passthroughBehavior": "when_no_match",
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"type": "mock"
}
}
},
"/not-found": {
"get": {
"consumes": [
"application/json"
],
"responses": {
"404": {
"description": "404 response",
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"description": "Returns a 404 status code.",
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "404",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"passthroughBehavior": "when_no_match",
"requestTemplates": {
"application/json": "{\"statusCode\": 404}"
},
"type": "mock"
}
},
"options": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"passthroughBehavior": "when_no_match",
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"type": "mock"
}
}
},
"/{id}": {
"get": {
"produces": [
"application/json"
],
"summary": "Retrieve the long URL associated with the id. This endpoint returns a HTTP 302 response with a Location header containing the long URL.",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"302": {
"description": "Redirection to the original URL",
"headers": {
"Location": {
"description": "Location of the original resource",
"type": "string"
},
"Access-Control-Expose-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"uri": "https://$GATEFOLD_DOMAIN/{id}/proxied",
"responses": {
"default": {
"statusCode": "302",
"responseParameters": {
"method.response.header.Location": "integration.response.body.longUrl",
"method.response.header.Access-Control-Expose-Headers": "'Location'"
},
"responseTemplates": {
"application/json": "#set($inputRoot = $input.path('$'))\n{}"
}
}
},
"requestParameters": {
"integration.request.path.id": "method.request.path.id"
},
"passthroughBehavior": "when_no_match",
"httpMethod": "GET",
"type": "http"
}
},
"put": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Updates the URL hidden behind the specified ID and refreshes the TTL of the ID, meaning that the short URL will continue to be available for a longer period of time.",
"parameters": [
{
"name": "id",
"description": "An unique ID identifying the short URL to update",
"in": "path",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "PutRequest",
"required": true,
"schema": {
"$ref": "#/definitions/PutRequest"
}
}
],
"responses": {
"200": {
"description": "Short URL successfully updated",
"schema": {
"$ref": "#/definitions/PostPutResponse"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
},
"400": {
"description": "400 response",
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
},
"500": {
"description": "500 response",
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-request-validator": "Validate body",
"x-amazon-apigateway-integration": {
"credentials": { "Fn::GetAtt": [ "GatefoldAPIRole", "Arn" ] },
"uri": "arn:aws:apigateway:eu-west-1:dynamodb:action/UpdateItem",
"responses": {
"4\\d{2}": {
"statusCode": "400",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{\n \"message\" : \"There already exists a resource with that ID and you've specified an incorrect token.\"\n}"
}
},
"5\\d{2}": {
"statusCode": "500",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
},
"2\\d{2}": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "#set($short = $input.json(\"$.Attributes.ShortUrl.S\").replace('\"', \"\"))\n{\n \"longUrl\": $input.json(\"$.Attributes.LongUrl.S\"),\n \"shortUrl\": \"$short\",\n \"token\": $input.json(\"$.Attributes.Token.S\")\n}"
}
}
},
"passthroughBehavior": "never",
"httpMethod": "POST",
"requestTemplates": {
"application/json": "#set ($suffix = $input.params(\"id\"))\n#set ($token = $input.json(\"$.token\"))\n#set ($curTime = $context.requestTimeEpoch / 1000)\n#set ($ttl = $curTime + 86400 * $GATEFOLD_TTL)\n{\n \"TableName\": \"gatefold-$GATEFOLD_DOMAIN\",\n \"Key\": {\n \"ShortUrl\": {\n \"S\": \"https://$GATEFOLD_DOMAIN/$suffix\"\n }\n },\n \"ExpressionAttributeNames\": {\n \"#Token\": \"Token\"\n },\n \"ExpressionAttributeValues\": {\n \":longUrl\": {\n \"S\": $input.json('$.longUrl')\n },\n \":token\": {\n \"S\": $token\n },\n \":currentTime\": {\n \"N\": \"$curTime\"\n },\n \":expiresAt\": {\n \"N\": \"$ttl\"\n }\n },\n \"UpdateExpression\": \"SET LongUrl = :longUrl, #Token = :token, CreatedAt = if_not_exists(CreatedAt, :currentTime), ModifiedAt = :currentTime, ExpiresAt = :expiresAt\",\n \"ConditionExpression\": \"(attribute_not_exists(ShortUrl)) or (#Token = :token)\",\n \"ReturnValues\": \"ALL_NEW\"\n}"
},
"type": "aws"
}
},
"options": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'GET,PUT,OPTIONS'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"passthroughBehavior": "when_no_match",
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"type": "mock"
}
}
},
"/{id}/proxied": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"description": "Required for applying a logical OR operator on one of the returned fields. This endpoint has no SLA and may be terminated without notice.",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "200 response"
}
},
"x-amazon-apigateway-integration": {
"credentials": { "Fn::GetAtt": [ "GatefoldAPIRole", "Arn" ] },
"uri": "arn:aws:apigateway:eu-west-1:dynamodb:action/GetItem",
"responses": {
"default": {
"statusCode": "200",
"responseTemplates": {
"application/json": "#set($inputRoot = $input.path('$'))\n#if($inputRoot.Item.LongUrl.S.isEmpty())\n #set($val = \"https://$GATEFOLD_DOMAIN/not-found\")\n#else\n #set($val = $inputRoot.Item.LongUrl.S)\n#end\n\n{\n \"longUrl\": \"$val\"\n}"
}
}
},
"passthroughBehavior": "never",
"httpMethod": "POST",
"requestTemplates": {
"application/json": "{\n \"TableName\": \"gatefold-$GATEFOLD_DOMAIN\",\n \"Key\": {\n \"ShortUrl\": {\n \"S\": \"https://$GATEFOLD_DOMAIN/$input.params(\"id\")\"\n }\n }\n}"
},
"type": "aws"
}
},
"options": {
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
},
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"credentials": { "Fn::GetAtt": [ "GatefoldAPIRole", "Arn" ] },
"uri": "arn:aws:apigateway:eu-west-1:dynamodb:action/GetItem",
"responses": {
"default": {
"statusCode": "200"
}
},
"passthroughBehavior": "never",
"httpMethod": "POST",
"type": "aws"
}
}
}
},
"definitions": {
"Empty": {
"type": "object",
"title": "Empty Schema"
},
"PostPutResponse": {
"type": "object",
"properties": {
"longUrl": {
"type": "string",
"description": "The long URL which the short URL will redirect to.",
"example": "https://www.example.org"
},
"shortUrl": {
"type": "string",
"description": "The short URL that would automatically redirect to the configured long URL.",
"example": "https://$GATEFOLD_DOMAIN/ae7d2f3e"
},
"token": {
"type": "string",
"description": "A token that can be used to update the long URL for the generated short URL at later stage.",
"example": "ae7d8ca0-8533-11e8-8eba-2f3e608cc07e"
}
},
"title": "PostPutRequest"
},
"PostRequest": {
"type": "object",
"required": [
"longUrl"
],
"properties": {
"longUrl": {
"type": "string",
"description": "The long URL to shorten",
"example": "https://www.example.org"
}
},
"title": "PostRequest"
},
"PutRequest": {
"type": "object",
"required": [
"longUrl",
"token"
],
"properties": {
"longUrl": {
"type": "string",
"description": "A (new) URL to use for the specified short URL",
"example": "https://www.example.org"
},
"token": {
"type": "string",
"description": "The token that was generated when the short URL was initially created.",
"example": "ae7d8ca0-8533-11e8-8eba-2f3e608cc07e"
}
},
"title": "PutRequest"
}
},
"x-amazon-apigateway-gateway-responses": {
"DEFAULT_4XX": {
"statusCode": "400",
"responseParameters": {
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{\"message\":$context.error.messageString}"
}
},
"DEFAULT_5XX": {
"statusCode": "500",
"responseParameters": {
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{\"message\":$context.error.messageString}"
}
}
},
"x-amazon-apigateway-request-validators": {
"Validate body": {
"validateRequestParameters": false,
"validateRequestBody": true
}
}
}