커뮤니티 커넥터 구축

커뮤니티 커넥터를 구축하는 단계는 다음과 같습니다.

  1. 새 Apps Script 프로젝트를 만듭니다.
  2. 커넥터 코드를 작성합니다.
  3. 프로젝트 매니페스트를 완성합니다.

새 Apps Script 프로젝트 만들기

새 프로젝트를 만들려면 Google Apps Script를 방문하세요. Apps Script에서 기본 스크립트를 만듭니다. 원하는 경우 myFunction 함수를 삭제하고 프로젝트 이름을 바꿉니다. (Apps Script에 대해 자세히 알아보기)

커넥터 코드 작성

모든 커넥터에는 특정 기능 집합이 정의되어 있어야 합니다. 호스팅 애플리케이션 (예: Looker Studio)에서 이러한 기능을 실행합니다. 커넥터는 커뮤니티 커넥터 API 참조에 설명된 대로 수신 요청을 처리하고 응답해야 합니다. 코드를 개발하는 중에 문제가 발생하면 디버깅 가이드를 참고하세요.

getAuthType()에서 인증 유형 정의

이 함수는 서드 파티 서비스에 사용되는 인증 방법을 식별하기 위해 호출됩니다. 자세한 내용은 getAuthType() 참조를 확인하세요. 현재 지원되는 인증 방법은 AuthType 참조에 나열되어 있습니다.

예를 들어 다음 커넥터는 인증이 필요하지 않습니다.

npm-downloads/src/auth.js
var cc = DataStudioApp.createCommunityConnector();

// https://developers.google.com/datastudio/connector/reference#getauthtype
function getAuthType() {
  var AuthTypes = cc.AuthType;
  return cc
    .newAuthTypeResponse()
    .setAuthType(AuthTypes.NONE)
    .build();
}

데이터 소스에 OAuth 2.0 인증이 필요한 경우 OAuth 2.0 인증 가이드를 확인하고 커넥터에 필요한 기능을 추가하세요.

getConfig()를 통해 구성 정의

getConfig() 함수는 커넥터에 필요한 사용자 제공 값을 포함한 커넥터 구성을 가져오기 위해 호출됩니다. 자세한 내용은 getConfig() 참조를 확인하세요.

getConfig()에서 제공된 응답에 따라 Looker Studio는 커넥터 구성 화면을 렌더링합니다. 지원되는 구성 요소는 ConfigType 참조에 나열되어 있습니다.

데이터 소스에 날짜가 매개변수로 필요한 경우 config.setDateRangeRequired(true)를 호출합니다. 조건부 또는 동적 구성 관련 질문은 단계별 구성을 참조하세요.

다음은 사용자에게 npm 패키지 이름 코드를 입력해야 하는 커넥터의 예시입니다. 정보와 입력 필드는 getConfig() 함수에 정의됩니다.

npm-downloads/src/main.js
// https://developers.google.com/datastudio/connector/reference#getconfig
function getConfig() {
  var config = cc.getConfig();

  config
    .newInfo()
    .setId('instructions')
    .setText(
      'Enter npm package names to fetch their download count. An invalid or blank entry will revert to the default value.'
    );

  config
    .newTextInput()
    .setId('package')
    .setName(
      'Enter a single package name or multiple names separated by commas (no spaces!)'
    )
    .setHelpText('e.g. "googleapis" or "package,somepackage,anotherpackage"')
    .setPlaceholder(DEFAULT_PACKAGE)
    .setAllowOverride(true);

  config.setDateRangeRequired(true);

  return config.build();
}

getSchema()를 사용하여 필드 정의

이 함수는 지정된 요청의 스키마를 가져오기 위해 호출됩니다. getConfig() 함수로 정의된 모든 구성 매개변수는 request 인수에 제공됩니다. 자세한 내용은 getSchema() 참조를 확인하세요.

커넥터의 데이터 소스와 사용자가 제공한 구성에 따라 스키마가 고정되거나 요청 시 동적으로 제공해야 할 수도 있습니다.

예를 들어 커넥터가 보고서 ID를 기반으로 보고서 데이터를 가져오는 경우 해당 보고서에 대해 반환되는 데이터이므로 스키마를 미리 알 수 없습니다. 이 경우 getSchema()에 데이터 가져오기가 필요할 수 있으며 스키마를 계산해야 합니다.

npm-downloads/src/main.js
function getFields() {
  var fields = cc.getFields();
  var types = cc.FieldType;
  var aggregations = cc.AggregationType;

  fields
    .newDimension()
    .setId('packageName')
    .setName('Package')
    .setType(types.TEXT);

  fields
    .newDimension()
    .setId('day')
    .setName('Date')
    .setType(types.YEAR_MONTH_DAY);

  fields
    .newMetric()
    .setId('downloads')
    .setName('Downloads')
    .setType(types.NUMBER)
    .setAggregation(aggregations.SUM);

  return fields;
}

// https://developers.google.com/datastudio/connector/reference#getschema
function getSchema(request) {
  return {schema: getFields().build()};
}

getData()로 데이터 가져오기 및 반환

