将应用程序连接到 GraphQL Fabric API

注意

Microsoft Fabric API for GraphQL 处于预览阶段。

若要将应用程序连接到 API for GraphQL,需要三个重要信息:客户端 ID租户 ID 和 Fabric 中的 GraphQL 终结点地址。 在以下部分中,我们将展示如何创建和检索所需的所有详细信息,以及如何使用示例应用程序访问 API。

先决条件

创建 Microsoft Entra 应用

在以下步骤中,我们将展示如何在 Microsoft Entra 中配置对 ReactJS 应用程序的支持。

  1. 使用 快速入门中所述的步骤注册应用程序:使用Microsoft标识平台注册应用程序。
  2. Microsoft Entra 应用“应用程序(客户端) ID”和“目录(租户) ID”值显示在“摘要”框中。 以后根据需要记录这些值。
  3. 管理 列表中,选择 API 权限,然后 添加权限
  4. 添加 PowerBI 服务,选择“委托的权限”,然后选择 Item.Execute.All 权限。 确保不需要管理员同意。
  5. 返回到“管理”列表,选择“身份验证”“添加平台”“单页应用程序”。>>
  6. 出于本地开发目的,请在“重定向 URI”下添加 http://localhost:3000,并确认应用程序是否已启用具有代码交换证明密钥 (PKCE) 的授权代码流 选择“创建”按钮以保存所做更改。 如果应用程序收到与跨原点请求相关的错误,请使用相同的重定向 URI 在上一步中添加“移动和桌面应用程序”平台。
  7. 返回到“授权”,向下滚动到“高级设置”,然后在“允许公共客户端流”下,对“启用以下移动和桌面流”选择“”。

为应用程序访问设置示例 GraphQL API

在本例中,我们创建了一个 GraphQL API 来向客户端公开示例湖屋数据。

  1. 在 Fabric 门户主页中,从工作负载列表中选择“数据工程”。

  2. 在数据工程体验中,选择“使用示例”,然后在“湖屋”下选择“公共节假日”,以自动创建新的包含公共假日数据的湖屋。

    选择示例数据湖屋选项的屏幕截图。

  3. 按照创建 API for GraphQL 中的步骤进行操作,创建新的 GraphQL API,然后选择创建的湖屋。 添加公共节假日表,以允许客户端访问此数据。

    将示例湖屋添加为 GraphQL 数据源的屏幕截图。

  4. 使用以下示例查询在 API 编辑器中测试 GraphQL API。 这与我们在 React 客户端应用程序中使用的查询相同:

     query {
       publicholidays (filter: {countryRegionCode: {eq:"US"}, date: {gte: "2024-01-01T00:00:00.000Z", lte: "2024-12-31T00:00:00.000Z"}}) {
         items {
           countryOrRegion
           holidayName
           date
         }
       }
     }
    
  5. 在 API 项的工具栏上选择复制终结点

    API 项工具栏选项的屏幕截图。

  6. 复制链接页面中,选择复制

    复制链接对话框屏幕的屏幕截图,显示在哪里选择复制。

  7. 正如之前记录的 Microsoft Entra 应用的客户端 ID 和租户 ID,稍后根据需要复制终结点 URI。

