

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# AWS AppSync 解析程式映射範本程式設計指南
<a name="resolver-mapping-template-reference-programming-guide"></a>

**注意**  
我們現在主要支援 APPSYNC\$1JS 執行期及其文件。請考慮[在此處](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)使用 APPSYNC\$1JS 執行期及其指南。

這是使用 Apache Velocity 範本語言 (VTL) 進行程式設計的技術指南樣式教學 AWS AppSync課程。如果您熟悉其他程式設計語言 (例如 JavaScript、C 或 Java)，此做法應相當直覺。

AWS AppSync 使用 VTL 將來自用戶端的 GraphQL 請求轉譯為對資料來源的請求。然後，它會反轉程序，來將資料來源轉換回 GraphQL 回應。VTL 是一種邏輯範本語言，可讓您使用下列技術，在 Web 應用程式的標準請求/回應流程中同時操作請求和回應：
+ 新項目的預設值
+ 輸入驗證和格式化
+ 轉換與打造資料
+ 逐一查看清單、映射和陣列以移出或更改值
+ 根據使用者身分篩選條件/變更回應
+ 複雜的授權檢查

例如，您可能想要在 GraphQL 引數的服務中執行電話號碼驗證，或先將輸入參數轉換為大寫，再將其儲存在 DynamoDB 中。或者，您希望用戶端系統能以 GraphQL 引數、JWT 字符或 HTTP 標頭的形式提供程式碼，並在程式碼符合清單中的特定字串時才回應資料。這些都是您可以使用 VTL in AWS AppSync 執行的邏輯檢查。

