graphql-mesh icon indicating copy to clipboard operation
graphql-mesh copied to clipboard

MSGraph Odata query sub objects error

Open falsadoo opened this issue 3 years ago • 2 comments

Hi All, I am facing an issue with graphql mesh example for odata ms graph. The schema get genrated for MS graph however, I am only able to get the data for the top object and not the child objects. image

what am I doing wrong?

falsadoo avatar Jun 27 '22 18:06 falsadoo

It seems to be working fine here; https://codesandbox.io/s/github/Urigo/graphql-mesh/tree/master/examples/odata-microsoft?from-embed=&file=/example-queries/fetchRecentEmails.graphql:0-220 Could you help us to reproduce it on CodeSandbox? Thanks

ardatan avatar Jun 27 '22 18:06 ardatan

Hi @ardatan , i am using on behalf of the user flow for azure ad which is working good and using resolversComposition to do the token exchange for ms graph. here is my .meshrc file content:

 sources:
  - name: Microsoft Graph
    handler:
      odata:
        baseUrl: https://graph.microsoft.com/{env.GRAPH_VERSION}
        batch: json
        expandNavProps: true
        operationHeaders:
          Authorization: Bearer {context.headers['x-my-api-token']}
    transforms:
      - resolversComposition:
          mode: bare
          compositions:
            - resolver: Query.*
              composer: auth#isAuth
documents:
  - example-queries/**/*.graphql
serve:
  #hostname: 0.0.0.0
  port: 4000
  playground: true

and here is my auth.js content

const msal = require('@azure/msal-node');
const jwt = require('jsonwebtoken')
const jwksClient = require('jwks-rsa');
const DISCOVERY_KEYS_ENDPOINT = "https://login.microsoftonline.com/tenentid/discovery/v2.0/keys";
const config = {
    auth: {
        clientId: "client id",
        authority: "https://login.microsoftonline.com/tenentid",
        clientSecret: "client secret value",
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
};
const cca = new msal.ConfidentialClientApplication(config);
const getSigningKeys = (header, callback) => {
    var client = jwksClient({
        jwksUri: DISCOVERY_KEYS_ENDPOINT
    });

    client.getSigningKey(header.kid, function (err, key) {
        var signingKey = key.publicKey || key.rsaPublicKey;
        callback(null, signingKey);
    });
}
function jwtAccessAuthCheck(accessToken) {
   
        const validationOptions = {
            audience: config.auth.clientId, // v2.0 token
            issuer: config.auth.authority + "/v2.0" // v2.0 token
        }
    return new Promise((resolve, reject) => {
        jwt.verify(accessToken, getSigningKeys, validationOptions, (err, payload) => {
            if (err) {
                console.log(err);
                reject('TokenExpiredError.');
            }
            const oboRequest = {
                oboAssertion: accessToken,
                scopes: ['openid', 'profile','Directory.ReadWrite.All',
                'User.Read', 'Sites.ReadWrite.All', 'Contacts.ReadWrite',
                'Calendars.Read', 'Mail.ReadWrite','Notes.Read.All',
                'Files.Read.All', 'Mail.ReadWrite.Shared', 'Tasks.ReadWrite.Shared',
                'People.Read', 'Tasks.ReadWrite'],
            }
        cca.acquireTokenOnBehalfOf(oboRequest).then((response) => {
                
               
                
                resolve(response.accessToken);
                
            }).catch((error) => {
                reject("Error: ",+error)
            });

        })
    });
  }

module.exports = {
    isAuth: next => async (root, args, context, info) => {
        // Check if Authorization header is present
        if(!context.headers.authorization) {
            throw new Error('Unauthorized');
        }
        const token = context.headers.authorization.split(' ')[1];
        context.headers['x-my-api-token'] =  await jwtAccessAuthCheck(token);
        return next(root,args,context,info);
    }
  };

and this the package.json

{
    "name": "msgraph",
    "version": "0.1.9",
    "scripts": {
        "start": "cross-env GRAPH_VERSION=\"v1.0\" mesh dev",
        "start:beta": "cross-env GRAPH_VERSION=\"beta\" mesh dev"
    },
    "license": "MIT",
    "private": true,
    "dependencies": {
        "@azure/msal-node": "^1.10.0",
        "@graphql-mesh/cli": "0.73.3",
        "@graphql-mesh/graphql": "^0.28.0",
        "@graphql-mesh/odata": "0.18.0",
        "@graphql-mesh/transform-resolvers-composition": "^0.12.63",
        "graphql": "16.0.1",
        "jsonwebtoken": "^8.5.1",
        "jwks-rsa": "^2.1.4",
        "cross-env": "7.0.2"
    }
}

I am running on node:16.15 can you help me what the error is referring to ?

falsadoo avatar Jun 27 '22 19:06 falsadoo

@ardatan just came across this issue after I messaged you in twitter

I created small TokenVault utility to cache and refresh OAuth tokens needed for backend calls. https://github.com/xmlking/svelte-starter-kit/blob/main/src/lib/server/backend/TokenVault.ts

wanted to set Dynamic Header Values (for Authorization) via context if @falsadoo example works.

xmlking avatar Mar 02 '23 06:03 xmlking

Maybe this example helps? https://github.com/Urigo/graphql-mesh/tree/master/examples/odata-msgraph-programmatic-ts

ardatan avatar Mar 02 '23 06:03 ardatan

I always get this error message, now: An error occurred while building the artifacts: Error: Input Object type GraphReportRootInput must define one or more fields.

I see the same error here: https://codesandbox.io/s/github/Urigo/graphql-mesh/tree/master/examples/odata-microsoft?from-embed=&file=/example-queries/fetchRecentEmails.graphql:0-220

skriptet avatar Mar 29 '23 08:03 skriptet