- > beginValidation(InstrumentationValidationParameters parameters);\n\n\n \/\/ 修改DataFetcher行爲\n default DataFetcher> instrumentDataFetcher(DataFetcher> dataFetcher, InstrumentationFieldFetchParameters parameters) {\n return dataFetcher;\n }\n \n \/\/ 在列表字段結束之後可進行的回調動作\n default InstrumentationContext
如何優雅地擴展GraphQL系統能力
{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"爲什麼要擴展GraphQL系統能力"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"GraphQL可將API表示的數據治理在GraphQL的schema中,爲API提供一套類型化的完整描述,使得客戶端能夠根據所需準確地獲取相應數據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在真實業務場景中,除了獲取基礎數據外,往往還會有一些對數據進行加工轉換和編排控制的需求,例如對數值字段取精或者轉換成展示文案、對列表字段進行排序過濾去重、根據條件判斷是否請求查詢中的某些字段、將一個字段的解析結果作爲另外一個字段的入參等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"原生的GraphQL查詢爲獲取基礎數據提供了便捷,但是計算能力不足導致其結果經常不能滿足業務需求,數據往往需要加工轉換、甚至經過多次編排查詢,才能展示給用戶。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"GraphQL的能力擴展機制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"GraphQL 提供指令作爲執行和校驗能力的擴展機制"},{"type":"text","text":"。指令的定義包括指令名稱、參數列表、可使用位置和是否可在同一位置重複使用等四個元素,用戶可以使用指令描述自定義的執行行爲或校驗規則。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以內置指令"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@skip"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"爲例,該指令定義如下:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@skip"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","text":"主要是解決指定條件滿足時跳過某些字段的獲取解析。判斷條件結果爲指令參數"},{"type":"codeinline","content":[{"type":"text","text":"if"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","text":"。該指令可使用的位置有查詢字段、命名片段和內聯片段,使用時將指令放置在要生效的元素後即可,示例如下:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"query myQuery($someTest: Boolean!) {\n experimentalField @skip(if: $someTest)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在實際業務場景中,是否跳過某些字段獲取的條件大多情況需要根據請求變量進行計算判斷。例如爲App渲染數據時,低於指定版本的客戶端不用請求某些字段,該條件判斷無法通過請求變量只有客戶端版本號的原生查詢實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"GraphQL原生指令只有"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@skip"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" 、"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@include"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" 、"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@deprecated"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" 和"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@specifiedBy"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" ,說明見"},{"type":"link","attrs":{"href":"https:\/\/spec.graphql.org\/draft\/#sec-Type-System.Directives","title":null,"type":null},"content":[{"type":"text","text":"Type-System.Directives"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",提供的能力有限,不能滿足業務計算所需。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL系統能力擴展實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文以"},{"type":"link","attrs":{"href":"https:\/\/github.com\/graphql-calculator\/graphql-calculator","title":null,"type":null},"content":[{"type":"text","text":"GraphQL Calculator"}]},{"type":"text","text":"爲例,介紹對GraphQL系統能力進行擴展的實踐。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開源代碼託管地址:"},{"type":"link","attrs":{"href":"https:\/\/github.com\/graphql-calculator\/graphql-calculator","title":null,"type":null},"content":[{"type":"text","text":"https:\/\/github.com\/graphql-calculator\/graphql-calculator"}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"指令分類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"指令使用位置分爲兩類:可執行位ExecutableDirectiveLocation和類型系統位TypeSystemDirectiveLocation。"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"# ExecutableDirectiveLocation\nQUERY # 查詢操作\nMUTATION # 更新操作\nSUBSCRIPTION # 訂閱操作\nFIELD # 查詢字段\nFRAGMENT_DEFINITION # 命名片段定義\nFRAGMENT_SPREAD # 命名片段\nINLINE_FRAGMENT # 內聯片段\nVARIABLE_DEFINITION # 查詢變量\n\n\n# TypeSystemDirectiveLocation\nSCALAR # 標量\nOBJECT # 對象\nFIELD_DEFINITION # 字段定義\nARGUMENT_DEFINITION # 參數定義\nINTERFACE # 接口\nUNION # 聯合類型\nENUM # 枚舉\nENUM_VALUE # 枚舉值\nINPUT_OBJECT # 輸入對象\nINPUT_FIELD_DEFINITION # 輸入字段定義"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"GraphQL規範並不會限制指令只能定義在可執行位或者類型系統位,但是爲了明確指令是用在查詢上、還是對於類型系統生效,往往只將指令的生效位置限定在其中一種:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"對於可執行位指令,其作用往往跟業務場景相關。例如,每個查詢所要跳過的字段都可能不同,因此"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@skip"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"的生效位置爲"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":";"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"對於類型系統位指令,主要是對類型系統本身額外信息、執行行爲的描述。 例如"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@deprecated"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"說明了一個字段將要被廢棄的原因,其定義位置爲"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"FIELD_DEFINITION | ENUM_VALUE"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"本文重點講解查詢指令的實現:根據不同的業務場景,對查詢進行不同的計算。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"定義指令"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"指令應該服務於特定類型的數據結構和通用的算法處理,而不是特定的業務場景,爲特定的業務場景定義指令將使得指令系統變得臃腫、難以維護。"},{"type":"link","attrs":{"href":"https:\/\/github.com\/graphql-calculator\/graphql-calculator","title":null,"type":null},"content":[{"type":"text","text":"GraphQL Calculator"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"參考了常見的編程概念對指令進行定義:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"字段加工:通過表達式對結果字段進行加工轉換;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"數組處理:對結果中的數組字段進行過濾、排序、去重;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"參數轉換:對請求參數進行轉換,包括加工、過濾、使用其他字段獲取結果進行替換;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"數據編排:將指定字段的獲取結果作爲全局可獲取的上下文,爲其他字段或參數的加工轉換提供可依賴的數據;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"控制流:"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@skip"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"和"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@include"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"拓展版本,通過表達式判斷是否請求註解的字段或片斷。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"指令的命名會直接影響指令的易用性。"},{"type":"link","attrs":{"href":"https:\/\/github.com\/graphql-calculator\/graphql-calculator","title":null,"type":null},"content":[{"type":"text","text":"GraphQL Calculator"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"指令的命名和語義參考了"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"java.util.stream.Stream"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"和GraphQL規範原生指令,易於理解和使用,例如"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@filter"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"、"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@sort"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"、"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"@skipBy"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"執行引擎"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"GraphQL的Java實現提供了"},{"type":"link","attrs":{"href":" https:\/\/www.graphql-java.com\/documentation\/v17\/instrumentation","title":null,"type":null},"content":[{"type":"text","text":"Instrumentation"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"機制,該機制可在查詢的各個階段獲取到執行上下文,可對執行信息進行記錄、修改。該機制的核心接口有"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"Instrumentation"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"、"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"InstrumentationContext"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"和"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"InstrumentationState"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Instrumentation"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Instrumentation"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","text":"主要可獲取指令及執行上下文信息,並對數據進行記錄、修改。該接口部分方法及說明例舉如下:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface Instrumentation {\n \/\/ 創建執行上下文對象\n default InstrumentationState createState() {\n return null;\n }\n\n\n \/\/ 根據本次請求上下文和schema創建執行上下文對象\n default InstrumentationState createState(InstrumentationCreateStateParameters parameters) {\n return createState();\n }\n \n \/\/ 在解析查詢dsl前調用\n InstrumentationContext beginParse(InstrumentationExecutionParameters parameters);\n\n\n \/\/ 在驗證查詢對象前調用\n InstrumentationContext
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.