VTL 可讓您使用熟悉的程式設計技巧，來套用邏輯。不過，其受到限制，必須在標準請求/回應流程內執行，以確保 GraphQL API 可隨著您的使用者成長而擴展。Because AWS AppSync 也支援 AWS Lambda 做為解析程式，如果您需要更多彈性，您可以使用您選擇的程式設計語言 (Node.js、Python、Go、Java 等） 撰寫 Lambda 函數。

## 設定
<a name="setup"></a>

學習語言的一項常用技巧是將結果列印出來 (例如，在 JavaScript 中的 `console.log(variable)`)，以查看發生的情況。在本教學中，我們透過建立簡單的 GraphQL 結構描述並將對應值傳遞到 Lambda 函式來示範此技巧。Lambda 函式會列印出值，然後加以回應。這可讓您了解請求/回應流程，並查看不同的程式設計技巧。

開始建立以下 GraphQL 結構描述：

```
type Query {
    get(id: ID, meta: String): Thing
}

type Thing {
    id: ID!
    title: String!
    meta: String
}

schema {
    query: Query
}
```

現在使用 Node.js 做為語言，建立下列 AWS Lambda 函數：

```
exports.handler = (event, context, callback) => {
    console.log('VTL details: ', event);
    callback(null, event);
};
```

在 AWS AppSync 主控台的**資料來源**窗格中，將此 Lambda 函數新增為新的資料來源。導覽回主控台的**結構描述**頁面，然後按一下`get(...):Thing`查詢旁邊的右側 **ATTACH** 按鈕。如需請求範本，從 **Invoke and forward arguments (叫用和轉送引數)** 功能表中選擇現有的範本。如需回應範本，請選擇 **Return Lambda result (傳回 Lambda 結果)**。

在一個位置開啟 Lambda 函數的 Amazon CloudWatch Logs，並從 AWS AppSync 主控台的**查詢**索引標籤執行下列 GraphQL 查詢：

```
query test {
  get(id:123 meta:"testing"){
    id
    meta
  }
}
```

GraphQL 回應應包含 `id:123` 和 `meta:testing`，因為 Lambda 函式會參考這兩者。幾秒鐘後，您也應該會在 CloudWatch Logs 中看到包含這些詳細資訊的記錄。

## Variables
<a name="variables"></a>

VTL 使用[參考](https://velocity.apache.org/engine/1.7/user-guide.html#references)，供您用來存放或操作資料。在 VTL 中有三種類型的參考：變數、屬性和方法。變數的前面會有 `$` 符號且這些變數的建立是透過 `#set` 指令：

```
#set($var = "a string")
```

變數會使用其他語言存放您所熟悉的類似類型，例如數字、字串、陣列、清單和映射。您可能已經注意到傳送至預設請求範本以供 Lambda 解析程式使用的 JSON 承載：

```
"payload": $util.toJson($context.arguments)
```

這裡要注意的幾件事 - 首先， AWS AppSync 為常見操作提供數種便利功能。在這個範例中，`$util.toJson` 會將變數轉換為 JSON。其次，會自動從 GraphQL 請求填入變數 `$context.arguments` 做為映射物件。您可以建立新的映射，如下所示：

```
#set( $myMap = {
  "id": $context.arguments.id,
  "meta": "stuff",
  "upperMeta" : $context.arguments.meta.toUpperCase()
} )
```

現在，您可以建立名為 `$myMap` 的變數，其擁有 `id`、`meta` 以及 `upperMeta` 的金鑰。這也展現了以下幾點：
+  從 GraphQL 引數將金鑰填入 `id`。在 VTL 中從用戶端擷取引數是很常見的做法。
+  是以值來將 `meta` 硬式編碼，以展現預設值。
+  `upperMeta` 會使用方法 `meta` 來轉換 `.toUpperCase()` 引數。

將之前的程式碼放在請求範本的頂部並變更 `payload` 以使用新的 `$myMap` 變數：

```
"payload": $util.toJson($myMap)
```

執行您的 Lambda 函式，您可以查看回應變更與在 CloudWatch 日誌中的此資料。在您逐步完成本教學的其餘部分，我們將保持填入 `$myMap` 如此您就可以執行類似的測試。

您也可以對變數設定 *properties\$1*。它們可以是簡單的字串、陣列或 JSON：

```
#set($myMap.myProperty = "ABC")
#set($myMap.arrProperty = ["Write", "Some", "GraphQL"])
#set($myMap.jsonProperty = {
    "AppSync" : "Offline and Realtime",
    "Cognito" : "AuthN and AuthZ"
})
```

### 靜音參考
<a name="quiet-references"></a>

由於 VTL 是一種範本化語言，在預設情況下，您給予的每個參考將會執行 `.toString()`。如果參考是未定義的，它會列印實際參考呈現做為字串。例如：

```
#set($myValue = 5)
##Prints '5'
$myValue

##Prints '$somethingelse'
$somethingelse
```

為了解決這個問題，VTL 有*安靜參考*或*無提示參考*語法，可告訴範本引擎抑制這種行為。此語法是 `$!{}`。例如，如果我們稍微變更之前的程式碼以使用 `$!{somethingelse}`，系統會抑制列印：

```
#set($myValue = 5)
##Prints '5'
$myValue

##Nothing prints out
$!{somethingelse}
```

## 呼叫方法
<a name="calling-methods"></a>

在之前的範例中，我們示範了如何建立變數並同時設定值。您也可以如下所示將資料新增到映射，以兩個步驟執行此操作：

```
#set ($myMap = {})
#set ($myList = [])

##Nothing prints out
$!{myMap.put("id", "first value")}
##Prints "first value"
$!{myMap.put("id", "another value")}
##Prints true
$!{myList.add("something")}
```

 **然而**，有一些需了解這種行為的原因。雖然安靜參考表示法可讓您 `$!{}` 如以上方式呼叫方法，它將不會抑制執行方法的傳回值。這也是為什麼我們會說明以上的 `##Prints "first value"` 及 `##Prints true`。您逐一查看映射或清單可能會導致錯誤，例如插入索引鍵已存在的值，因為輸出會在評估時將未預期的字串加入範本中。

此問題的解決方法有時會使用 `#set` 指令來呼叫方法並忽略變數。例如：

```
#set ($myMap = {})
#set($discard = $myMap.put("id", "first value"))
```

您可以在範本中使用此技術，因為它可防止在範本中列印出非預期的字串。 AWS AppSync 提供替代的便利函數，可在更簡潔的表示法中提供相同的行為。這可讓您不需要考慮這些實作特性。您可以透過 `$util.quiet()` 或其別名 `$util.qr()` 存取此函式。例如：

```
#set ($myMap = {})
#set ($myList = [])

##Nothing prints out
$util.quiet($myMap.put("id", "first value"))
##Nothing prints out
$util.qr($myList.add("something"))
```

## Strings
<a name="strings"></a>

隨著使用多種程式設計語言，字串可能會變得難以處理，尤其是當您想要透過變數建構字串時。有一些與 VTL 相關的常見事件。

假設您要將資料作為字串插入 DynamoDB 之類的資料來源，但會從變數填入，例如 GraphQL 引數。字串會有雙引號，並在您只需要的字串中參考變數 `"${}"`（因此在[安靜參考表示法](https://velocity.apache.org/engine/1.7/user-guide.html#quiet-reference-notation)中不會`!`有 )。這與 JavaScript 中的範本常值類似：[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template\$1literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) 

```
#set($firstname = "Jeff")
$!{myMap.put("Firstname", "${firstname}")}
```

您可以在 DynamoDB 請求範本中看到此問題，例如使用來自 GraphQL 用戶端的引數`"author": { "S" : "${context.arguments.author}"}`時，或自動產生 ID 時，例如 `"id" : { "S" : "$util.autoId()"}`。這表示您可以參考變數或字串內部的方法結果來填入資料。

您也可以使用 Java [字串類別](https://docs.oracle.com/javase/6/docs/api/java/lang/String.html)的公有方法 (例如取出子字串)：

```
#set($bigstring = "This is a long string, I want to pull out everything after the comma")
#set ($comma = $bigstring.indexOf(','))
#set ($comma = $comma +2)
#set ($substring = $bigstring.substring($comma))

$util.qr($myMap.put("substring", "${substring}"))
```

字串連接也是非常常見的任務。您可以單獨使用變數參考或使用變數參考搭配靜態值來這樣做：

```
#set($s1 = "Hello")
#set($s2 = " World")

$util.qr($myMap.put("concat","$s1$s2"))
$util.qr($myMap.put("concat2","Second $s1 World"))
```

## 迴圈
<a name="loops"></a>

現在您已建立變數和呼叫方法，您可以將一些邏輯到新增到程式碼。與其他語言不同，VTL 只允許迴圈，其中反覆次數是預先決定的。在速度中沒有 `do..while`。此設計可確保評估處理一律終止，並提供 GraphQL 操作執行時的擴充界限。

迴圈是使用 `#foreach` 所建立並需要您輸入**迴圈變數**和 **iterable 物件** (例如陣列、清單、映射或集合)。`#foreach` 迴圈的典型程式設計範例，即是循環執行集合中的項目並將他們印出，因此我們的案例中，我們將這些項目移出，並將他們新增至映射：

```
#set($start = 0)
#set($end = 5)
#set($range = [$start..$end])

#foreach($i in $range)
   ##$util.qr($myMap.put($i, "abc"))
   ##$util.qr($myMap.put($i, $i.toString()+"foo")) ##Concat variable with string
   $util.qr($myMap.put($i, "${i}foo"))     ##Reference a variable in a string with "${varname}"
#end
```

此範例顯示一些考量。第一種是使用變數與範圍 `[..]` 運算子來建立 iterable 物件。然後，您可以操作的變數 `$i` 會參考每個項目。在上述範例中，您還可以查看**註解**，註解會加上雙井字號 `##` 來表示。這也展示在金鑰或值兩者中使用迴圈變數，以及使用字串的不同串連方法。

請注意，`$i` 是一個整數，因此您可以呼叫 `.toString()` 方法。若是 INT 的 GraphQL 類型，此技巧非常實用。

您也可以直接使用各種操作，例如：

```
#foreach($item in [1..5])
    ...
#end
```

## 陣列
<a name="arrays"></a>

您到目前為止已完成映射的操控，但在 VTL 中陣列也相當常見。您也可以使用陣列存取一些基本方法，例如 `.isEmpty()`、`.size()`、`.set()`、`.get()` 和 `.add()`，如下所示：

```
#set($array = [])
#set($idx = 0)

##adding elements
$util.qr($array.add("element in array"))
$util.qr($myMap.put("array", $array[$idx]))

##initialize array vals on create
#set($arr2 = [42, "a string", 21, "test"])

$util.qr($myMap.put("arr2", $arr2[$idx]))
$util.qr($myMap.put("isEmpty", $array.isEmpty()))  ##isEmpty == false
$util.qr($myMap.put("size", $array.size()))

##Get and set items in an array
$util.qr($myMap.put("set", $array.set(0, 'changing array value')))
$util.qr($myMap.put("get", $array.get(0)))
```

上一個範例使用陣列索引表示法來擷取具有 的 元素`arr2[$idx]`。您可以透過類似的方式從 Map/字典中來查詢名稱：

```
#set($result = {
    "Author" : "Nadia",
    "Topic" : "GraphQL"
})

$util.qr($myMap.put("Author", $result["Author"]))
```

在使用條件時，在回應範本中篩選來自資料來源的結果是很常見的。

## 條件式檢查
<a name="conditional-checks"></a>

`#foreach` 的舊區段說明使用邏輯來透過 VTL 轉換資料的部分範例。您也可以套用條件式檢查來評估執行時間的資料：

```
#if(!$array.isEmpty())
    $util.qr($myMap.put("ifCheck", "Array not empty"))
#else
    $util.qr($myMap.put("ifCheck", "Your array is empty"))
#end
```

上述布林值表達式 `#if()` 的檢查是精細的，但您也可以使用運算子和 `#elseif()` 來進行分支：

```
#if ($arr2.size() == 0)
    $util.qr($myMap.put("elseIfCheck", "You forgot to put anything into this array!"))
#elseif ($arr2.size() == 1)
    $util.qr($myMap.put("elseIfCheck", "Good start but please add more stuff"))
#else
    $util.qr($myMap.put("elseIfCheck", "Good job!"))
#end
```

這兩個範例示範 negation(\$1) 和 equality (==)。我們也可以使用 \$1\$1、&&、>、<、>=、<= 和 \$1=。

```
#set($T = true)
#set($F = false)

#if ($T || $F)
  $util.qr($myMap.put("OR", "TRUE"))
#end

#if ($T && $F)
  $util.qr($myMap.put("AND", "TRUE"))
#end
```

 **注意：**在條件中只有 `Boolean.FALSE` 和 `null` 會被視為 false。零 (0) 和空白字串 ("") 不等同於 false。

## 運算子
<a name="operators"></a>

程式設計語言需要運算子來執行數學動作才得以完整。有幾種簡單的入門方式：

```
#set($x = 5)
#set($y = 7)
#set($z = $x + $y)
#set($x-y = $x - $y)
#set($xy = $x * $y)
#set($xDIVy = $x / $y)
#set($xMODy = $x % $y)

$util.qr($myMap.put("z", $z))
$util.qr($myMap.put("x-y", $x-y))
$util.qr($myMap.put("x*y", $xy))
$util.qr($myMap.put("x/y", $xDIVy))
$util.qr($myMap.put("x|y", $xMODy))
```

### 同時使用迴圈和條件
<a name="loops-and-conditionals-together"></a>

這在 VTL 中轉換資料很常見 (例如在從資料來源寫入或讀取前)，以循環執行物件，然後執行檢查，再執行動作。將之前區段的一些工具進行整合，讓您有更多功能可使用。一個便利的工具是知道 `#foreach` 會自動提供您每個項目的 `.count`：

```
#foreach ($item in $arr2)
  #set($idx = "item" + $foreach.count)
  $util.qr($myMap.put($idx, $item))
#end
```

例如，如果值小於特定大小，則也許您會希望將其從映射移出。使用計數以及條件和 `#break` 陳述式可讓您執行此操作：

```
#set($hashmap = {
  "DynamoDB" : "https://aws.amazon.com/dynamodb/",
  "Amplify" : "https://github.com/aws/aws-amplify",
  "DynamoDB2" : "https://aws.amazon.com/dynamodb/",
  "Amplify2" : "https://github.com/aws/aws-amplify"
})

#foreach ($key in $hashmap.keySet())
    #if($foreach.count > 2)
    #break
  #end
    $util.qr($myMap.put($key, $hashmap.get($key)))
#end
```

先前 `#foreach` 已透過 `.keySet()` (您可以在映射上使用此項目) 來重複使用。這讓您能夠取得 `$key` 並使用 `.get($key)` 來參考該值。來自用戶端 in AWS AppSync 的 GraphQL 引數會儲存為映射。也可以透過 `.entrySet()` (您可以同時存取金鑰和值做為 Set) 來重複使用這些項目，並填入其他變數或執行複雜條件式檢查 (例如驗證或輸入的轉換)：

```
#foreach( $entry in $context.arguments.entrySet() )
#if ($entry.key == "XYZ" && $entry.value == "BAD")
    #set($myvar = "...")
  #else
    #break
  #end
#end
```

其他常見範例會自動填入預設資訊，例如同步資料時的初始物件版本 （在衝突解決中非常重要） 或授權檢查物件的預設擁有者 - Mary 建立此部落格文章，因此：

```
#set($myMap.owner ="Mary")
#set($myMap.defaultOwners = ["Admins", "Editors"])
```

## Context
<a name="context"></a>

現在您已更熟悉在具有 VTL 的解析程式中 AWS AppSync 執行邏輯檢查，請查看內容物件：

```
$util.qr($myMap.put("context", $context))
```

這包含所有資訊，您可以在 GraphQL 請求中存取這些資訊。有關詳細說明，請參閱[內容參考](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference)。

## 篩選
<a name="filtering"></a>

此教學到目前為止，會透過非常簡單的 JSON 轉換，將 Lambda 函式的所有資訊傳回到 GraphQL 查詢：

```
$util.toJson($context.result)
```

VTL 邏輯就跟您從資料來源取得回應時一樣強大，尤其是在對回應進行授權檢查時。讓我們逐步介紹一些範例。首先，嘗試如下變更回應範本：

```
#set($data = {
    "id" : "456",
    "meta" : "Valid Response"
})

$util.toJson($data)
```

無論您對 GraphQL 操作進行何種操作，系統會將硬式編碼值傳回用戶端。稍微變更此項目，以從 Lambda 回應填入 `meta` 欄位，如需了解條件時則在教學課程中以 `elseIfCheck` 值來較早設立此值：

```
#set($data = {
    "id" : "456"
})

#foreach($item in $context.result.entrySet())
    #if($item.key == "elseIfCheck")
        $util.qr($data.put("meta", $item.value))
    #end
#end

$util.toJson($data)
```

 `$context.result` 是一種映射，因此您可以使用 `entrySet()` 來對金鑰或傳回的值執行邏輯。由於 `$context.identity` 包含使用者執行 GraphQL 操作的相關資訊，如果您從資料來源傳回授權資訊，則可以根據邏輯決定是否向使用傳回全部、部分資料或不傳回資料。將您的回應範本變更為如下所示：

```
#if($context.result["id"] == 123)
    $util.toJson($context.result)
  #else
    $util.unauthorized()
#end
```

如果您執行 GraphQL 查詢，系統會以一般的方式傳回。不過，如果您將 id 引數變更 123 以外的值 (`query test { get(id:456 meta:"badrequest"){} }`)，您會收到授權失敗的訊息。

您可以在[授權使用案例](security-authorization-use-cases.md#aws-appsync-security-authorization-use-cases)小節中找到更多授權案例的範例。

### 範本範例
<a name="appendix-template-sample"></a>

若您遵循此教學至今，您可以逐步建立此範本。若您並未遵循此教學，我們已在下方提供，可讓您複製並進行測試。

 **請求範本** 

```
#set( $myMap = {
  "id": $context.arguments.id,
  "meta": "stuff",
  "upperMeta" : "$context.arguments.meta.toUpperCase()"
} )

##This is how you would do it in two steps with a "quiet reference" and you can use it for invoking methods, such as .put() to add items to a Map
#set ($myMap2 = {})
$util.qr($myMap2.put("id", "first value"))

## Properties are created with a dot notation
#set($myMap.myProperty = "ABC")
#set($myMap.arrProperty = ["Write", "Some", "GraphQL"])
#set($myMap.jsonProperty = {
    "AppSync" : "Offline and Realtime",
    "Cognito" : "AuthN and AuthZ"
})

##When you are inside a string and just have ${} without ! it means stuff inside curly braces are a reference
#set($firstname = "Jeff")
$util.qr($myMap.put("Firstname", "${firstname}"))

#set($bigstring = "This is a long string, I want to pull out everything after the comma")
#set ($comma = $bigstring.indexOf(','))
#set ($comma = $comma +2)
#set ($substring = $bigstring.substring($comma))
$util.qr($myMap.put("substring", "${substring}"))

##Classic for-each loop over N items:
#set($start = 0)
#set($end = 5)
#set($range = [$start..$end])
#foreach($i in $range)          ##Can also use range operator directly like #foreach($item in [1...5])
   ##$util.qr($myMap.put($i, "abc"))
   ##$util.qr($myMap.put($i, $i.toString()+"foo")) ##Concat variable with string
   $util.qr($myMap.put($i, "${i}foo"))     ##Reference a variable in a string with "${varname)"
#end

##Operators don't work
#set($x = 5)
#set($y = 7)
#set($z = $x + $y)
#set($x-y = $x - $y)
#set($xy = $x * $y)
#set($xDIVy = $x / $y)
#set($xMODy = $x % $y)
$util.qr($myMap.put("z", $z))
$util.qr($myMap.put("x-y", $x-y))
$util.qr($myMap.put("x*y", $xy))
$util.qr($myMap.put("x/y", $xDIVy))
$util.qr($myMap.put("x|y", $xMODy))

##arrays
#set($array = ["first"])
#set($idx = 0)
$util.qr($myMap.put("array", $array[$idx]))
##initialize array vals on create
#set($arr2 = [42, "a string", 21, "test"])
$util.qr($myMap.put("arr2", $arr2[$idx]))
$util.qr($myMap.put("isEmpty", $array.isEmpty()))  ##Returns false
$util.qr($myMap.put("size", $array.size()))
##Get and set items in an array
$util.qr($myMap.put("set", $array.set(0, 'changing array value')))
$util.qr($myMap.put("get", $array.get(0)))

##Lookup by name from a Map/dictionary in a similar way:
#set($result = {
    "Author" : "Nadia",
    "Topic" : "GraphQL"
})
$util.qr($myMap.put("Author", $result["Author"]))


##Conditional examples
#if(!$array.isEmpty())
$util.qr($myMap.put("ifCheck", "Array not empty"))
#else
$util.qr($myMap.put("ifCheck", "Your array is empty"))
#end

#if ($arr2.size() == 0)
$util.qr($myMap.put("elseIfCheck", "You forgot to put anything into this array!"))
#elseif ($arr2.size() == 1)
$util.qr($myMap.put("elseIfCheck", "Good start but please add more stuff"))
#else
$util.qr($myMap.put("elseIfCheck", "Good job!"))
#end

##Above showed negation(!) and equality (==), we can also use OR, AND, >, <, >=, <=, and !=
#set($T = true)
#set($F = false)
#if ($T || $F)
  $util.qr($myMap.put("OR", "TRUE"))
#end

#if ($T && $F)
  $util.qr($myMap.put("AND", "TRUE"))
#end

##Using the foreach loop counter - $foreach.count
#foreach ($item in $arr2)
  #set($idx = "item" + $foreach.count)
  $util.qr($myMap.put($idx, $item))
#end

##Using a Map and plucking out keys/vals
#set($hashmap = {
    "DynamoDB" : "https://aws.amazon.com/dynamodb/",
    "Amplify" : "https://github.com/aws/aws-amplify",
    "DynamoDB2" : "https://aws.amazon.com/dynamodb/",
    "Amplify2" : "https://github.com/aws/aws-amplify"
})

#foreach ($key in $hashmap.keySet())
    #if($foreach.count > 2)
        #break
    #end
    $util.qr($myMap.put($key, $hashmap.get($key)))
#end

##concatenate strings
#set($s1 = "Hello")
#set($s2 = " World")
$util.qr($myMap.put("concat","$s1$s2"))
$util.qr($myMap.put("concat2","Second $s1 World"))

$util.qr($myMap.put("context", $context))

{
    "version" : "2017-02-28",
    "operation": "Invoke",
    "payload": $util.toJson($myMap)
}
```

 **回應範本** 

```
#set($data = {
"id" : "456"
})
#foreach($item in $context.result.entrySet())   ##$context.result is a MAP so we use entrySet()
    #if($item.key == "ifCheck")
        $util.qr($data.put("meta", "$item.value"))
    #end
#end

##Uncomment this out if you want to test and remove the below #if check
##$util.toJson($data)

#if($context.result["id"] == 123)
    $util.toJson($context.result)
  #else
    $util.unauthorized()
#end
```