הגדרת אפשרויות מתקדמות של API

בדף הזה מוסבר איך להגדיר אפשרויות מתקדמות, כמו מיפוי קלט ומאפיינים וירטואליים, לספקי סוגים. סקירה כללית על סוגים מידע נוסף על ספקי סוגים זמין במדריך בן עמוד אחד לשילוב עם Deployment Manager.

אם אתם מנסים לשלב API שלא עומד בדרישות ה-API שמוגדרות על ידי Deployment Manager, אתם יכולים להשתמש במיפוי קלט ובמאפיינים וירטואליים כדי לפתור את חוסר העקביות הזה. מיפוי קלט מאפשר לספק מיפויים מפורשים של פרמטרים של API במקרים של דו-משמעות, ומאפיינים וירטואליים מאפשרים לחשוף מאפיינים שרירותיים שלא קיימים בממשקי ה-API הבסיסיים, כדי לפשט את הקלט ולהסתיר את המורכבויות של ה-API מהמשתמשים.

כדי להטמיע אפשרויות הגדרה מתקדמות, צריך להכיר היטב את ה-API שעבורו יוצרים את ספק הסוגים. מכיוון שכל API יכול להיות שונה מאוד מאחרים, בדף הזה מופיעות הנחיות כלליות ודוגמאות, אבל לא הנחיות ספציפיות ל-API.

לפני שמתחילים

תרחישים נפוצים שבהם נדרשות אפשרויות הגדרה מתקדמות

שם הנכס נמצא בשימוש חוזר עם ערכים שונים

בממשקי API מסוימים, יכול להיות ששם של מאפיין או פרמטר ישמש שוב בשיטות שונות, אבל עם ערכים שונים. לדוגמה, יכול להיות שב-API מסוים הפרמטר name ליצירת משאב (בקשת POST) יכול לקבל את הערך foo/bar, אבל אותו שדה name בבקשות עדכון (PATCH או PUT) יחייב את הערך foo/bar/baz.

אפשר להסיק את ערכי הנכס מתגובת ה-API

חלק מה-methods של ה-API דורשות ערך שנוצר על ידי השרת ומוחזר כששולחים בקשת GET למשאב. לדוגמה, יכול להיות ש-API ידרוש פרמטר etag כדי לבצע בקשות עדכון כשמשנים משאב. הערך של etag משתנה אחרי כל בקשת שינוי, לכן כדי לקבל את הפרמטר הנוכחי etag צריך לבצע בקשת GET למשאב לפני שמבצעים את הבקשה לעדכון המשאב.

באמצעות מיפוי קלט, אפשר להגדיר ב-Deployment Manager שאפשר לאחזר את השדה etag ממשאב ה-API. ‫Deployment Manager מבצע באופן אוטומטי בקשה לקבלת הערך הזה כשמשתמש קורא לשיטה שציינתם במיפוי הקלט.GET

מפשטים את קלט המשתמש

‫Deployment Manager תומך במאפיינים וירטואליים שהם מאפיינים שרירותיים שאפשר לחשוף למשתמשים באמצעות Deployment Manager לשימושים שונים. נכסים וירטואליים הם משתנים שרירותיים שערכם לא מוגדר ב-API הבסיסי, אבל אפשר להוסיף אותם למיפוי של קלט לפי הצורך. לדוגמה, נניח שיש מאפיין API שצריך לקודד בפורמט Base64 לפני שהערך נשלח ל-API הבסיסי. במקום לבקש מהמשתמשים לספק את הערך בקידוד Base64, אפשר ליצור מאפיין וירטואלי שמבקש מהמשתמשים את הערך בטקסט רגיל, ואז לקודד את הערך ב-Base64 באמצעות מיפויי קלט, ולבסוף לספק את התוצאה ל-API הבסיסי.

ציון אפשרויות מתקדמות

כדי לציין אפשרויות מתקדמות, צריך לספק את המאפיין collectionOverrides כשיוצרים את משאב ספק הסוגים, ולהגדיר מיפויים של קלט או מאפיינים וירטואליים לכל אוסף API לפי הצורך.