配置 React 应用以访问公共节假日 API

  1. 我们使用现有 React 应用作为起点。 按照教程创建 React 单页应用程序并准备将其用于身份验证中的所有步骤创建一个已配置 Microsoft Entra 身份验证的 React 项目,包括添加到项目结构所需的其他文件和文件夹。 我们只需要更改三个文件就可以使应用适应 GraphQL 用例。

  2. src 文件夹中,打开 authConfig.js 文件,并将文件的内容替换为以下代码片段:

     /*
      * Copyright (c) Microsoft Corporation. All rights reserved.
      * Licensed under the MIT License.
      */
    
     import { LogLevel } from "@azure/msal-browser";
    
     /**
      * Configuration object to be passed to MSAL instance on creation. 
      * For a full list of MSAL.js configuration parameters, visit:
      * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md 
      */
    
     export const graphqlConfig = {
         graphqlEndpoint: "`Enter_the_GraphQL_Endpoint_Here"
     };
    
     export const msalConfig = {
         auth: {
             clientId: "Enter_the_Application_Id_Here",
             authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here",
             redirectUri: "http://localhost:3000",
         },
         cache: {
             cacheLocation: "sessionStorage", // This configures where your cache will be stored
             storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
         },
         system: {	
             loggerOptions: {	
                 loggerCallback: (level, message, containsPii) => {	
                     if (containsPii) {		
                         return;		
                     }		
                     switch (level) {
                         case LogLevel.Error:
                             console.error(message);
                             return;
                         case LogLevel.Info:
                             console.info(message);
                             return;
                         case LogLevel.Verbose:
                             console.debug(message);
                             return;
                         case LogLevel.Warning:
                             console.warn(message);
                             return;
                         default:
                             return;
                     }	
                 }	
             }	
         }
     };
    
     /**
      * Scopes you add here will be prompted for user consent during sign-in. 
      * By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
      * For more information about OIDC scopes, visit: 
      * https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
      */
     export const loginRequest = {
         scopes: ["https://analysis.windows.net/powerbi/api/Item.Execute.All"]
     };
    
     /**
      * Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
      * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
      */
     export const graphConfig = {
         graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
     };
    

    正如上面的代码所示,请务必使用正确的范围来访问应用程序。 在本例中为 https://analysis.windows.net/powerbi/api/Item.Execute.All

    重要

    在 Microsoft Fabric API for GraphQL 预览版期间,范围可能会发生更改。

  3. 将以下值替换为 Microsoft Entra 管理中心的值。

    • clientId - 应用程序的标识符,也称为客户端。 将 Enter_the_Application_Id_Here 替换为先前在已注册 Microsoft Entra 应用程序的概述页面中记录的“应用程序(客户端) ID”值。
    • authority - 其由两个部分组成:
      • Instance 是云提供程序的终结点。 在“国家云”查看可用的不同终结点。
      • 租户 ID 是在其中注册应用程序的租户的标识符。 将 Enter_the_Tenant_Info_Here 替换为注册应用程序的概述页中先前记录的“目录(租户) ID”值。
    • graphQLEndpoint - Fabric API for GraphQL 终结点。 将 Enter_the_GraphQL_Endpoint_Here 替换为前面记录的 GraphQL API 终结点。
  4. 保存文件。

  5. 在相同的 src 文件夹中,打开 App.js 文件,并将文件的内容替换为以下代码片段:

     import React, { useState } from 'react';
     import { PageLayout } from './components/PageLayout';
     import { loginRequest, graphqlConfig } from './authConfig';
     import { ProfileData } from './components/ProfileData';
     import { AuthenticatedTemplate, UnauthenticatedTemplate, useMsal } from '@azure/msal-react';
     import './App.css';
     import Button from 'react-bootstrap/Button';
     import Spinner from 'react-bootstrap/Spinner';
    
     /**
     * Renders information about the signed-in user or a button to retrieve data about the user
     */
     const ProfileContent = () => {
       const { instance, accounts } = useMsal();
       const [graphqlData, setGraphqlData] = useState(null);
       const [display, setDisplay] = useState(false);
    
       function RequestGraphQL() {
           // Silently acquires an access token which is then attached to a request for GraphQL data
           instance
               .acquireTokenSilent({
                   ...loginRequest,
                   account: accounts[0],
               })
               .then((response) => {
                   callGraphQL(response.accessToken).then((response) => setGraphqlData(response));
               });
       }
    
     async function callGraphQL(accessToken) {
       setDisplay(true);
       const query = `query {
         publicholidays (filter: {countryRegionCode: {eq:"US"}, date: {gte: "2024-01-01T00:00:00.000Z", lte: "2024-12-31T00:00:00.000Z"}}) {
           items {
             countryOrRegion
             holidayName
             date
           }
         }
       }`;
       fetch(graphqlConfig.graphqlEndpoint, {
               method: 'POST',
               headers: {
                   'Content-Type': 'application/json',
                   'Authorization': `Bearer ${accessToken}`,
               },
               body: JSON.stringify({ 
                   query: query
               })
           })
           .then((res) => res.json())
           .then((result) => setGraphqlData(result));
     }
    
       return (
           <>
               <h5 className="card-title">Welcome {accounts[0].name}</h5>
               <br/>
               {graphqlData ? (
                   <ProfileData graphqlData={graphqlData} />
               ) : (
                   <Button variant="primary" onClick={RequestGraphQL}>
                       Query Fabric API for GraphQL Data 
                       {display ? (
                             <Spinner
                                 as="span"
                                 animation="border"
                                 size="sm"
                                 role="status"
                                 aria-hidden="true"
                             />
                         ) : null}
                   </Button>
               )}
           </>
       );
     };
    
     /**
     * If a user is authenticated the ProfileContent component above is rendered. Otherwise a message indicating a user is not authenticated is rendered.
     */
     const MainContent = () => {
       return (
           <div className="App">
               <AuthenticatedTemplate>
                   <ProfileContent />
               </AuthenticatedTemplate>
    
               <UnauthenticatedTemplate>
                   <h5>
                       <center>
                           Please sign-in to see your profile information.
                       </center>
                   </h5>
               </UnauthenticatedTemplate>
           </div>
       );
     };
    
     export default function App() {
       return (
           <PageLayout>
               <center>
                   <MainContent />
               </center>
           </PageLayout>
       );
     }
    
  6. 保存文件。

  7. 最后,在 src/components 文件夹下,打开 ProfileData.jsx 文件,并将文件的内容替换为以下代码片段:

     import React from "react";
     import ListGroup from 'react-bootstrap/ListGroup'; 
     import Table from 'react-bootstrap/Table';
     /**
      * Renders information about the user obtained from MS Graph 
      * @param props
      */
     export const ProfileData = (props) => {
       const holidays = props.graphqlData.data.publicholidays.items;
       return (
         <Table striped bordered hover responsive>
         <thead>
           <tr>
             <th>Country</th>
             <th>Holiday</th>
             <th>Date</th>
           </tr>
         </thead>
         <tbody>
           {holidays.map((item,i) => (
           <tr key={i}>
             <td>{item.countryOrRegion}</td>
             <td>{item.holidayName}</td>
             <td>{item.date}</td>
           </tr>
           ))}
           </tbody>
         </Table>
     )};
    
  8. 保存所有文件更改。

  9. 在所选终端应用程序中,转到 React 项目的根文件夹,并执行命令 npm start 以在本地测试应用程序。

  10. 应用程序从 http://localhost:3000 加载到浏览器后,请按照教程从应用程序调用 API 最后一部分的步骤进行身份验证。

  11. 登录后,单击“查询 Fabric API for GraphQL 数据”按钮。

