สร้างอินเทอร์เฟซการค้นหาด้วยวิดเจ็ตการค้นหา

วิดเจ็ตค้นหาจะแสดงอินเทอร์เฟซการค้นหาที่ปรับแต่งได้สำหรับเว็บแอปพลิเคชัน วิดเจ็ตนี้ต้องการ HTML และ JavaScript เพียงเล็กน้อยในการ ติดตั้งใช้งาน และเปิดใช้ฟีเจอร์การค้นหาทั่วไป เช่น Facet และการแบ่งหน้า คุณ ยังปรับแต่งส่วนต่างๆ ของอินเทอร์เฟซด้วย CSS และ JavaScript ได้ด้วย

หากต้องการความยืดหยุ่นมากกว่าที่วิดเจ็ตมีให้ ให้ลองใช้ Query API ดูข้อมูลเกี่ยวกับการสร้างอินเทอร์เฟซการค้นหาด้วย Query API ได้ที่การสร้างอินเทอร์เฟซการค้นหาด้วย Query API

สร้างอินเทอร์เฟซการค้นหา

การสร้างอินเทอร์เฟซการค้นหาต้องมีหลายขั้นตอน ดังนี้

  1. กำหนดค่าแอปพลิเคชันการค้นหา
  2. สร้างรหัสไคลเอ็นต์สำหรับแอปพลิเคชัน
  3. เพิ่มมาร์กอัป HTML สำหรับช่องค้นหาและผลการค้นหา
  4. โหลดวิดเจ็ตในหน้าเว็บ
  5. เริ่มต้นวิดเจ็ต

กำหนดค่าแอปพลิเคชันการค้นหา

อินเทอร์เฟซการค้นหาแต่ละรายการต้องมีแอปพลิเคชันการค้นหาที่กำหนดไว้ใน คอนโซลผู้ดูแลระบบ แอปพลิเคชันการค้นหาให้ข้อมูลเพิ่มเติมสําหรับการค้นหา เช่น แหล่งข้อมูล ข้อมูลประกอบ และการตั้งค่าคุณภาพการค้นหา

หากต้องการสร้างแอปพลิเคชันการค้นหา โปรดดูสร้างประสบการณ์การค้นหาที่กำหนดเอง

สร้างรหัสไคลเอ็นต์สำหรับแอปพลิเคชัน

นอกเหนือจากขั้นตอนในกำหนดค่าการเข้าถึง Google Cloud Search API คุณยังต้องสร้าง Client ID สำหรับเว็บแอปพลิเคชันด้วย

กำหนดค่าโปรเจ็กต์

เมื่อกำหนดค่าโปรเจ็กต์ ให้ทำดังนี้

  • เลือกประเภทไคลเอ็นต์เป็นเว็บเบราว์เซอร์
  • ระบุ URI ต้นทาง ของแอป
  • จดบันทึกรหัสไคลเอ็นต์ที่สร้างขึ้น คุณจะต้องใช้รหัสไคลเอ็นต์เพื่อ ทำขั้นตอนถัดไปให้เสร็จสมบูรณ์ ไม่จำเป็นต้องระบุรหัสลับไคลเอ็นต์สำหรับ วิดเจ็ต

ดูข้อมูลเพิ่มเติมได้ที่ OAuth 2.0 สำหรับเว็บแอปพลิเคชันฝั่งไคลเอ็นต์

เพิ่มมาร์กอัป HTML

วิดเจ็ตต้องใช้ HTML เพียงเล็กน้อยจึงจะทำงานได้ คุณต้องระบุข้อมูลต่อไปนี้

  • องค์ประกอบ input สำหรับช่องค้นหา
  • องค์ประกอบที่จะยึดป๊อปอัปคำแนะนำ
  • องค์ประกอบที่ใช้เก็บผลการค้นหา
  • (ไม่บังคับ) ระบุองค์ประกอบที่จะมีตัวควบคุมแง่มุม