לדוגמה, באמצעות ה-CLI של gcloud, אפשר לספק אפשרויות מתקדמות באמצעות קובץ YAML ולספק את קובץ ה-YAML עם הבקשה type-providers create. קובץ YAML לדוגמה:

collectionOverrides:
- collection: /emailAddresses/v1beta/people
  options:
    inputMappings:
    - methodMatch: ^create$
      fieldName: emailAddress.displayName
      value: $.resource.properties.displayName
      location: BODY
    - methodMatch: ^update$
      fieldName: displayName
      value: $.resource.properties.displayName
      location: PATH
    virtualProperties: |
      schema: http://json-schema.org/draft-04/schema#
      type: object
        properties:
          displayName:
            type: string
credential:
  basicAuth:
    user: [USERNAME]
    password: [PASSWORD]

ההגדרות האלה אומרות ל-Deployment Manager:

  • בשיטה create, מחפשים את השדה שנקרא emailAddress.displayName בגוף המשאב ומגדירים את הערך של השדה הזה לקלט של המשתמש במאפיין displayName בהגדרות של Deployment Manager. לכן, אם משתמש מגדיר את ההגדרה שלו כך:

     resources:
     - name: example
       type: myproject/emailAddress:/emailAddresses/v1beta/people
       properties:
       - displayName: John Doe
         ...
    

    ‫Deployment Manager יגדיר את הערך של emailAddress.displayName ל-John Doe.

  • בשיטה update, השדה נמצא בנתיב המשאב ולא בגוף המשאב, אבל מוחל אותו מיפוי קלט.

קביעת מיפויי קלט

מיפוי קלט מאפשר לכם למפות או להוסיף מידע לשדות מסוימים ב-API, כדי ש-Deployment Manager יוכל ליצור אינטראקציה חלקה יותר עם ה-API הבסיסי. כך המשתמשים לא צריכים להבין את ההתנהגות המורכבת של ה-API.

אפשר להשתמש במיפוי קלט כדי לפשט את האינטראקציה של המשתמשים עם ה-API. לדוגמה, אפשר להשתמש במיפוי קלט כדי לקבל באופן אוטומטי ערכים שנוצרו על ידי השרת, כמו טביעות אצבע, מזהים או תגי etag. כך המשתמשים לא צריכים לבצע בקשת get נפרדת במשאב בכל פעם שהם רוצים לבצע עדכון.

באופן דומה, אפשר גם להשתמש במיפויים של קלט כדי לטפל במצבים מעורפלים או מבלבלים שבהם לאותו שדה API יש ערכים שונים בשיטות שונות. לדוגמה, בקשה ליצירת משאב עשויה לדרוש מאפיין name שהמשתמש יכול לציין, אבל אותו API עשוי לדרוש מאפיין name בפורמט שונה לשיטות update. אפשר להשתמש במיפוי קלט כדי לציין ל-Deployment Manager איזה ערך מתאים לכל שיטת API.

כדי לציין מיפויי קלט לספק סוגים, צריך לספק את המאפיין options.inputMappings. אפשר להגדיר מיפויי קלט שחלים על כל ה-API, או לספק במפורש מיפויי קלט לכל אוסף:

# Input mappings for the entire API
"options": {
  "inputMappings": [
      {
          "fieldName": "[NAME]",
          "location":  "[PATH | BODY | QUERY | HEADER]",
          "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
          "value": "[VALUE_TO_INJECT]"
      },
      {
          "fieldName": "[NAME]",
          "location":  "[PATH | BODY | QUERY | HEADER]",
          "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
          "value": "[VALUE_TO_INJECT]"
      }
   ]
},
# Input mappings for specific collections
"collectionOverrides": [
    {
        "collection": "[SPECIFIC_COLLECTION]",
        "options": {
            "inputMappings": [
                {
                    "fieldName": "[NAME]",
                    "location": "[PATH | BODY | QUERY | HEADER]",
                    "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
                    "value": "[VALUE_TO_INJECT]"
                },
                {
                    "fieldName": "[NAME]",
                    "location": "[PATH | BODY]",
                    "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
                    "value": "[VALUE_TO_INJECT]"
                },
                ...[additional fields if necessary]...
            ]
        }
    }
]

