Это продолжение перевода книги Грегори Декер, Рик де Гроот, Мелисса де Корте. Полное руководство по языку М Power Query. Power Query поддерживает широкий спектр коннекторов для доступа и извлечения данных из различных источников. Power BI Desktop поддерживает почти 200 различных коннекторов, большинство из которых были рассмотрены в главе 3 Доступ к данным и их объединение. Многие из этих коннекторов являются общими для различных стандартов и протоколов форматов данных, таких как Open Data Protocol (OData), Open Database Connectivity (ODBC), JavaScript Object Notation (JSON), Extensible Markup Language (XML) и Parquet. Поддержка этих стандартов и протоколов значительно расширяет возможные источники данных для Power Query до тысяч, если не десятков тысяч.
Мои комментарии набраны с отступом.
Скачать заметку в формате Word или pdf
Даже с учетом того, что все источники данных уже поддерживаются Power Query, все еще существует потребность в поддержке дополнительных источников данных. В частности, различные веб-сервисы часто предоставляют интерфейс прикладного программирования (API), который необходимо использовать для доступа к их данным. В этой главе показано, как создать пользовательские расширения для Power Query. Будут изучены следующие темы:
- Что такое расширения Power Query?
- Подготовка среды
- Создание пользовательского коннектора
- Установка и использование пользовательского коннектора
Что такое расширения Power Query?
Расширения Power Query – это фрагменты кода M, которые вносят свой вклад в глобальную среду. Обратитесь к главе 7 Концепция М, чтобы вспомнить понятие глобальной окружающей среды. Расширения используются для создания пользовательских коннекторов, но также могут использоваться для создания библиотеки пользовательских функций.
Встречаются расширения четырех типов файлов:
- m: простой текстовый файл, содержащий код M.
- Pq: простой текстовый файл, содержащий код М.
- mez: стандартный ZIP-файл, содержащий пользовательские файлы коннекторов. Можно использовать стандартный ZIP-файл и переименовать его из file.zip в file.mez.
- pqx: ZIP-файл Open Packaging Conventions (OPC), содержащий пользовательские файлы коннекторов. Обычно упаковывается с помощью библиотеки System.IO.Packaging.NET. Этот формат упаковки позволяет подписывать файл цифровыми подписями и является форматом для коннекторов, сертифицированных корпорацией Майкрософт.
Файлы .mez и .pqx – это наборы файлов, используемых для создания пользовательских коннекторов. Эти наборы всегда включают один файл .m или .pq, который является основным, содержащим код пользовательского коннектора.
Расширения Power Query реализуются с помощью ключевого слова section, а функции внутри section предоставляются глобальной среде с помощью ключевого слова shared. Секции реализованы в виде кода М со специальными синтаксическими правилами.
Рассмотрим следующий код. Обратите внимание на особое использование точки с запятой (;) в синтаксисе раздела. Подобно тому, как несколько выражений в операторе let разделяются запятой (,), несколько выражений в разделе разделяются точкой с запятой:
1 2 3 4 5 6 7 8 |
section MyAwesomeExtension; shared MyAwesomeExtension.DoSomething = ( firstNumber as number, secondNumber as number ) as number => MyAwesomeExtension.SquareNumber(firstNumber) + MyAwesomeExtension.SquareNumber(secondNumber); MyAwesomeExtension.SquareNumber = (paramNumber as number) as number => paramNumber * paramNumber; |
В этом разделе есть одна общая функция, MyAwesomeExtension.DoSomething, и одна внутренняя функция, доступная только в разделе, MyAwesomeExtension.SquareNumber. Функция MyAwesomeExtension.DoSomething вызывает MyAwesomeExtension.SquareNumber , чтобы возвести два числа в квадрат и сложить их вместе.
Вставьте этот код в текстовый редактор и сохраните файл с именем MyAwesomeExtension.pq. Поместите этот файл в папку …\Документы\Microsoft Power BI Desktop\Custom Connectors. Скорее всего, вам придется создать эту папку.
Откройте Power BI Desktop и пройдите Файл –> Параметры и настройки –> Параметры. В разделе Глобальные выберите Безопасность и установить переключатель:
Рис. 16.1 Включение несертифицированных расширений
Закройте Power BI Desktop и перезапустите Power BI Desktop. Откройте редактор Power Query и создайте запрос:
1 2 3 4 |
let Source = MyAwesomeExtension.DoSomething(10, 5) in Source |
Этот запрос возвращает 125, результат выражения 10^2+5^2. Обратите внимание, что только функция MyAwesomeExtension.DoSomething доступна и может использоваться в запросе. На функцию MyAwesomeExtension.SquareNumber нельзя ссылаться, так как перед ней не стоит ключевое слово shared.
Что можно делать с расширениями?
Существует два основных варианта использования расширений в Power Query:
- Библиотека пользовательских функций. Вместо того чтобы копировать и вставлять код от одного проекта к другому, вы можете включить пользовательские функции в раздел с ключевым словом shared . Странно, но эта опция используется не часто.
- Пользовательские коннекторы. Расширения Power Query позволяют разработчикам создавать настраиваемые коннекторы.
Пользовательские коннекторы можно использовать для создания новых источников данных или настройки и расширения существующих источников. После установки эти коннекторы расширяют глобальную среду Power Query, предоставляя новые функции доступа к данным, которые по умолчанию отсутствуют в языке M. Оставшаяся часть главы посвящена созданию пользовательского коннектора. А начнем мы с подготовки среды разработки.
Подготовка среды
Чтобы подготовить среду, необходимо загрузить и установить два компонента программного обеспечения:
- Visual Studio Code
- Комплект средств разработки программного обеспечения (SDK) Power Query
Пользовательский коннектор, описанный в этой главе, подключается к Discord, популярной платформе для обмена мгновенными сообщениями и передачи голоса по интернет-протоколу (VoIP). Мы будем использовать Discord API для подключения к серверу Discord и получения информации. Чтобы использовать Discord, нам также потребуется установить и настроить два дополнительных компонента, а именно:
- Информационные услуги в Интернете (Internet Information Services)
- Discord
Получение Visual Studio Code
В настоящее время существует две версии Power Query: версия, выпущенная в 2017 году, которая работает как расширение для Visual Studio 2017 и 2019, и новая версия (на дату публикации книги выпущена предварительная версии), которая работает с Visual Studio Code. Мы будем работать с новой версией Power BI SDK для Visual Studio Code. Чтобы начать работу, скачайте и установите последнюю версию Visual Studio Code.
Если ранее вы работали с Visual Studio 2017, 2019 или 2022, интерфейс Visual Studio Code скорее всего вам не понравится. Это тот случай, когда аббревиатуру GUI можно расшифровывать как необоснованно неинтуитивный интерфейс (Gratuitously Unintuitive Interface). В Visual Studio Code есть много отличий от предыдущих версий и странных причуд, которые способны вызвать путаницу. Несмотря на то, что полное рассмотрение опыта работы с Visual Studio Code выходит за рамки данной книги, мы предприняли значительные усилия, чтобы указать, где и как перемещаться по Visual Studio Code для создания пользовательского коннектора. Кроме того, действия в разделе Создание пользовательского коннектора были специально разработаны для минимизации количества случайных ошибок или сообщений. Поэтому внимательно следуйте инструкциям в этом разделе.
Совет: Иногда помогает просто сохранить свою работу, закрыть папку, а затем снова открыть.
После установки Visual Studio Code следует установить пакет SDK для Power Query.
Получение пакета SDK для Power Query
Пакет SDK для Power Query можно скачать в Visual Studio Marketplace. Если вы используете Visual Studio 2017 или 2019, вы можете скачать и установить старую версию SDK по ссылке https://marketplace.visualstudio.com/items?itemName=Dakahn.PowerQuerySDK (01.10.2024 ссылка не работала). Примеры основаны на SDK для Visual Studio Code.
При нажатии кнопки Установить (Install) на веб-странице нового пакета SDK Power Query для Visual Studio Code запускается Visual Studio Code и открывается страница установки пакета SDK для Power Query. Просто нажмите кнопку Установить, чтобы загрузить SDK. После установки пакета SDK для Power Query следующим шагом является настройка локального веб-сервера.
Настройка информационных служб Интернета
Internet Information Services – это веб-сервер, который можно настроить на любом компьютере с Windows. Чтобы настроить службы Internet Information Services на локальном компьютере нажмите Win + R. В окне Выполнить введите optionalfeatures. Нажмите OK. В окне Компоненты Windows включите флажок рядом с пунктом Internet Information Services или Службы IIS. Если раскрыть пункт Службы IIS по умолчанию автоматически выбираются Службы Интернета и Службы управления веб-сайтом, но не FTP-сервер. Для наших целей это нормально. Нажмите OK.
Рис. 16.2. Включение информационных служб Интернета
В нашем пользовательском коннекторе мы будем использовать OAuth для аутентификации. OAuth расшифровывается как Open Authentication и является открытым стандартом, который предоставляет ограниченный доступ к веб-сайту или сервису сторонним приложениям. Мы будем использовать локальные службы Internet Information Services в качестве конечной точки для перенаправления единого идентификатора ресурса (URI), требуемого протоколом OAuth.
Обратите внимание, что не слишком важно, какой URI для перенаправления предоставляется, и фактический URI даже не обязательно должен существовать в некоторых случаях, в том числе в нашем примере. Все, что требуется, – это наличие веб-сервера, который будет служить конечной точкой перенаправления URI.
В качестве альтернативы использованию локального веб-сервера в качестве конечной точки мы теоретически могли бы использовать практически любой URI перенаправления OAuth, например https://oauth.powerbi.com/views/oauthredirect.html. Однако это может быть рискованно, если доменное имя и, следовательно, веб-служба не находятся под контролем разработчика, поскольку эта конечная точка может в какой-то момент стать недействительной и, таким образом, потенциально вызвать ошибку в процессе аутентификации.
Обратите внимание, что этот вид ошибки «не действителен» отличается от того, что страница просто не существует для веб-сервера. Например, jazzysneakers123.com является недействительным доменным именем и, таким образом, вызовет ошибку в процессе аутентификации, если кто-то попытается использовать jazzysneakers.com в качестве URI перенаправления. Это отличается от использования недействительной страницы для доменного имени, которое существует и отвечает на запросы в качестве веб-сервера. В последнем случае генерируется сообщение 404 Страница не найдена, и это не приведет к автоматическому сбою процесса аутентификации.
Для целей разработки мы будем использовать локальные информационные службы Интернета, и мы можем называть этот веб-сервер локальным хостом. Очевидно, что этого будет недостаточно для пользовательского коннектора, поскольку не у всех установлен и настроен локальный веб-сервер по умолчанию. В реальных сценариях разработчику потребуется настроить общедоступный веб-сервер и соответствующий универсальный код ресурса (URI) перенаправления. Такой сценарий выходит за рамки книги, но это может быть так же просто, как создать блог на WordPress с пользовательским доменом или без него.
Следующим шаг – установка и настройка Discord.
Настройка Discord
Чтобы настроить Discord для использования с Discord API, нам потребуются:
- Клиент Discord
- Сервер Discord
- Приложение Discord
- Конфигурация OAuth
Клиент Discord
Чтобы загрузить и установить клиент Discord, в браузере откройте https://discord.com/. Нажмите Загрузить для Windows на главной странице, а затем запустите установку, чтобы установить Discord. Вы также можете использовать кнопку Открыть Discord в браузере.
Я не смог запустить клиента на ПК, поэтому использовал версию в браузере.
Сервер Discord
Чтобы создать сервер Discord, нажмите значок + в левой панели навигации клиента Discord.
Рис. 16.3. Добавление сервера Discord
Выберите тип сервера, укажите его имя, нажмите Создать. Cервер появится на левой панели навигации клиента Discord. Следующим шагом будет создание приложения Discord.
Приложение Discord
Чтобы использовать Discord API, нам нужно создать приложение Discord. Для этого:
- В браузер перейдите на https://discord.com/developers/applications.
- Нажмите кнопку New Application в правом верхнем углу.
- Укажите имя приложения и нажмите Create:
Рис. 16.4. Создание приложения Discord
Вы попадете на страницу настроек приложения. Здесь мы настроим OAuth. Это приложение можно добавить на сервер Discord, хотя для наших целей это не понадобится.
Настройка OAuth в Discord
Чтобы использовать API для отправки запросов к Discord API от имени пользователя, сначала нужно авторизовать приложение:
Перейдите на вкладку OAuth2 в левой области навигации на странице настроек приложения. Обратите внимание на идентификатор клиента в разделе Информация о клиенте (Client information). Скопируйте и сохраните этот идентификатор, он понадобится нам при создании пользовательского коннектора. Нажмите кнопку Сбросить секрет (Reset Secret):
Рис. 16.5. Добавление редиректа для приложения OAuth2
У вас запросят пароль и сгенерят новый секретный код. Используйте Copy, чтобы скопировать секретный ключ клиента и сохранить его. Он понадобится при создании коннектора.
Нажмите Add Redirect и добавьте http://localhost/discord/redirect, как показано на рисунке 16.5. Нажмите кнопку Сохранить изменения.
Веб-страницs http://localhost/discord/redirect на самом деле не существует. Для этого шага можно применить любой URI, использующий localhost. Важно то, что localhost относится к локальному компьютеру, на котором работает локальный веб-сервер, как указано в разделе Настройка IIS (Internet Information Services).
Сейчас мы закончили подготовку среды. Теперь перейдем к созданию пользовательского коннектора или расширения.
Создание пользовательского коннектора
Файлы к этой главе доступны на репозитории GitHub. Мы выполним следующие шаги по созданию коннектора:
- Создадим проект расширения.
- Настроим аутентификацию.
- Настроим навигацию и содержимое.
Создание проекта расширения
Запустите Visual Studio Code. Откройте проводник (1), разверните раздел POWER QUERY SDK (2), создайте новый проект расширения, нажав кнопку Создать проект расширения (3):
Рис. 16.6. Создание проекта расширения
При появлении запроса введите TDGTPQM_Discord в поле Имя нового проекта и нажмите Enter. Откроется проводник Windows, найдите и выберите папку TDGTPQM_Discord. Создастся структура файлов и папок:
Рис. 16.6а. Структура файлов и папок проекта
Рассмотрим созданные файлы:
settings.json: файл JavaScript Open Notation (JSON), содержащий настройки рабочей области.
TDGTPQM_Discord.mez: файл содержится в каталоге bin\AnyCPU\Debug, это расширение, созданное с помощью пакета SDK для Power Query, которое вы развертываете и используете в качестве настраиваемого коннектора.
resources.resx: XML-файл, в котором хранятся строки, используемые в расширении.
TDGTPQM_Discord.pq: основной файл кода для пользовательского коннектора. Допускается использование только одного файла .pq, а его формат соответствует формату раздела M.
TDGTPQM_Discord.proj: XML-файл, содержащий настройки проекта.
TDGTPQM_Discord.query.pq: используется для создания тестовых запросов, он содержит код:
1 2 3 4 |
let result = TDGTPQM_Discord.Contents() in result |
Код ссылается на функцию TDGTPQM_Discord.Contents, определенную в TDGTPQM_Discord.pq (подробнее об этом позже). Этот код достаточен для наших целей и останется без изменений.
.png файлы: значки, используемые коннектором.
Чтобы написать пользовательский коннектор, мы будем работать с файлом TDGTPQM_Discord.pq, поэтому рассмотрим его более подробно.
TDGTPQM_Discord.pq
Пользовательский коннектор реализован в виде секции в соответствии со следующим кодом в верхней части файла:
1 2 3 |
// This file contains your Data Connector logic [Version = "1.0.0"] section TDGTPQM_Discord; |
В документах разделов показано, как расширения реализуются в M. Под определением этого раздела мы находим код:
1 2 3 4 5 6 7 |
[DataSource.Kind="TDGTPQM_Discord", Publish="TDGTPQM_Discord.Publish"] shared TDGTPQM_Discord.Contents = (optional message as text) => let _message = if (message <> null) then message else "Hello World", a = "Hello from HelloWorld: " & _message in a; |
TDGTPQM_Discord.Contents – это основная функция, которую мы используем для возврата содержимого коннектора. Обратите внимание на ключевое слово shared перед определением функции. Это означает, что на функцию можно ссылаться за пределами раздела.
Кроме того, обратите внимание, что DataSource.Kind определено как TDGTPQM_Discord. Использование ключевого слова shared вместе с определением литерала DataSource.Kind для функции связывает функцию с конкретным источником данных – TDGTPQM_Discord.
Следующий блок кода – это запись определения для источника данных:
1 2 3 4 5 6 7 8 9 |
// Data Source Kind description TDGTPQM_Discord = [ Authentication = [ // Key = [], // UsernamePassword = [], // Windows = [], Anonymous = [] ] ]; |
Запись определяет поддерживаемые типы проверки подлинности источника данных. Поддерживается только анонимная или неявная аутентификация.
Кроме литерального атрибута DataSource.Kind задан литеральный атрибут Publish, связанный с функцией TDGTPQM_Discord.Contents. Блок кода под записью определения источника данных – это определение записи Publish:
Описание публикации пользовательского интерфейса источника данных
1 2 3 4 5 6 7 8 9 |
// Data Source UI publishing description TDGTPQM_Discord.Publish = [ Beta = true, Category = "Other", ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") }, LearnMoreUrl = "https://powerbi.microsoft.com/", SourceImage = TDGTPQM_Discord.Icons, SourceTypeImage = TDGTPQM_Discord.Icons ]; |
Запись Publish используется пользовательским интерфейсом (UI) Power Query (например, редактором Power Query) для отображения расширения источника данных в диалоговом окне Get Data. Эти параметры можно настроить для изменения поведения определенных элементов пользовательского интерфейса, таких как категория, в которой отображается коннектор, находится ли коннектор в бета-версии или нет, а также ссылка Дополнительные сведения для коннектора.
Запись Publish ссылается на дополнительную запись TDGTPQM_Discord.Icons, которая содержит ссылки на файлы .png изображений:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
TDGTPQM_Discord.Icons = [ Icon16 = { Extension.Contents("TDGTPQM_Discord16.png"), Extension.Contents("TDGTPQM_Discord20.png"), Extension.Contents("TDGTPQM_Discord24.png"), Extension.Contents("TDGTPQM_Discord32.png") }, Icon32 = { Extension.Contents("TDGTPQM_Discord32.png"), Extension.Contents("TDGTPQM_Discord40.png"), Extension.Contents("TDGTPQM_Discord48.png"), Extension.Contents("TDGTPQM_Discord64.png") } ]; |
Это значки, отображаемые в пользовательском интерфейсе Power Query для коннектора.
На этом мы завершаем наше исследование файла TDGTPQM_Discord.pq. Вы можете быть удивлены небольшим объемом кода, необходимым для создания пользовательского коннектора; Не заблуждайтесь, проект расширения по умолчанию, созданный пакетом SDK для Power Query, уже является функционирующим пользовательским коннектором. Тем не менее, этот разъем на самом деле не делает ничего, кроме того, что по сути возвращает эквивалент Hello World.
Настройка аутентификации
Теперь, когда проект базового расширения создан, пришло время обратить внимание на настройку расширения в соответствии с потребностями подключения к Discord. И первая задача – написать код и настроить аутентификацию для коннектора. Discord использует протокол безопасности OAuth для аутентификации. Существуют и другие типы аутентификации: анонимный, AAD, логин и пароль, Windows, ключ. Каждый из этих механизмов аутентификации уникален. Подробную информацию об этих типах аутентификации можно найти здесь (версия на русском языке).
Аутентификация OAuth на сегодняшний день является самым сложным методом аутентификации для расширений Power Query, поэтому она была выбрана для этой книги в качестве примера. Как упоминалось ранее, OAuth расшифровывается как Open Authentication и является открытым стандартом, который позволяет пользователям предоставлять ограниченный доступ к веб-сайту или сервису сторонним приложениям. В нашем случае пользователь предоставляет стороннему коннектору ограниченный доступ к своей информации в Discord.
Протокол OAuth обеспечивает доступ без необходимости делиться своими учетными данными (именами пользователей и паролями). OA OAuth предоставляет пользователям безопасный и стандартизированный способ авторизации внешних приложений для доступа к данным или выполнения действий от их имени.
Вместо логинов и паролей OAuth использует секретный клиентский ID и токены для доступа приложения к службе, предоставляющей ресурсы. Реализуя OAuth, поставщики услуг могут гарантировать, что учетные данные пользователей останутся защищенными и не будут доступны внешним приложениям. OAuth позволяет пользователям контролировать и отзывать доступ к своим ресурсам в любое время, обеспечивая повышенную безопасность и контроль над данными.
В целом, OAuth играет решающую роль в обеспечении безопасного и доступа к ресурсам, продвижении совместимости и повышении конфиденциальности пользователей в различных веб-приложениях и мобильных приложениях. Скорее всего, в прошлом вы сталкивались с запросами на авторизацию OAuth, хотя, возможно, не знали, что это такое.
Чтобы реализовать аутентификацию OAuth выполним следующие шаги:
- Добавим файлы с ID клиента и секретным клиентским ID.
- Добавим настройки конфигурации.
- Создадим функцию OAuth.
- Изменим определение источника данных.
- Добавим учетные данные.
- Проверим соединение.
Добавление файлов ID клиента и секретного клиентского ID
Первым шагом является добавление файлов для хранения идентификатора клиента и секретного клиентского ID. Открыв проектd Visual Studio Code, пройдите File –> New file. При появлении запроса введите client_id в качестве имени файла, нажмите Enter. Проводник Windows откроется в папке TDGTPQM_Discord. Нажмите Create File. Поскольку такой файл поставляется в наборе GitHub, подтвердите, что вы хотите перезаписать файл.
Файл client_id автоматически открывается в Visual Studio Code. В строке 1 просто вставьте идентификатор клиента, который вы сохранили ранее, работая в разделе конфигурации Discord OAuth.
Повторите эти шаги, для файла с именем client_secret и значения секретного клиентского ID.
Файлы, которые вы только что создали, будут использоваться как часть конфигурации OAuth для пользовательского коннектора, поэтому давайте сразу добавим эти параметры.
Добавление настроек конфигурации
Конфигурационные настройки – это переменные, определенные в пользовательском коннекторе. Эти переменные можно рассматривать как глобальные переменные в том смысле, что любое выражение или функция, определенная в пользовательском коннекторе, может получать доступ к этим переменным и использовать их.
Чтобы добавить параметры конфигурации, в верхней части файла TDGTPQM_Discord.pq за строкой TDGTPQM_Discord; вставьте следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// OAuth Configuration Settings client_id = Text.FromBinary(Extension.Contents("client_id")); client_secret = Text.FromBinary(Extension.Contents("client_secret")); redirect_uri = "http://localhost/discord/redirect"; authorize_uri = "https://discord.com/api/oauth2/authorize?"; token_uri = "https://discord.com/api/oauth2/token"; // Static variables api_uri = "https://discord.com/api"; // Connector window windowWidth = 1200; windowHeight = 1000; |
Подробнее о коде:
- client_id: считывается файл client_id, созданный ранее.
- client_secret: считывается файл client_id, созданный ранее.
- redirect_uri: здесь хранится URI перенаправления. Он должен совпадать с URI, который мы использовали ранее при настройке OAuth Discord.
- authorize_uri: это значение для URI авторизации OAuth в Discord.
- token_uri: содержит значение URI Discord, предоставляющего токены.
- api_uri: здесь хранится базовый URI Discord API.
- WindowWidth: определяет ширину окна для диалогового окна аутентификации.
- WindowHeight: определяет высоту окна для диалогового окна аутентификации.
Теперь, когда у нас есть эти конфигурационные переменные/настройки, мы можем перейти к реализации функций, определенных типом аутентификации OAuth Power Query.
Создание функций OAuth
Тип аутентификации OAuth в Power Query требует реализации двух полей: StartLogin и FinishLogin. Также можно реализовать два необязательных поля: Обновить (Refresh) и Выйти (Logout). В каждом из этих полей должна быть указана функция, определенная в пользовательском коннекторе. Мы реализуем только обязательные поля/функции.
Между определением записи источника данных TDGTPQM_Discord и определением записи TDGTPQM_Discord.Publish вставьте следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// // OAuth2 flow definition // StartLogin = (resourceUrl, state, display) => let AuthorizeUrl = authorize_uri & Uri.BuildQueryString( [ client_id = client_id, redirect_uri = redirect_uri, response_type = "code", scope = "identify email connections guilds guilds.members. read " ] ) in [ LoginUri = AuthorizeUrl, CallbackUri = redirect_uri, WindowHeight = windowHeight, WindowWidth = windowWidth, Context = null ]; FinishLogin = (context, callbackUri, state) => let Parts = Uri.Parts(callbackUri)[Query] in TokenMethod(Parts[code]); TokenMethod = (code) => let Response = Web.Contents( token_uri, [ Content = Text.ToBinary( Uri.BuildQueryString( [ client_id = client_id, client_secret = client_secret, grant_type = "authorization_code", code = code, redirect_uri = redirect_uri ] ) ), Headers = [#"Content-type" = "application/x-www-formurlencoded", #"Accept" = "application/json"] ] ), Parts = Json.Document(Response) in Parts; |
Работа аутентификации OAuth в Power Query не прозрачна. Майкрософт предоставляет скудные объяснения / документацию, лишь код примеров реализации. Приведенный здесь код основан на коннекторе GitHub от Microsoft, который можно найти здесь.
Определения функций StartLogin и FinishLogin будут различаться в зависимости от спецификаций каждого API. Эти функции реализуют информацию и структуры, необходимые для API Discord. Давайте начнем с более подробного рассмотрения функции StartLogin.
Функция StartLogin начинает процесс аутентификации OAuth, создавая URI авторизации, ожидаемый API Discord. Это значение хранится в выражении AuthorizationUrl. По сути, это выражение просто создает универсальный код ресурса (URI) с определенными параметрами строки запроса. Фактический URI выглядит следующим образом: https://discord.com/api/oauth2/authorize?client_ id=1128020929558098050&redirect_uri=http%3A%2F%2Flocalhost%2Fdiscord%2Fredirect&respo nse_type=code&scope=identify%20guilds%20guilds.members.read%20email%20connections.
Обратите внимание, что базовый URI авторизации берется из нашей переменной authorize_uri, определенной в разделе Добавление настроек конфигурации. Строка запроса содержит наш client_id (ваш будет другим) вместе с нашим redirect_uri, response_type кода и параметром области. Тип ответа кода сообщает API Discord, какой ответ на авторизацию мы хотим получить, а область определяет разрешения, которые мы хотим предоставить нашему стороннему пользовательскому коннектору.
Функция StartLogin возвращает запись, содержащую следующие поля:
- LoginUri: это фактический URI приведено выше
- CallbackUri: переменная конфигурации redirect_uri
- WindowHeight: наша конфигурационная переменная/настройка высоты окна
- WindowWidth: наша переменная/настройка конфигурации ширины окна
- Context: null
Чтобы лучше понять, как это работает, просто вставьте предоставленный ранее URI (AuthorizeUrl) в веб-браузер. Появится запрос на авторизацию в Discord, указывающий на то, что стороннее приложение запрашивает ряд разрешений:
Рис. 16.6б. Запрос разрешений в Discord
Этот список разрешений берется из параметра строки запроса scope универсального кода ресурса (URI). Нажмите Авторизовать. Теперь вы будете перенаправлены на указанный redirect_uri, http://localhost/discord/redirect. Эта страница на самом деле не существует, поэтому ваш локальный веб-сервер возвращает страницу 404 Not Found. Тем не менее, внимательно посмотрите на URI в браузере; URI будет выглядеть примерно так: http://localhost/discord/redirect?code=TN8Va9aHuFvfJtd0ObF9A9kN5WJP17.
Обратите внимание на параметр code в строке запроса. Поскольку мы запросили response_type кода, это код, возвращенный сервером Discord. Затем мы используем этот код в функции FinishLogin для генерации токена доступа и завершения процесса авторизации. Это делается путем подачи кода (Parts[code]) в функцию TokenMethod.
По сути, метод FinishLogin получает ответ от нашего redirect_uri и передает значение строкового параметра запроса кода в функцию TokenMethod. Функция TokenMethod завершает процесс получения маркера доступа OAuth.
Чтобы лучше понять этот процесс, мы можем использовать Postman (postman.com) для эмуляции поведения, как показано на следующем снимке экрана. Postman – это платформа API, которая включает в себя инструменты, которые обычно используются для эмуляции сложных взаимодействий API, таких как аутентификация. D этом запросе Postman мы выбрали операцию POST (в отличие от GET):
Рис. 16.7. Получение токена доступа OAuth через Postman
Наш пользовательский соединитель ведет себя так же, потому что мы включили элемент Headers в вызов функции Web.Contents в функцию TokenMethod. На рисунке 16.7 мы указали сообщение для https://discord.com/api/oauth/token URI, которое совпадает с token_uri, которое мы используем в нашем пользовательском коннекторе.
В теле нашего запроса Postman мы указали x-www-form-urlencoded, то же самое, что реализовано в наших заголовках для типа Content в функции TokenMethod. В этом теле мы кодируем следующую информацию:
- client_id
- client_secret
- grant_type: для этого параметра устанавливается значение authorization_code в соответствии с требованиями API Discord OAuth.
- redirect_uri: наш URI перенаправления, определенный в разделе конфигурации Discord OAuth, http://localhost/discord/redirect.
- code: это код, возвращаемый при первоначальном вызове авторизации API Discord. В нашем пользовательском коннекторе это код, сгенерированный и помещенный в строку запроса redirect_uri. Если вы вставили пример URI (AuthorizationUrl), то это будет код: http://localhost/discord/redirect?code=TN8Va9aHuFvfJtd0ObF9A9kN5WJP17.
Каждая из этих частей информации также закодирована в нашем запросе, сделанном в функции TokenMethod.
На рис. 16.7 также есть ответ, предоставленный Discord. URI токена Discord отвечает следующей информацией в формате JSON:
- token_type: тип возвращаемого токена, в данном случае Bearer
- access_token: маркер доступа Bearer
- expires_in: когда истекает срок действия токена
- refresh_token: токен для обновления нашего токена
- scope: область действия маркера доступа
Обратите внимание на то, что в функции TokenMethod мы ожидаем, что в качестве ответа будет возвращен Json.Document (Response).
Хотя специфика параметров, передаваемых в запросах, различается в разных реализациях API OAuth, в целом предоставленный код M должен работать с минимальными изменениями для большинства сценариев аутентификации OAuth. К счастью для нас, тип аутентификации OAuth в Power Query с этого момента берет на себя большую часть тяжелой работы.
Например, если бы мы продолжили наш пример в Postman, то для каждого последующего запроса API (например, получения списка серверов для пользователя) нам нужно было бы включить токен доступа в параметр Authorization со значением «Bearer <access_token>». Тем не менее, тип аутентификации OAuth Power Query автоматически обрабатывает включение необходимых заголовков для нас во время последующих запросов API.
Теперь, когда мы реализовали необходимые функции типа аутентификации OAuth, мы можем изменить определение записи источника данных, изменив тип аутентификации с анонимного на OAuth.
Изменение определения записи источника данных
Найдите запись определения источника данных, TDGTPQM_Discord. Замените определение записи кодом:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Data Source Kind description TDGTPQM_Discord = [ TestConnection = (dataSourcePath) => { "TDGTPQM_Discord.Contents" }, Authentication = [ OAuth = [ StartLogin = StartLogin, FinishLogin = FinishLogin ] ] ]; |
Обратите внимание, мы указываем тип аутентификации OAuth, предоставленный в виде записи, которая указывает функции StartLogin и FinishLogin для соответствующих полей типа аутентификации OAuth.
Теперь, когда тип аутентификации OAuth определен, пришло время протестировать аутентификацию OAuth для пользовательского коннектора. Для этого мы должны сначала добавить учетные данные.
Добавление учетных данных
Добавление учетных данных в Visual Studio Code имитирует поведение пользовательского интерфейса Power Query (например, редактора Power Query) при входе в систему или проверке подлинности для подключения к источнику данных. Чтобы добавить учетные данные:
- Если вы еще этого не сделали, в меню Visual Studio Code пройдите File –> Save All.
- Разверните POWER QUERY SDK в области обозревателя в левой части Visual Studio Code.
- Нажмите кнопку Run TestConnection function. Появится сообщение об ошибке:
Рис. 16.7а. Сообщение об ошибке
- Нажмите Evaluate current file (Оценить текущий файл). Вы снова получите сообщение об ошибке.
- Нажмите Set credentials (Установить учетные данные).
- При появлении запроса выберите из списка тип источника данных TDGTPQM_Discord.
- При появлении запроса выберите из списка файл запросаquery.pq.
- При появлении запроса выберите из списка метод проверки подлинности OAuth.
- Обратите внимание, что если вместо этого вы видите в списке только Anonymous, это одна из многих причуд Visual Studio Code. Попробуйте сохранить свою работу и выполнить предыдущие шаги еще раз. Если это не помогло, выберите File –> Clouse Folder, сохраните свою работу, если будет предложено, а затем снова откройте папку с File –> Open Folder. После повторного открытия папки разверните раздел POWER QUERY SDK на панели Проводник и повторите шаги.
- Должно отобразиться окно, подобное следующему:
Рис. 16.8. Диалоговое окно авторизации Discord
Нажмите Авторизовать. В коде вы получить сообщение:
Рис. 16.8а. Новые учетные данные OAuth успешно сгенерированы
Теперь, когда у нас есть учетные данные, мы можем проверить соединение.
Проверка подключения
Несмотря на то, что пользовательский коннектор по-прежнему не делает ничего, кроме того, что по сути отвечает вездесущим текстом Hello World, тестирование соединения на данном этапе подтверждает наличие учетных данных OAuth. Чтобы проверить подключение:
- Разверните раздел POWER QUERY SDK в области проводника.
- Нажмите кнопку Run TestConnection function. На этот раз ошибка не отображается. В коде появляется строка «Message»: «TestConnection succeeded».
- Убедитесь, что в области проводника выбран файл pq, и нажмите Оценить текущий файл. На этот раз в правой верхней части экрана появляется вкладка результатов PQTest result.
Рис. 16.8б. Результат тестирования
После того, как наше пользовательское соединение будет успешно протестировано, подтвердив, что наши учетные данные OAuth созданы и присутствуют в Visual Studio Code, мы можем перейти к тому, чтобы наш пользовательский соединитель получал данные из Discord API.
Настройка навигации и содержимого
Теперь пришло время начать делать наш пользовательский коннектор, чтобы он фактически получал и представлял данные из Discord.
Если рассматривать пользовательский опыт в редакторе Power Query при подключении к источнику данных, то процесс выглядит следующим образом:
- Вы кликаете Получить данные, а затем выбираете коннектор источника данных.
- Входите в систему или выполняете аутентификацию в соответствии с предложенными вариантами.
- Часто появляется диалоговое окно Навигатор, где можно просматривать папки и таблицы для получения предварительных данных.
- Вы выбираете таблицы данных в окне Навигатор и жмете ОK для получения данных.
Мы уже реализовали тип аутентификации OAuth. Для реализации остальных шагов в коде M мы по сути должны реализовать этот процесс в обратном порядке:
- Добавить функции для получения данных через вызовы API: эти функции будут реализовывать получение данных из API Discord. Это позволит отображать данные в панели предварительного просмотра диалогового окна Навигатор, а также загружать данные в редактор Power Query.
- Добавить функции навигации: эти функции будут отвечать за отображение диалогового окна Навигатор и связывать навигацию с соответствующими операциями получения данных (функциями вызова API), реализованными на шаге 1.
- Изменить функцию TDGTPQM.Contents: завершить реализацию диалогового окна Навигатор модифицируя функцию Contents для инициализации навигации.
Начнем с первого шага этого процесса.
Добавление функций вызова API для получения данных
Для пользовательского коннектора Discord мы хотим реализовать три различных операции получения данных, связанные со следующими аспектами:
- Информация, связанная с идентификацией пользователя.
- Информация о серверах или гильдиях, к которым принадлежит пользователь.
- Информация, связанная с личностью пользователя на каждом сервере/гильдии.
Информация, полученная таким образом, довольно проста и не иметь большой ценности. Тем не менее, этот базовый коннектор может быть расширен для получения количества сообщений, отправленных на каждый сервер/гильдию, и так далее.
Чтобы реализовать указанных операций извлечения данных, в Visual Studio Code в файле TDGTPQM_Discord.pq непосредственно над определением типа источника данных, TDGTPQM_Discord, вставьте следующие три функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// ** Identity TDGTPQM_Discord.GetIdentity = () as table => let apiCall = Json.Document( Web.Contents( api_uri, [ RelativePath = "users/@me" ] ) ), output = Table.FromRecords({ apiCall }) in output; // ** Servers (Guilds) TDGTPQM_Discord.GetGuilds = () as table => let apiCall = Json.Document( Web.Contents( api_uri, [ RelativePath = "users/@me/guilds" ] ) ), output = Table.FromList(apiCall, Splitter.SplitByNothing(), null, null, ExtraValues.Error) in output; // ** Member User TDGTPQM_Discord.GetGuildMember = (guildid as text) as table => let apiCall = Json.Document( Web.Contents( api_uri, [ RelativePath = "users/@me/guilds/" & guildid & "/member" ] ) ), output = Table.FromRecords({ apiCall }) in output; |
Эти функции очень похожи. TDGTPQM_Discord.GetIdentity извлекает информацию, связанную с личностью пользователя. Она возвращает таблицу и не имеет параметров. Считывая выражение apiCall в обратном направлении, мы создаем URI, состоящий из нашего базового api_uri, созданного выше в разделе Добавление параметров конфигурации, и относительного пути users/@me. Затем этот URI становится https://discord.com/api/users/@me.
Мы используем Web.Contents для получения ответа от этого вызова URI и указываем, что это содержимое является Json.Document. Затем выходное выражение извлекает информацию в этом документе JSON с помощью функции Table.FromRecords.
Функция TDGTPQM.GetGuilds получает информацию о серверах или гильдиях на языке Discord API, к которым принадлежит пользователь. Эта функция также не принимает параметров и возвращает таблицу. Вызов API выполняется к следующему URI: https://discord.com/api/users/@me/guilds. Выходное выражение немного отличается. Вместо обработки записей мы обрабатываем список с помощью функции Table.FromList.
Вы можете спросить, как мы можем узнать, как обрабатывать ответ на вызов API и следует ли использовать Table.FromRecords или Table.FromList. Следует внимательно читать документацию, предоставленную API, который мы вызываем. Тем не менее, хорошим приемом является использование Postman для создания и просмотра ответа. Для этого:
- Вставьте AuthorizationUrl в веб-браузер и нажмите Enter. Этот URI имеет вид https://discord. com/api/oauth2/authorize?client_ id=1128020929558098050&redirect_uri=http%3A%2F%2Flocalhost%2Fdiscord%2Fredire ct&response_type=code&scope=identify%20guilds%20guilds.members.read%20email%20 connections
- Скопируйте значение строкового параметра запроса кода, возвращенное в составе redirect_uri.
- Как показано на рисунке 16.7, используйте Postman для запроса маркера доступа, используя код в качестве одного из закодированных параметров.
- Используйте этот токен доступа в другом запросе Postman к вызову API:
Рис. 16.9. Вызов Discord API с использованием Postman
Мы взяли запрос GET к конечной точке Discord API https://discord.com/api/users/@me/guilds. Мы включили параметр Authorization в наши заголовки со значением «Bearer <токен доступа>». На нижней панели мы можем просмотреть ответ, возвращенный вызовом API.
- Скопируйте полученный ответ и вставьте эти данные в файл.
- Используйте пользовательский интерфейс редактора Power Query для извлечения и расширения/обработки информации в файле.
- Просмотрите код M, сгенерированный в расширенном редакторе.
- Скопируйте соответствующий код M из Расширенного редактора и вставьте этот код в качестве выходного выражения для нашей функции.
Надеемся, это поможет вам понять, как создать код M для функций извлечения данных. Давайте закончим этот раздел, кратко взглянув на функцию извлечения данных TDGTPQM_Discord.GetGuildMember.
Функция TDGTPQM_Discord.GetGuildMember извлекает информацию, связанную с личностью пользователя на каждом сервере/гильдии. Эта функция возвращает таблицу и принимает один параметр, guildid. Этот параметр используется в вызове API для указания сервера/гильдии, с которой будет получена информация о пользователе. Вызов API имеет следующий формат: https://discord.com/api/ users/@me/guilds/<guildid>/member.
Выходное выражение использует Table.FromRecords для преобразования ответа в таблицу.
На этом мы завершаем описание функций извлечения данных из вызова API. Теперь мы можем перейти к функциям, которые обеспечат нашу навигацию в диалоговом окне Навигатор в пользовательском интерфейсе редактора Power Query.
Добавление функций навигации
После того как функции извлечения данных из вызова API закодированы, мы теперь можем реализовать функции, которые представляют взаимодействие с пользователем в диалоговом окне Навигатор пользовательского интерфейса редактора Power Query.
Чтобы реализовать функции навигации, нам сначала нужно написать вспомогательную функцию: Table.ToNavigationTable. Код для этой функции взят из примера кода Microsoft. Добавьте следующую функцию в конец кода пользовательского коннектора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// Navigation Tables Functions Table.ToNavigationTable = ( table as table, keyColumns as list, nameColumn as text, dataColumn as text, itemKindColumn as text, itemNameColumn as text, isLeafColumn as text ) as table => let tableType = Value.Type(table), newTableType = Type.AddTableKey(tableType, keyColumns, true) meta[ NavigationTable.NameColumn = nameColumn, NavigationTable.DataColumn = dataColumn, NavigationTable.ItemKindColumn = itemKindColumn, Preview.DelayColumn = itemNameColumn, NavigationTable.IsLeafColumn = isLeafColumn ], navigationTable = Value.ReplaceType(table, newTableType) in navigationTable; |
Эта функция преобразует таблицу путем добавления метаданных, ожидаемых пользовательским интерфейсом диалогового окна Навигатор интерфейсе Power Query. Метаданные включают:
- NameColumn
- DataColumn
- ItemKindColumn
- DelayColumn
- IsLeafColumn
В настоящее время необходимо вручную добавить эту функцию в код. Но Microsoft указала, что эта функция может стать частью стандартной библиотеки M. Функция принимает аргументы:
- table: таблица, которую вы хотите преобразовать в таблицу навигации.
- keyColumns: столбцы первичного ключа (уникальных значений) для таблицы.
- nameColumn: столбец, используемый для отображения имени в диалоговом окне Навигатор.
- dataColumn: столбец, содержащий таблицу или функцию, возвращающую таблицу. Это те данные, которые будут получены при выборе в диалоговом окне Навигатор.
- itemKindColumn – столбец, указывающий тип значка для отображения в диалоговом окне Навигатор. Значения в этом столбце могут быть следующими: Cube, CubeDatabase, CubeView, CubeViewFolder, Database, DatabaseServer, DefinedName, Dimension, Feed, Folder, Function, Record, Sheet, Subcube, Table, View
- itemNameColumn – столбец, указывающий способ обработки предварительного просмотра данных в диалоговом окне Навигатор. Чаще всего значение в itemKindColumn совпадает со значением в itemNameColumn.
- isLeafColumn: столбец, указывающий, можно ли развернуть элемент или нет. Этот столбец должен содержать логическое или двоичное значение.
Дополнительную информацию об этой функции можно найти здесь (русская версия).
Далее над функцией TDGTPQM_Discord.GetIdentity добавим следующие три функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
TDGTPQM_Discord.UsersNavigation = () as table => // Navigation //** Users Navigation let objects = #table({ "Name", "Key", "Data", "ItemKind", "ItemName", "IsLeaf" }, { { "Me", "me", TDGTPQM_Discord.GetIdentity(), "Record", "Record", true } }), Navigation = Table.ToNavigationTable(objects, { "Key" }, "Name", "Data", "ItemKind", "ItemName", "IsLeaf") in Navigation; TDGTPQM_Discord.GuildsNavigation = () as table => // Navigation //** Guilds (Server) Navigation let objects = #table({ "Name", "Key", "Data", "ItemKind", "ItemName", "IsLeaf" }, { { "Servers", "guilds", TDGTPQM_Discord.GetGuilds(), "Table", "Table", true } }), Navigation = Table.ToNavigationTable(objects, { "Key" }, "Name", "Data", "ItemKind", "ItemName", "IsLeaf") in Navigation; TDGTPQM_Discord.MemberNavigation = () as table => // Navigation //** Membership Navigation let servers = Table.ExpandRecordColumn(TDGTPQM_Discord.GetGuilds(), "Column1", { "name", "id" }, { "Name", "Key" }), addDataColumn = Table.AddColumn(servers, "Data", each TDGTPQM_Discord.GetGuildMember([Key])), addItemKindColumn = Table.AddColumn(addDataColumn, "ItemKind", each "Record"), addItemNameColumn = Table.AddColumn(addItemKindColumn, "ItemName", each "Function"), addIsLeafColumn = Table.AddColumn(addItemNameColumn, "IsLeaf", each true), Navigation = Table.ToNavigationTable(addIsLeafColumn, { "Key" }, "Name", "Data", "ItemKind", "ItemName", "IsLeaf") in Navigation; |
Первые две функции, TDGTPQM_Discord.UsersNavigation и TDGTPQM_Discord.GuildsNavigation, почти идентичны. В обоих есть выражение objects, определяющее таблицу со столбцами Name, Key, Data, ItemKind, ItemName, IsLeaf.
Для каждого из них определен один навигационный конечный узел. Функция TDGTPQM_Discord.UsersNavigation определяет следующие значения для одной строки в таблице: Me, me, TDGTPQM_Discord.GetIdentity(), Record, Record, true.
Согласно этому определению, слово Me появится в диалоговом окне Навигатор для этого узла. Уникальное имя или ключ для узла – это me. Данные, полученные при выборе этого узла, – это данные, полученные функцией извлечения данных из вызова API TDGTPQM_Discord.GetIdentity. Значок и предварительный просмотр указываются как запись и, наконец, значение true указывает, что это конечный узел (конечная точка), а не расширяемая папка.
Функция TDGTPQM_Discord.GuildsNavigation почти идентична, но указывает значение столбца Data функции извлечения данных вызова API, TDGTPQM_Discord.GetGuilds. Мы также указываем, что иконка и предварительный просмотр данных должны обрабатываться как таблица, а не запись.
В обоих случаях эта таблица затем передается в функцию Table.ToNavigationTable с дополнительными параметрами, ссылающимися на соответствующие столбцы в таблице объектов.
Функция TDGTPQM_Discord.MemberNavigation представляет собой более сложную динамическую навигацию. Эта функция использует функцию извлечения данных из API TDGTPQM_Discord.GetGuilds для получения списка серверов, к которым принадлежит пользователь. Возвращаемые записи расширяются для столбцов name и id, возвращенных вызовом API, и переименовываются в Name и Key соответственно. Затем мы добавляем дополнительные столбцы, необходимые для функции Table.ToNavigationTable, включая столбец Data, в котором мы вызываем функцию TDGTPQM_Discord.GetGuildMember, передавая Key (id сервера/гильдии) в качестве единственного параметра. Мы также указываем столбец ItemKind, в котором указываем значение Record, и столбец IsLeaf, в котором логическое значение равно true.
Для столбца ItemName вместо того же значения, которое используется для столбца ItemKind (Record), мы указываем значение Function. Это необходимо, потому что это динамически генерируемая таблица навигации.
На этом мы завершаем наше объяснение функций навигационной таблицы. Теперь мы готовы завершить работу над пользовательским коннектором, изменив функцию TDGTPQM_Discord.Contents.
Изменение функции содержимого
Нам нужно изменить функцию TDGTPQM_Discord.Contents, чтобы представить верхний уровень навигации, который будет отображаться в диалоговом окне Навигатор пользовательского интерфейса Power Query. Вместо возврата одной текстовой строки мы создадим диалоговое окно Навигатор, которое позволит выбрать данные, которые мы хотим получить из Discord. Для этого найдите функцию TDGTPQM_Discord.Contents в коде коннектора. Замените ее на код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
shared TDGTPQM_Discord.Contents = () => let objects = #table({ "Name", "Key", "Data", "ItemKind", "ItemName", "IsLeaf" }, { { "Users", "usersFolder", TDGTPQM_Discord. UsersNavigation(), "Folder", "usersFolder", false }, { "Servers", "guildsFolder", TDGTPQM_Discord. GuildsNavigation(), "Folder", "guildsFolder", false }, { "Membership", "membershipFolder", TDGTPQM_Discord. MemberNavigation(), "Folder", "membershipFolder", false } }), Navigation = Table.ToNavigationTable(objects, { "Key" }, "Name", "Data", "ItemKind", "ItemName", "IsLeaf") in Navigation; |
Код очень похож на наши первые две функции навигации, TDGTPQM_Discord.UserNavigation и TDGTPQM_Discord.GuildsNavigation. Мы снова определяем выражение objects, которое возвращает таблицу со столбцами, ожидаемыми функцией Table.ToNavigationTable. Каждая строка в этой таблице содержит имя (Name) и уникальный ключ (Key), а также указание типа ItemFolder = Folder, ItemName = Folder и IsLeaf = false. Для столбца Data мы указываем значения соответствующих функций навигационной таблицы: TDGTPQM_Discord.UserNavigation, TDGTPQM_Discord.GuildsNavigation, TDGTPQM_Discord.MemberNavigation.
Как и в случае с функциями навигации, мы передаем эту таблицу объектов в нашу функцию Table.ToNavigationTable в качестве выходных данных функции TDGTPQM_Discord.Contents.
Теперь, когда мы завершили кодирование пользовательского коннектора, мы готовы установить и начать использовать этот коннектор в Power BI Desktop.
Установка и использование пользовательского коннектора
Откройте проект в Visual Studio Code, сохраните работу, пройдя File –> Save All. Разверните раздел POWER QUERY SDK в области EXPLORER. Нажмите Run TestConnection function и убедитесь, что ошибки не отображаются. В области EXPLORER выберите файл TDGTPQM.pq и нажмите Evaluate current file. На вкладке результатов PQTest должна отображаться следующая информация:
Рис. 16.10. Результат оценки TDGTPQM.pq
Это верхний уровень того, как навигация будет отображаться в диалоговом окне Навигатор пользовательского интерфейса Power Query и напрямую соответствует спецификациям выражения объектов из функции TDGTPQM_Discord.Contents.
На панели EXPLORER щелкните ссылку папки bin\AnyCPU\Debug, чтобы развернуть ее. Щелкните правой кнопкой мыши файл TDGTPQM_Discord.mez и выберите Reveal in File Explorer. Скопируйте файл TDGTPQM_Discord.mez. Находясь в проводнике Windows, перейдите в каталог Документы. Создайте папку с именем Microsoft Power BI Desktop. Перейдите в папку Microsoft Power BI Desktop. Создайте папку с именем Custom Connectors. Перейдите в папку Custom Connectors. Вставьте файл TDGTPQM_Discord.mez. Откройте Power BI Desktop. Пройдите Файл –> Параметры и настройки –> Параметры. Перейдите на закладку Безопасность. В разделе Расширения данных выберите переключатель Разрешить загрузку любого расширения без проверки и предупреждений (не рекомендуется. Нажмите OK. Перезапустите Power BI Desktop.
Откройте Power BI Desktop. На вкладке Главная выберите Преобразовать данные, чтобы открыть редактор Power Query. На вкладке Главная кликните Создать источник. В строке поиска введите tdgtpqm. Выберите разъем TDGTPQM_Discord (бета-версия) и нажмите Подключить. Отобразится диалоговое окно:
Рис. 16.11. Уведомление о подключении стороннего сервиса
Коннектор TDGTPQM_Discord зависит от стороннего сервиса и все еще находится в разработке. Пожалуйста, попробуйте и оставьте нам отзыв. Мы не можем гарантировать, что в финальной версии он будет работать так же. Будущие изменения могут привести к тому, что ваши запросы станут несовместимыми.
Узнайте больше о сервисе, используемом для коннектора TDGTPQM Discord
□ Больше не предупреждать меня об этом разъеме
Нажмите Продолжить и Sign-in. Авторизуйте приложение, нажав Авторизовать в диалоговом окне. Нажмите Подключить. Откроется диалоговое окно Навигатор:
Рис. 16.12. TDGTPQM._Discord диалоговое окно Навигатора пользовательского коннектора в редакторе Power Query
Поздравляю! Вы успешно создали пользовательский коннектор для Power Query, который подключается к API Discord и извлекает данные из него. Этот коннектор можно использовать аналогично коннекторам по умолчанию, включенным в Power BI Desktop.
Имейте в виду, что это всего лишь один из примеров создания пользовательского коннектора для получения данных из API. Существуют сотни, если не тысячи, пользовательских API, из которых вы можете извлекать данные. Научившись программировать пользовательские коннекторы для Power Query, вы узнаете, сколько и к каким типам источников данных можно подключиться.
Сводка
В этой главе мы погрузимся в мир расширения возможностей Power Query путем добавления библиотеки пользовательских функций и создания пользовательского коннектора. Power Query – это мощное средство преобразования и интеграции данных, но его истинный потенциал можно раскрыть, разработав собственные коннекторы для доступа к специализированным источникам данных или API. Это позволяет получать данные из уникальных систем или служб, которые изначально не поддерживаются Power Query.
Мы начали с подготовки среды разработки, включая настройку Visual Studio Code, Power Query SDK и элементов Discord, нашего целевого источника данных. Далее мы создали и объяснили базовый шаблон для пользовательских расширений Power Query. Затем мы закодировали и настроили аутентификацию OAuth. Мы написали пользовательский код M для получения данных из API Discord и навигации по окну Навигатор в интерфейсе Power Query. Мы показали, как установить пользовательский коннектор в Power BI Desktop.
На протяжении всей главы мы подробно объясняли процесс разработки и анатомию пользовательского коннектора, разбивая его компоненты и код, чтобы объяснить его назначение. Мы рассмотрели, как определить метаданные коннектора, установить подключения, обработать аутентификацию и реализовать логику извлечения и преобразования данных.
Если вы прочитали эту книгу целиком, вы узнали огромное количество информации о языке М, от основных элементов языка до абстрактных концепций, лежащих в основе М. Попутно мы привели множество примеров того, как использовать M для решения задач преобразования данных, а также лучшие практики кодирования M. Мы искренне надеемся, что вам понравилось это путешествие и вы почувствуете в себе силы использовать возможности языка M.