登录后 React 示例应用的屏幕截图。

  1. 成功向 Fabric 中的 GraphQL API 发出经过身份验证的请求会将 GraphQL 查询中的数据返回到 React 客户端应用程序中的湖屋:

    收到 GraphQL 请求后 React 示例应用的屏幕截图。

使用服务主体

虽然上一部分中的步骤需要提供对用户主体的访问权限,但也可以使用服务主体访问 GraphQL API:

  1. 按照上一部分中的步骤创建具有类似权限(PowerBI 服务的 Item.Execute.All 范围)的第二个 Microsoft Entra 应用。 在新应用中,在“证书和密码”下添加客户端密码。有关详细信息,请参阅注册 Microsoft Entra 应用并创建服务主体

  2. 确保租户管理员在 Fabric 中启用了服务主体的使用。 在“租户管理员”门户中,转到“租户设置”。 在“开发人员设置”下,启用“服务主体可以使用 Fabric API”。 启用此设置后,应用程序将在 Fabric 门户中可见,以便进行角色或权限分配。 可以找到有关标识支持的详细信息。

  3. 服务主体需要访问 GraphQL API 和数据源,更具体地说,需要对 GraphQL API 的执行权限,以及在所选数据源中相应的读取或写入访问权限。 在 Fabric 门户中,打开工作区并选择 API 旁边的省略号。 选择 API 的“管理权限”,然后选择“添加用户”。 添加应用程序并选择“运行查询和突变”,这将为服务主体提供所需的执行权限。 出于测试目的,实现对 API 和数据源的所需权限的最简单方法是将应用程序添加为具有参与者角色的工作区成员,GraphQL API 和数据源项都位于该工作区中。

    GraphQL API 权限的屏幕截图。