ข้อมูลโค้ด HTML ต่อไปนี้แสดง HTML สำหรับวิดเจ็ตการค้นหา ซึ่งระบุองค์ประกอบที่จะเชื่อมโยงด้วยแอตทริบิวต์ id

serving/widget/public/with_css/index.html
<div id="search_bar">
  <div id="suggestions_anchor">
    <input type="text" id="search_input" placeholder="Search for...">
  </div>
</div>
<div id="facet_results"></div>
<div id="search_results"></div>

โหลดวิดเจ็ต

ระบบจะโหลดวิดเจ็ตแบบไดนามิกผ่านสคริปต์ตัวโหลด หากต้องการรวม โปรแกรมโหลด ให้ใช้แท็ก <script> ดังที่แสดง

serving/widget/public/with_css/index.html
<!-- Google API loader -->
<script src="https://apis.google.com/js/api.js?mods=enable_cloud_search_widget&onload=onLoad" async defer></script>

คุณต้องระบุonloadการเรียกกลับในแท็กสคริปต์ ระบบจะเรียกใช้ฟังก์ชันนี้ เมื่อตัวโหลดพร้อม เมื่อตัวโหลดพร้อมแล้ว ให้โหลดวิดเจ็ตต่อไป โดยเรียกใช้ gapi.load() เพื่อโหลดไคลเอ็นต์ API, Google Sign-In และโมดูล Cloud Search

serving/widget/public/with_css/app.js
/**
* Load the cloud search widget & auth libraries. Runs after
* the initial gapi bootstrap library is ready.
*/
function onLoad() {
  gapi.load('client:auth2:cloudsearch-widget', initializeApp)
}

ระบบจะเรียกใช้ฟังก์ชัน initializeApp() หลังจากโหลดโมดูลทั้งหมดแล้ว

เริ่มต้นวิดเจ็ต

ก่อนอื่น ให้เริ่มต้นไลบรารีของไคลเอ็นต์โดยเรียกใช้ gapi.client.init() หรือ gapi.auth2.init() ด้วยรหัสไคลเอ็นต์ที่สร้างขึ้น และขอบเขต https://www.googleapis.com/auth/cloud_search.query จากนั้นใช้คลาส gapi.cloudsearch.widget.resultscontainer.Builder และ gapi.cloudsearch.widget.searchbox.Builder เพื่อกำหนดค่าวิดเจ็ต และเชื่อมโยงกับองค์ประกอบ HTML

ตัวอย่างต่อไปนี้แสดงวิธีเริ่มต้นวิดเจ็ต

serving/widget/public/with_css/app.js
/**
 * Initialize the app after loading the Google API client &
 * Cloud Search widget.
 */
function initializeApp() {
  // Load client ID & search app.
  loadConfiguration().then(function() {
    // Set API version to v1.
    gapi.config.update('cloudsearch.config/apiVersion', 'v1');

    // Build the result container and bind to DOM elements.
    var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setSearchResultsContainerElement(document.getElementById('search_results'))
      .setFacetResultsContainerElement(document.getElementById('facet_results'))
      .build();

    // Build the search box and bind to DOM elements.
    var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setInput(document.getElementById('search_input'))
      .setAnchor(document.getElementById('suggestions_anchor'))
      .setResultsContainer(resultsContainer)
      .build();
  }).then(function() {
    // Init API/oauth client w/client ID.
    return gapi.auth2.init({
        'clientId': clientId,
        'scope': 'https://www.googleapis.com/auth/cloud_search.query'
    });
  });
}

ตัวอย่างข้างต้นอ้างอิงถึงตัวแปร 2 รายการสำหรับการกำหนดค่า ซึ่งกำหนดไว้ดังนี้

serving/widget/public/with_css/app.js
/**
* Client ID from OAuth credentials.
*/
var clientId = "...apps.googleusercontent.com";

/**
* Full resource name of the search application, such as
* "searchapplications/<your-id>".
*/
var searchApplicationName = "searchapplications/...";