이 함수는 지정된 요청에 대한 데이터를 가져오기 위해 호출됩니다. getConfig() 함수로 정의된 모든 구성 매개변수는 request 인수에 제공됩니다. 자세한 내용은 getData() 참조를 확인하세요.

getData() 요청의 다음 매개변수는 추가 주의가 필요합니다.

  • lastRefresh
    lastRefresh는 데이터 새로고침에 대한 가장 최근 요청의 시간을 나타내는 타임스탬프를 나타냅니다. new Date(timestampString)로 값을 파싱할 수 있어야 합니다. Apps Script 캐시 서비스 또는 다른 캐싱 메서드를 사용 중인 경우 lastRefresh 타임스탬프를 사용하면 데이터 소스에 대한 가져오기 요청을 새로 할지 아니면 캐시에서 데이터를 제공할지 결정할 수 있습니다.

  • dateRange
    getConfig()에서 dateRangeRequiredtrue로 설정되면 각 getData() 호출에 요청에서 선택한 기간이 포함됩니다. 자세한 내용은 기간 사용을 참조하세요.

다음 예에서는 수신 요청을 기준으로 데이터를 가져오고 패키지 통계를 반환합니다.

npm-downloads/src/main.js
// https://developers.google.com/datastudio/connector/reference#getdata
function getData(request) {
  request.configParams = validateConfig(request.configParams);

  var requestedFields = getFields().forIds(
    request.fields.map(function(field) {
      return field.name;
    })
  );

  try {
    var apiResponse = fetchDataFromApi(request);
    var normalizedResponse = normalizeResponse(request, apiResponse);
    var data = getFormattedData(normalizedResponse, requestedFields);
  } catch (e) {
    cc.newUserError()
      .setDebugText('Error fetching data from API. Exception details: ' + e)
      .setText(
        'The connector has encountered an unrecoverable error. Please try again later, or file an issue if this error persists.'
      )
      .throwException();
  }

  return {
    schema: requestedFields.build(),
    rows: data
  };
}

/**
 * Gets response for UrlFetchApp.
 *
 * @param {Object} request Data request parameters.
 * @returns {string} Response text for UrlFetchApp.
 */
function fetchDataFromApi(request) {
  var url = [
    'https://api.npmjs.org/downloads/range/',
    request.dateRange.startDate,
    ':',
    request.dateRange.endDate,
    '/',
    request.configParams.package
  ].join('');
  var response = UrlFetchApp.fetch(url);
  return response;
}

/**
 * Parses response string into an object. Also standardizes the object structure
 * for single vs multiple packages.
 *
 * @param {Object} request Data request parameters.
 * @param {string} responseString Response from the API.
 * @return {Object} Contains package names as keys and associated download count
 *     information(object) as values.
 */
function normalizeResponse(request, responseString) {
  var response = JSON.parse(responseString);
  var package_list = request.configParams.package.split(',');
  var mapped_response = {};

  if (package_list.length == 1) {
    mapped_response[package_list[0]] = response;
  } else {
    mapped_response = response;
  }

  return mapped_response;
}

/**
 * Formats the parsed response from external data source into correct tabular
 * format and returns only the requestedFields
 *
 * @param {Object} parsedResponse The response string from external data source
 *     parsed into an object in a standard format.
 * @param {Array} requestedFields The fields requested in the getData request.
 * @returns {Array} Array containing rows of data in key-value pairs for each
 *     field.
 */
function getFormattedData(response, requestedFields) {
  var data = [];
  Object.keys(response).map(function(packageName) {
    var package = response[packageName];
    var downloadData = package.downloads;
    var formattedData = downloadData.map(function(dailyDownload) {
      return formatData(requestedFields, packageName, dailyDownload);
    });
    data = data.concat(formattedData);
  });
  return data;
}

프로젝트 매니페스트 완료

매니페스트 파일에는 Looker Studio에서 커넥터를 배포하고 사용하는 데 필요한 커뮤니티 커넥터에 대한 정보가 포함되어 있습니다.

Apps Script 개발 환경에서 매니페스트 파일을 수정하려면 보기 메뉴를 클릭하고 매니페스트 파일 표시를 클릭하세요. 이렇게 하면 새로운 appsscript.json 매니페스트 파일이 생성됩니다.

다음 데이터를 포함하도록 매니페스트를 업데이트합니다.

npm-downloads/src/appsscript.json
{
  "dependencies": {
    "libraries": []
  },
  "dataStudio": {
    "name": "npm Downloads",
    "logoUrl": "https://raw.githubusercontent.com/npm/logos/master/npm%20square/n-64.png",
    "company": "Google Data Studio Developer Relations",
    "companyUrl": "https://developers.google.com/datastudio/",
    "addonUrl": "https://github.com/googledatastudio/community-connectors/tree/master/npm-downloads#readme",
    "supportUrl": "https://github.com/googledatastudio/community-connectors/issues",
    "description": "Get npm package download counts.",
    "sources": ["npm"],
    "templates": {
      "default": "1twu0sHjqR5dELAPyGJcw4GS3-D0_NTrQ"
    }
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request"
  ]
}

Looker Studio 매니페스트에 대한 자세한 내용은 참조 매니페스트 참조를 확인하세요.

다음 단계

다음 단계는 커뮤니티 커넥터를 배포하는 것입니다.