בהמשך מפורט כל אחד מהחלקים החשובים בתחביר הזה.

אוסף

[SPECIFIC_COLLECTION] הוא אוסף ה-API שאליו מיפוי הקלט הזה מתייחס. לדוגמה, אם סיפקתם מיפויים של קלט למסמך Discovery של Google, כמו IAM Service Accounts API, האוספים הרלוונטיים הם projects.serviceAccounts ו-projects.serviceAccountKeys.

ב-API שמשתמש במפרט OpenAPI, נתיב האיסוף יכול להיות /example-collection/{name}. אפשר לעיין בדוגמה פונקציונלית של OpenAPI במאגר OpenAPI GitHub.

שם השדה

"fieldName" הוא מאפיין או נכס של API שרוצים לציין עבורו את מיפוי הקלט. לדוגמה, "fieldName": "fingerprint", "fieldName": "etag" וכן הלאה.

Location

מאפייני API יכולים להופיע כפרמטרים בנתיב כתובת ה-URL, או כחלק מגוף הבקשה או התגובה. מציינים איפה מיפוי הקלט הזה חל, למשל כתובת ה-URL PATH או הבקשה BODY כמיקום. הערכים הנתמכים כוללים:

  • PATH
  • BODY
  • QUERY
  • HEADER

Method match

מציינים לאילו שיטות מיפוי הקלט הזה חל. משתמשים בביטוי רגולרי כדי לציין כמה שיטות. לדוגמה:

"methodMatch":"^create$"

במפרטים של OpenAPI, אפשר:

"methodMatch: ^(put|get|delete|post)$"

ערך

מציינים את הערך ש-Deployment Manager צריך להוסיף לשדה הזה. השדה הזה משתמש בסימון JSONPath. לדוגמה, מיפוי הקלט הזה אומר שעבור השדה name, ‏ Deployment Manager צריך לקחת את הערך שהמשתמש סיפק ולהוסיף אותו לפורמט projects/$.project/topics/$resource.properties.topic:

"inputMappings":[
{
  "fieldName":"name",
  "location":"PATH",
  "methodMatch":"^post$",
  "value":"concat(\"projects/\", $.project, \"/topics/\", $.resource.properties.topic)"
}...
  • כשמשתמשים ב-$.resource.properties.[VARIABLE], מגדירים ערך למאפיין שהמשתמש יגדיר בהגדרות שלו. לדוגמה, עבור $.resource.properties.topic, הערך יהיה הערך שהמשתמש סיפק למאפיין topic בהגדרה שלו:

    resources:
    - name: example
      type: example-type-provider:collectionA
      properties:
        topic: history # The value of "history" would be used for the `name` parameter because of the input mapping above
    
  • כדי להפנות למשאב עצמו אחרי בקשת get, משתמשים ב-$.resource.self.[VARIABLE]. לדוגמה, אם רוצים לקבל את טביעת האצבע העדכנית בבקשות עדכון, אפשר להשתמש בתחביר הזה כדי להנחות את Deployment Manager לבצע get ולשלוף את הערך:

    {
      'fieldName': 'fingerprint',
      'location': 'BODY',
      'methodMatch': '^(put)$',
      # self represents the resource by doing a GET on it.
      # This mappings gets latest fingerprint on the request.
      # Final PUT Body will be
      # {
      #   "name": "my-resource-name",
      #   "fingerprint": "<server generated fingerprint>"
      # }
      'value': '$.resource.self.fingerprint'
    }
    

שימוש במאפיינים וירטואליים

מאפיינים וירטואליים הם מאפיינים שרירותיים שאפשר לחשוף למשתמשים באמצעות Deployment Manager. המאפיינים האלה לא מהווים חלק מה-API הבסיסי, אלא הם משתנים שרירותיים שאפשר להשתמש בהם כדי להעביר מידע או להסתיר אי-התאמות ב-API מהמשתמשים. אפשר גם להפנות למאפיינים וירטואליים במיפויי הקלט.

מאפיינים וירטואליים פועלים לפי סכימת JSON 4. צריך לספק נכסים וירטואליים כחלק מהמאפיין options לאוסף ספציפי:

"collection": "[SPECIFIC_COLLECTION]",
  "options": {
   "virtualProperties": "schema: http://json-schema.org/draft-04/schema#\ntype: object\nproperties:\n  [PROPERTY]:\n    type: [DATA_TYPE]\n  [ANOTHER_PROPERTY]:\n    type: [ANOTHER_DATA_TYPE]n"
   "inputMappings": [
    ...
   ]
  }

בקובץ הגדרה ב-YAML, זה ייראה כך:

- collection: projects.serviceAccounts
  options:
    virtualProperties: |
      schema: http://json-schema.org/draft-04/schema#
      type: object
      properties:
        a-property:
          type : string
        b-property:
          type : string
      required:
      - a-property
      - b-property
    inputMappings:
    ...

לדוגמה, נניח שיש API מזויף שיוצר כתובות אימייל. נניח של-API יש שיטה ליצירת אימייל שמקבלת מאפיין emailAddress.displayName. כשמשתמש שולח בקשה ליצירת כתובת אימייל, הוא מציין בקשה כזו:

POST https://example.com/emailAddresses/v1beta/people/

{
  "emailAddress": {
    "displayName": "john"
  }
}

נניח ש-API מסוים מאפשר לעדכן את כתובת האימייל, אבל ה-method לעדכון כתובת האימייל דורש רק את המאפיין displayName ולא את המאפיין email.displayName:

POST https://example.com/emailAddresses/v1beta/people/john

{
  "displayName": "josh"
}

איך אתם מצפים שהמשתמשים יספקו את הערך הזה כשהם משתמשים בסוג הזה של ספק? אפשר לבקש מהם לציין את הנכס בצורה אחרת בהתאם לפעולה:

# Creating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    emailAddress:
      displayName: john


# Updating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    displayName: john

לחלופין, אפשר ליצור מאפיין וירטואלי שמקבל את אותו ערך, ללא קשר לפעולה, ואז להשתמש במיפויי קלט כדי למפות את המאפיין הווירטואלי לפרמטר המתאים ב-API. בדוגמה הזו, נניח שהגדרתם נכס וירטואלי בשם displayName. מיפוי הקלט יכול להיראות כך:

{
    "collectionOverrides":[
      {
        "collection":"emailAddresses",
        "options":{
          "inputMappings":[
            {
              "fieldName":"emailAddress.displayName",
              "location":"BODY",
              "methodMatch":"^create$",
              "value":"$.resource.properties.displayName"
            },
            {
              "fieldName":"displayName",
              "location":"BODY",
              "methodMatch":"^update$",
              "value":"$.resource.properties.displayName"
            }
          ],
          "virtualProperties":"schema: http://json-schema.org/draft-04/schema#\ntype: object\nproperties:\n  displayName:\n    type: string\nrequired:\n- displayName\n"
        }
      }
    ],
    "descriptorUrl":"https://example.com/emailAddresses/v1beta/",
    "options":{
      "nameProperty":""
    }
}

באופן ספציפי, המאפיין הווירטואלי מוגדר כאן:

"virtualProperties":"schema: http://json-schema.org/draft-04/schema#\ntype: object\nproperties:\n  displayName:\n    type: string\nrequired:\n- displayName\n"

בפורמט קריא לאנשים:

"virtualProperties":
  "schema: http://json-schema.org/draft-04/schema#\n
   type: object\n
   properties:\n
     displayName:\n
     - type: string\n
   required:\n
   - displayName\n"

מעכשיו, המשתמשים יכולים לציין את displayName כנכס ברמה העליונה גם בבקשות לעדכון וגם בבקשות ליצירה, ומערכת Deployment Manager תדע איך למפות את הערך בצורה נכונה.

# Creating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    displayName: john


# Updating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    displayName: john

המאמרים הבאים