ปรับแต่งประสบการณ์การลงชื่อเข้าใช้

โดยค่าเริ่มต้น วิดเจ็ตจะแจ้งให้ผู้ใช้ลงชื่อเข้าใช้และให้สิทธิ์แอป เมื่อเริ่มพิมพ์คำค้นหา คุณสามารถใช้ การลงชื่อเข้าใช้ด้วยบัญชี Google สำหรับเว็บไซต์ เพื่อมอบประสบการณ์การลงชื่อเข้าใช้ที่ปรับแต่งให้เหมาะกับผู้ใช้มากขึ้น

ให้สิทธิ์ผู้ใช้โดยตรง

ใช้ลงชื่อเข้าใช้ด้วย Google เพื่อตรวจสอบสถานะการลงชื่อเข้าใช้ของ ผู้ใช้ และลงชื่อเข้าใช้หรือออกจากระบบของผู้ใช้ตามต้องการ ตัวอย่างเช่น ตัวอย่างต่อไปนี้ จะสังเกตสถานะ isSignedIn เพื่อตรวจสอบการเปลี่ยนแปลงการลงชื่อเข้าใช้ และ ใช้วิธี GoogleAuth.signIn() เพื่อเริ่มการลงชื่อเข้าใช้จากการคลิกปุ่ม

serving/widget/public/with_signin/app.js
// Handle sign-in/sign-out.
let auth = gapi.auth2.getAuthInstance();

// Watch for sign in status changes to update the UI appropriately.
let onSignInChanged = (isSignedIn) => {
  // Update UI to switch between signed in/out states
  // ...
}
auth.isSignedIn.listen(onSignInChanged);
onSignInChanged(auth.isSignedIn.get()); // Trigger with current status.

// Connect sign-in/sign-out buttons.
document.getElementById("sign-in").onclick = function(e) {
  auth.signIn();
};
document.getElementById("sign-out").onclick = function(e) {
  auth.signOut();
};

ดูรายละเอียดเพิ่มเติมได้ที่ลงชื่อเข้าใช้ด้วย Google

ลงชื่อเข้าใช้ผู้ใช้โดยอัตโนมัติ

คุณสามารถปรับปรุงประสบการณ์การลงชื่อเข้าใช้ให้ดียิ่งขึ้นได้โดยการให้สิทธิ์แอปพลิเคชันล่วงหน้าในนามของผู้ใช้ในองค์กร เทคนิคนี้ยัง���ีประโยชน์ในกรณีที่ใช้ Cloud Identity Aware Proxy เพื่อป้องกันแอปพลิเคชันด้วย

ดูข้อมูลเพิ่มเติมได้ที่หัวข้อใช้การลงชื่อเข้าใช้ด้วยบัญชี Google กับแอปไอที

ปรับแต่งอินเทอร์เฟซ

คุณเปลี่ยนลักษณะของอินเทอร์เฟซการค้นหาได้โดยใช้เทคนิคต่างๆ ร่วมกัน ดังนี้

  • ลบล้างรูปแบบด้วย CSS
  • ตกแต่งองค์ประกอบด้วยอะแดปเตอร์
  • สร้างองค์ประกอบที่กำหนดเองด้วยอแดปเตอร์

ลบล้างรูปแบบด้วย CSS

วิดเจ็ตการค้นหามาพร้อมกับ CSS ของตัวเองเพื่อจัดรูปแบบองค์ประกอบคำแนะนำและผลการค้นหา รวมถึงตัวควบคุมการแบ่งหน้า คุณปรับแต่งองค์ประกอบเหล่านี้ได้ตามต้องการ

ในระหว่างการโหลด วิดเจ็ตค้นหาจะโหลดสไตล์ชีตเริ่มต้นแบบไดนามิก ซึ่งจะเกิดขึ้นหลังจากโหลดสไตล์ชีตของแอปพลิเคชันแล้ว โดยจะเพิ่มลำดับความสำคัญ ของกฎ หากต้องการให้รูปแบบของคุณมีผลเหนือกว่ารูปแบบเริ่มต้น ให้ใช้ตัวเลือกบรรพบุรุษเพื่อเพิ่มความเฉพาะเจาะจงของกฎเริ่มต้น