重要

为 API 定义连接选项时,请确保 API 配置为使用单一登录 (SSO)。 目前,服务主体不支持保存的凭据。 有关详细信息,请参阅在 Fabric 中创建 GraphQL API 并添加数据

由于服务主体需要证书或客户端密码,因此单页应用程序 (SPA) 中的 Microsoft 身份验证库 (MSAL) 不支持它,如我们在最后一步中生成的 React 应用。 可以根据要求和用例,利用妥善定义的授权逻辑正确保护后端服务。

将 API 配置为可由服务主体访问后,可以在本地计算机上使用简单的 Node.JS 应用程序在本地对其进行测试:

const { ClientSecretCredential } = require('@azure/identity');

// Define your Microsoft Entra ID credentials
const tenantId = "<YOUR_TENANT_ID>";
const clientId = "<YOUR_CLIENT_ID>";
const clientSecret = "<YOUR_CLIENT_SECRET>"; // Service principal secret value

const scope = "https://api.fabric.microsoft.com/.default"; // The scope of the token to access Fabric

// Create a credential object with service principal details
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);

// Function to retrieve the token
async function getToken() {
    try {
        // Get the token for the specified scope
        const tokenResponse = await credential.getToken(scope);
        console.log("Access Token:", tokenResponse.token);
    } catch (err) {
        console.error("Error retrieving token:", err.message);
    }
}

使用所选 Node.JS 包管理器安装依赖项 (@azure/identity),使用所需的信息修改文件,保存并执行该文件 (node <filename.js>) 后,可以从 Microsoft Entra 检索令牌。

然后,可以通过将相应的详细信息替换为刚刚检索到的令牌、要执行的 GraphQL 查询以及 GraphQL API 终结点,可使用该令牌通过 PowerShell 调用 GraphQL API:

$headers = @{
    Authorization = "Bearer <YOUR_TOKEN>"
    'Content-Type' = 'application/json'
}

$body = @{
    query = @"
    <YOUR_GRAPHQL_QUERY>
"@
}

# Make the POST request to the GraphQL API
$response = Invoke-RestMethod -Uri "<YOUR_GRAPHQL_API_ENDPOINT>" -Method POST -Headers $headers -Body ($body | ConvertTo-Json)

# Output the response
$response | ConvertTo-Json -Depth 10 


或者,可以使用 cURL 来实现相同的结果:

curl -X POST <YOUR_GRAPHQL_API_ENDPOINT> \
-H "Authorization: <YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"query": "<YOUR_GRAPHQL_QUERY(in a single line)>"}'

出于本地测试目的,可以使用附加依赖项 (axios) 稍微修改 Node.JS 代码,以便在单个执行中检索令牌并调用 API:

const { ClientSecretCredential } = require('@azure/identity');
const axios = require('axios');

// Microsoft Entra ID credentials
const tenantId = "<YOUR_TENANT_ID>";
const clientId = "<YOUR_CLIENT_ID>";
const clientSecret = "<YOUR_CLIENT_SECRET>"; // Service principal secret value

// GraphQL API details
const graphqlApiUrl = "YOUR_GRAPHQL_API_ENDPOINT>";
const scope = "https://api.fabric.microsoft.com/.default"; // The scope to request the token for

// The GraphQL query
const graphqlQuery = {
  query: `
  <YOUR_GRAPHQL_QUERY>
  `
};

// Function to retrieve a token and call the GraphQL API
async function fetchGraphQLData() {
  try {
    // Step 1: Retrieve token using the ClientSecretCredential
    const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
    const tokenResponse = await credential.getToken(scope);
    const accessToken = tokenResponse.token;

    console.log("Access token retrieved!");

    // Step 2: Use the token to make a POST request to the GraphQL API
    const response = await axios.post(
      graphqlApiUrl,
      graphqlQuery,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        }
      }
    );

    // Step 3: Output the GraphQL response data
    console.log("GraphQL API response:", JSON.stringify(response.data));
    
  } catch (err) {
    console.error("Error:", err.message);
  }
}

// Execute the function
fetchGraphQLData();

其他语言

Microsoft Fabric 示例 GitHub 存储库中查找用于连接到 GraphQL API 的 C#、Python 和其他语言示例。