ตัวอย่างเช่น กฎต่อไปนี้จะไม่มีผลหากโหลดในแท็ก link หรือ style แบบคงที่ในเอกสาร

.cloudsearch_suggestion_container {
  font-size: 14px;
}

แต่ให้ระบุกฎด้วยรหัสหรือคลาสของคอนเทนเนอร์ระดับบน ที่ประกาศในหน้าเว็บ

#suggestions_anchor .cloudsearch_suggestion_container {
  font-size: 14px;
}

ดูรายการคลาสที่รองรับและตัวอย่าง HTML ที่วิดเจ็ตสร้างขึ้นได้ในการอ้างอิงคลาส CSS ที่รองรับ

ตกแต่งองค์ประกอบด้วยอะแดปเตอร์

หากต้องการตกแต่งองค์ประกอบก่อนการแสดงผล ให้สร้างและลงทะเบียนอแดปเตอร์ที่ใช้เมธอดการตกแต่งอย่างใดอย่างหนึ่ง เช่น decorateSuggestionElement หรือ decorateSearchResultElement.

เช่น อะแดปเตอร์ต่อไปนี้จะเพิ่มคลาสที่กำหนดเองลงในองค์ประกอบคำแนะนำและผลลัพธ์

serving/widget/public/with_decorated_element/app.js
/**
 * Search box adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.decorateSuggestionElement = function(element) {
  element.classList.add('my-suggestion');
}

/**
 * Results container adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.decorateSearchResultElement = function(element) {
  element.classList.add('my-result');
}

หากต้องการลงทะเบียนอแดปเตอร์เมื่อเริ่มต้นวิดเจ็ต ให้ใช้setAdapter() เมธอดของคลาส Builder ที่เกี่ยวข้อง

serving/widget/public/with_decorated_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

Decorator อาจแก้ไขแอตทริบิวต์ขององค์ประกอบคอนเทนเนอร์รวมถึงองค์ประกอบย่อย ได้ คุณเพิ่มหรือนำองค์ประกอบย่อยออกได้ในระหว่างการตกแต่ง อย่างไรก็ตาม หากทำการเปลี่ยนแปลงโครงสร้างขององค์ประกอบ ให้พิจารณาสร้าง องค์ประกอบโดยตรงแทนการตกแต่ง

สร้างองค์ประกอบที่กำหนดเองด้วยอแดปเตอร์

หากต้องการสร้างองค์ประกอบที่กำหนดเองสำหรับคำแนะนำ คอนเทนเนอร์ Facet หรือผลการค้นหา ให้สร้างและลงทะเบียนอแดปเตอร์ที่ใช้ createSuggestionElement, createFacetResultElement หรือ createSearchResultElement ตามลำดับ

อแดปเตอร์ต่อไปนี้แสดงการสร้างองค์ประกอบคำแนะนำและผลการค้นหาที่กำหนดเองโดยใช้แท็ก <template> HTML

serving/widget/public/with_custom_element/app.js
/**
 * Search box adapter that overrides creation of suggestion elements.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.createSuggestionElement = function(suggestion) {
  let template = document.querySelector('#suggestion_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.suggested_query').textContent = suggestion.suggestedQuery;
  return fragment.firstElementChild;
}

/**
 * Results container adapter that overrides creation of result elements.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.createSearchResultElement = function(result) {
  let template = document.querySelector('#result_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.title').textContent = result.title;
  fragment.querySelector('.title').href = result.url;
  let snippetText = result.snippet != null ?
    result.snippet.snippet : '';
  fragment.querySelector('.query_snippet').innerHTML = snippetText;
  return fragment.firstElementChild;
}

หาก��้องการลงทะเบียนอแดปเตอร์เมื่อเริ่มต้นวิดเจ็ต ให้ใช้เมธอด setAdapter() ของคลาส Builder ที่เกี่ยวข้อง

serving/widget/public/with_custom_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

การสร้างองค์ประกอบ Facet ที่กำหนดเองด้วย createFacetResultElement มีข้อจำกัดหลายประการดังนี้

  • คุณต้องแนบคลาส CSS cloudsearch_facet_bucket_clickable กับองค์ประกอบที่ผู้ใช้คลิกเพื่อสลับที่เก็บข้อมูล
  • คุณต้องรวมแต่ละ Bucket ไว้ในองค์ประกอบที่มี CSS คลาส cloudsearch_facet_bucket_container
  • คุณไม่สามารถแสดงผลที่เก็บข้อมูลในลำดับที่แตกต่างจากที่ปรากฏใน การตอบกลับ

ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้จะแสดงแง่มุมโดยใช้ลิงก์แทน ช่องทําเครื่องหมาย

serving/widget/public/with_custom_facet/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}

ResultsContainerAdapter.prototype.createFacetResultElement = function(result) {
  // container for the facet
  var container = document.createElement('div');

  // Add a label describing the facet (operator/property)
  var label = document.createElement('div')
  label.classList.add('facet_label');
  label.textContent = result.operatorName;
  container.appendChild(label);

  // Add each bucket
  for(var i in result.buckets) {
    var bucket = document.createElement('div');
    bucket.classList.add('cloudsearch_facet_bucket_container');

    // Extract & render value from structured value
    // Note: implementation of renderValue() not shown
    var bucketValue = this.renderValue(result.buckets[i].value)
    var link = document.createElement('a');
    link.classList.add('cloudsearch_facet_bucket_clickable');
    link.textContent = bucketValue;
    bucket.appendChild(link);
    container.appendChild(bucket);
  }
  return container;
}

// Renders a value for user display
ResultsContainerAdapter.prototype.renderValue = function(value) {
  // ...
}

ปรับแต่งลักษณะการทำงานของการค้นหา

การตั้งค่าแอปพลิเคชันการค้นหาแสดงถึงการกำหนดค่าเริ่มต้น สำหรับอินเทอร์เฟซการค้นหาและเป็นแบบคงที่ หากต้องการใช้ตัวกรองหรือข้อมูลประกอบแบบไดนามิก เช่น อนุญาตให้ผู้ใช้เปิด/ปิดแหล่งข้อมูล คุณสามารถลบล้างการตั้งค่าแอปพลิเคชันการค้นหาได้โดยการสกัดกั้นคำขอค้นหาด้วยอแดปเตอร์

ใช้ตัวดัดแปลงที่มีเมธอด interceptSearchRequest เพื่อแก้ไขคำขอที่ส่งไปยัง Search API ก่อนที่จะดำเนินการ

เช่น อะแดปเตอร์ต่อไปนี้จะสกัดกั้นคำขอเพื่อจำกัดการค้นหา ไปยังแหล่งที่มาที่ผู้ใช้เลือก

serving/widget/public/with_request_interceptor/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}
ResultsContainerAdapter.prototype.interceptSearchRequest = function(request) {
  if (!this.selectedSource || this.selectedSource == 'ALL') {
    // Everything selected, fall back to sources defined in the search
    // application.
    request.dataSourceRestrictions = null;
  } else {
    // Restrict to a single selected source.
    request.dataSourceRestrictions = [
      {
        source: {
          predefinedSource: this.selectedSource
        }
      }
    ];
  }
  return request;
}

หากต้องการลงทะเบียนอแดปเตอร์เมื่อเริ่มต้นวิดเจ็ต ให้ใช้เมธอด setAdapter() เมื่อสร้าง ResultsContainer

serving/widget/public/with_request_interceptor/app.js
var resultsContainerAdapter = new ResultsContainerAdapter();
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(resultsContainerAdapter)
  // ...
  .build();

HTML ต่อไปนี้ใช้เพื่อแสดงกล่องเลือกสำหรับการกรองตาม แหล่งที่มา

serving/widget/public/with_request_interceptor/index.html
<div>
  <span>Source</span>
  <select id="sources">
    <option value="ALL">All</option>
    <option value="GOOGLE_GMAIL">Gmail</option>
    <option value="GOOGLE_DRIVE">Drive</option>
    <option value="GOOGLE_SITES">Sites</option>
    <option value="GOOGLE_GROUPS">Groups</option>
    <option value="GOOGLE_CALENDAR">Calendar</option>
    <option value="GOOGLE_KEEP">Keep</option>
  </select>
</div>

โค้ดต่อไปนี้จะรอการเปลี่ยนแปลง ตั้งค่าการเลือก และ เรียกใช้คำค้นหาอีกครั้งหากจำเป็น

serving/widget/public/with_request_interceptor/app.js
// Handle source selection
document.getElementById('sources').onchange = (e) => {
  resultsContainerAdapter.selectedSource = e.target.value;
  let request = resultsContainer.getCurrentRequest();
  if (request.query) {
    // Re-execute if there's a valid query. The source selection
    // will be applied in the interceptor.
    resultsContainer.resetState();
    resultsContainer.executeRequest(request);
  }
}

นอกจากนี้ คุณยังสกัดกั้นการตอบกลับการค้นหาได้โดยการใช้ interceptSearchResponse ในอแดปเตอร���

ปักหมุดเวอร์ชัน API

โดยค่าเริ่มต้น วิดเจ็ตจะใช้ API เวอร์ชันล่าสุดที่เสถียร หากต้องการล็อกเวอร์ชันที่เฉพาะเจาะจง ให้ตั้งค่าcloudsearch.config/apiVersionพารามิเตอร์การกำหนดค่า เป็นเวอร์ชันที่ต้องการก่อนเริ่มต้นวิดเจ็ต

serving/widget/public/basic/app.js
gapi.config.update('cloudsearch.config/apiVersion', 'v1');

เวอร์ชัน API จะเป็น 1.0 โดยค่าเริ่มต้นหากไม่ได้ตั้งค่าหรือตั้งค่าเป็นค่าที่ไม่ถูกต้อง

ปักหมุดเวอร์ชันวิดเจ็ต

หากต้องการหลีกเลี่ยงการเปลี่ยนแปลงที่ไม่คาดคิดในอินเทอร์เฟซการค้นหา ให้ตั้งค่า cloudsearch.config/clientVersionพารามิเตอร์การกำหนดค่าตามที่แสดง

gapi.config.update('cloudsearch.config/clientVersion', 1.1);

เวอร์ชันวิดเจ็ตจะเป็น 1.0 โดยค่าเริ่มต้นหากไม่ได้ตั้งค่าหรือตั้งค่าเป็นค่าที่ไม่ถูกต้อง

รักษาความปลอดภัยของอินเทอร์เฟซการค้นหา

ผลการค้นหามีข้อมูลที่มีความละเอียดอ่อนสูง ทำตามแนวทางปฏิบัติแนะนำ เพื่อรักษาความปลอดภัยของเว็บแอปพลิเคชัน โดยเฉพาะอย่างยิ่งเพื่อป้องกันการโจมตีแบบคลิกแจ็กกิ้ง

ดูข้อมูลเพิ่มเติมได้ที่ OWASP Guide Project

เปิดใช้การแก้ไขข้อบกพร่อง

ใช้ interceptSearchRequest เพื่อเปิดการแก้ไขข้อบกพร่องสำหรับวิดเจ็ตค้นหา เช่น

  if (!request.requestOptions) {
  // Make sure requestOptions is populated
  request.requestOptions = {};
  }
  // Enable debugging
  request.requestOptions.debugOptions = {enableDebugging: true}

  return request;