Comments (33)
@ardatan Test locally and yes it works Thanks.
from graphql-mesh.
Also, my linter was not too happy with the add-fields.js
file
Erroring with
7:3 error Assignment to property of function parameter 'args' no-param-reassign
Here the updated file
const graphqlFields = require('graphql-fields');
// This function gets requested fields from GraphQLResolveInfo
// And passes them through `fields` parameter
module.exports = (next) => (root, args, context, info) => {
const fields = Object.keys(graphqlFields(info));
const updatedArgs = { ...args, fields: fields.join(',') };
return next(root, updatedArgs, context, info);
};
``
from graphql-mesh.
We changed the API for Swagger but haven't released it yet. You can use the alpha release for now. 0.0.20-alpha-aa57f27.19+aa57f27
You can pass headers for your schema by using schemaHeaders option like below with that alpha version;
sources:
- name: Youtrack
handler:
openapi:
source: https://youtrack.example.com/api/openapi.json
schemaHeaders:
Authorization: Bearer perm:******************
from graphql-mesh.
Thank you I've changed to the alpha release as suggested. Also I assume the Authorization: Bearer Bearer perm:******************
having Bearer twice was just a typo.
Anyway, the error has now changed to:
error: Unable to serve mesh: Request is not defined {"stack":"ReferenceError: Request is not defined\n at readUrlWithCache (/Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/utils/index.cjs.js:69:52)\n at Object.readFileOrUrlWithCache (/Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/utils/index.cjs.js:32:16)\n at Object.getMeshSource (/Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/openapi/index.cjs.js:9:34)\n at /Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/runtime/index.cjs.js:240:55\n at Array.map (<anonymous>)\n at Object.getMesh (/Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/runtime/index.cjs.js:239:39)\n at Object.handler (/Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/cli/bin.js:238:62)"}
from graphql-mesh.
@garethhallnz You're right. Could you try with this one?
0.0.20-alpha-18f10ab.20+18f10ab
from graphql-mesh.
Yay, a step forward :) That said there is a new failure much later down the stack.
Playground now launches but queries are failing because the uri is not correct.
apiadmin/projects
should be api/admin/projects
https://youtrack.example.com/api/openapi.json defines the uri as
"servers": [
{
"url": "/api"
}
],
....
Error message
{
"errors": [
{
"message": "Invalid URI \"/apiadmin/projects\"",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getAdminProjects"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Invalid URI \"/apiadmin/projects\"",
" at Request.init (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:273:31)",
" at new Request (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:127:8)",
" at request (/Users/garethhall/Sites/communica/taskhub/node_modules/request/index.js:53:10)",
" at /Users/garethhall/Sites/communica/taskhub/node_modules/openapi-to-graphql/lib/resolver_builder.js:238:13",
" at new Promise (<anonymous>)",
" at /Users/garethhall/Sites/communica/taskhub/node_modules/openapi-to-graphql/lib/resolver_builder.js:237:16",
" at /Users/garethhall/Sites/communica/taskhub/node_modules/@graphql-mesh/runtime/index.cjs.js:215:38",
" at field.resolve (/Users/garethhall/Sites/communica/taskhub/node_modules/graphql-extensions/dist/index.js:133:26)",
" at resolveFieldValueOrError (/Users/garethhall/Sites/communica/taskhub/node_modules/graphql/execution/execute.js:486:18)",
" at resolveField (/Users/garethhall/Sites/communica/taskhub/node_modules/graphql/execution/execute.js:444:16)"
]
}
}
}
],
"data": {
"getAdminProjects": null
}
}
from graphql-mesh.
Are you sure this swagger file is valid because when I paste it into Swagger Editor? I got an error like paths must start with '/'
.
https://editor.swagger.io/
You can see the specifications; paths always starts with /
character.
https://swagger.io/docs/specification/basic-structure/
We can add an option to override baseUrl
. Working on it.
from graphql-mesh.
@garethhallnz
I added an option to override server
config. So you can use it like below;
sources:
- name: Youtrack
handler:
openapi:
source: https://youtrack.example.com/api/openapi.json
schemaHeaders:
Authorization: Bearer perm:******************
baseUrl: https://youtrack.example.com/api/
npx match-version @graphql-mesh 0.0.20-alpha-067bc75.23+067bc75
Could you try again with this configuration with that canary version?
from graphql-mesh.
@ardatan Your help has been awesome thanks!
Another step forward but still not working :(
The baseUrl does seem to resolve the previous problem. However, PlayGround now reports Unauthorized.
My graphql query
{
getAdminProjects{
shortName
}
}
Response
{
"errors": [
{
"message": "Could not invoke operation GET admin/projects",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getAdminProjects"
],
"extensions": {
"method": "get",
"path": "admin/projects",
"statusCode": 401,
"responseHeaders": {
"server": "nginx/1.14.0 (Ubuntu)",
"date": "Tue, 07 Apr 2020 00:06:40 GMT",
"content-type": "application/json;charset=utf-8",
"content-length": "47",
"connection": "close",
"access-control-expose-headers": "Location",
"x-xss-protection": "1; mode=block",
"x-frame-options": "SAMEORIGIN",
"x-content-type-options": "nosniff",
"referrer-policy": "strict-origin-when-cross-origin",
"content-encoding": "UTF-8",
"cache-control": "no-cache, no-store, no-transform, must-revalidate"
},
"responseBody": {
"error": "Unauthorized",
"error_description": ""
},
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"message": "Could not invoke operation GET admin/projects",
"stacktrace": [
"GraphQLError: Could not invoke operation GET admin/projects",
" at graphQLErrorWithExtensions (/Users/garethhall/Sites/communica/taskhub/node_modules/openapi-to-graphql/lib/resolver_builder.js:700:12)",
" at Request._callback (/Users/garethhall/Sites/communica/taskhub/node_modules/openapi-to-graphql/lib/resolver_builder.js:261:32)",
" at Request.self.callback (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:185:22)",
" at Request.emit (events.js:321:20)",
" at Request.<anonymous> (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:1154:10)",
" at Request.emit (events.js:321:20)",
" at IncomingMessage.<anonymous> (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:1076:12)",
" at Object.onceWrapper (events.js:427:28)",
" at IncomingMessage.emit (events.js:333:22)",
" at endReadableNT (_stream_readable.js:1201:12)"
]
}
}
}
],
"data": {
"getAdminProjects": null
}
}
I have also tried to pass the Authorization header in PlayGround but to no avail.
Here is the curl request that should match the above and it does work.
curl --location --request GET 'https://youtrack.example.com/api/admin/projects?fields=shortName' \
--header 'Authorization: Bearer perm:******************'
from graphql-mesh.
For operations, you need to use operationHeaders additionally.
schemaHeaders is used while fetching the Swagger/OpenAPI schema only but operationHeaders is used during the operations on runtime.
from graphql-mesh.
Sorry I am not sure I follow, what does that mean?
from graphql-mesh.
schemaHeaders
is used only for fetching openapi.json
. There is another option called operationHeaders
used for operations/queries.
sources:
- name: Youtrack
handler:
openapi:
source: https://youtrack.example.com/api/openapi.json
schemaHeaders:
Authorization: Bearer perm:******************
operationHeaders:
Authorization: Bearer perm:******************
baseUrl: https://youtrack.example.com/api/
That means you need to define your headers under operationHeaders
as well.
from graphql-mesh.
Thanks that has inched things forward a little more. It's almost working;
My output is now
{
"data": {
"getAdminProjects": [
{
"shortName": null
},
{
"shortName": null
},
{
"shortName": null
},
{
"shortName": null
},
{
"shortName": null
}
]
}
}
from graphql-mesh.
It looks like an incompability between the schema and the actual result. How does the actual result look like?
Another question; does that swagger file work well within a Swagger UI? because it looks like broken as I said before.
from graphql-mesh.
Well clearly the Youtrack devs have not implemented the openapi standard correctly.
I too get an error in swagger.
So I downloaded the openapi file and edited it to make it pass. Basically I removed the license section and added "/" to the paths.
I then updated the mesh file to:
sources:
- name: Youtrack
handler:
openapi:
source: ./openapi.json
schemaHeaders:
Authorization: Bearer perm:***********
operationHeaders:
Authorization: Bearer perm:***********
baseUrl: https://youtrack.example.com/api/
I was hoping your new baseUrl
option would allow me to bypass prevouse with the url.
But that caused this error in PlayGround
{
"errors": [
{
"message": "Could not invoke operation GET /admin/projects",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getAdminProjects"
],
"extensions": {
"method": "get",
"path": "/admin/projects",
"statusCode": 404,
"responseHeaders": {
"server": "nginx/1.14.0 (Ubuntu)",
"date": "Tue, 07 Apr 2020 09:19:42 GMT",
"content-type": "application/json;charset=utf-8",
"content-length": "62",
"connection": "close",
"access-control-expose-headers": "Location",
"x-xss-protection": "1; mode=block",
"x-frame-options": "SAMEORIGIN",
"x-content-type-options": "nosniff",
"referrer-policy": "strict-origin-when-cross-origin",
"set-cookie": [
"YTJSESSIONID=node01sxh5pog88xdy1gt1vt3gmffet1350.node0; Path=/; Secure; HttpOnly"
],
"expires": "Thu, 01 Jan 1970 00:00:00 GMT",
"content-encoding": "UTF-8",
"cache-control": "no-cache, no-store, no-transform, must-revalidate"
},
"responseBody": {
"error": "Not Found",
"error_description": "HTTP 404 Not Found"
},
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"message": "Could not invoke operation GET /admin/projects",
"stacktrace": [
"GraphQLError: Could not invoke operation GET /admin/projects",
" at graphQLErrorWithExtensions (/Users/garethhall/Sites/communica/taskhub/node_modules/openapi-to-graphql/lib/resolver_builder.js:700:12)",
" at Request._callback (/Users/garethhall/Sites/communica/taskhub/node_modules/openapi-to-graphql/lib/resolver_builder.js:261:32)",
" at Request.self.callback (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:185:22)",
" at Request.emit (events.js:321:20)",
" at Request.<anonymous> (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:1154:10)",
" at Request.emit (events.js:321:20)",
" at IncomingMessage.<anonymous> (/Users/garethhall/Sites/communica/taskhub/node_modules/request/request.js:1076:12)",
" at Object.onceWrapper (events.js:427:28)",
" at IncomingMessage.emit (events.js:333:22)",
" at endReadableNT (_stream_readable.js:1201:12)"
]
}
}
}
],
"data": {
"getAdminProjects": null
}
}
I think the schema is correct
"Project": {
"allOf": [
{
"$ref": "#/components/schemas/IssueFolder"
},
{
"properties": {
"startingNumber": {
"type": "integer",
"description": "Starting number for issues in project. This property can be set only during creation of the new project.",
"format": "int64",
"readOnly": false
},
"shortName": {
"type": "string",
"description": "The ID of the project. This short name is also a prefix for an issue ID.",
"readOnly": false
},
"description": {
"type": "string",
"description": "The description of the project as shown on the project profile page.",
"readOnly": false
},
"leader": {
"description": "The user who is set as the project lead.",
"$ref": "#/components/schemas/User"
},
"createdBy": {
"description": "The user who created the project.",
"$ref": "#/components/schemas/User"
},
"issues": {
"type": "array",
"description": "A list of all issues that belong to the project.",
"items": {
"$ref": "#/components/schemas/Issue"
}
},
"customFields": {
"type": "object",
"description": "The set of custom fields that are available in the project.",
"readOnly": true
},
"archived": {
"type": "boolean",
"description": "If the project is currently archived, this property is `true`.",
"readOnly": false
},
"fromEmail": {
"type": "string",
"description": "\n The email address that is used to send notifications for the project. If a 'From' address is not set \n for the project, the default 'From' address for the YouTrack server is returned.\n ",
"readOnly": false
},
"replyToEmail": {
"type": "string",
"description": "\n The email address that is used as the reply email to send notifications for the project.\n If it is not set for the project, the default address for the YouTrack server is returned. \n ",
"readOnly": false
},
"template": {
"type": "boolean",
"description": "If `true`, this project is a template. ",
"readOnly": false
},
"iconUrl": {
"type": "string",
"description": "The URL of the icon of the project.",
"readOnly": true
}
},
"description": "Represents a YouTrack project."
}
]
},
The equivalent curl request
curl --location --request GET 'https://youtrack.example.com/api/admin/projects?fields=shortName' \
--header 'Authorization: Bearer perm:***********'
Result to
[
{
"shortName": "ANZW",
"$type": "Project"
},
{
"shortName": "APM",
"$type": "Project"
},
{
"shortName": "ARB",
"$type": "Project"
},
{
"shortName": "AM",
"$type": "Project"
}
]
from graphql-mesh.
@garethhallnz we are thinking about having the idea of "Mesh Modules" or something, where you can install ready to use, open source Mesh npm packages for APIs a lot of people need.
Do you think you can share your code on Github somehow and we can help you create something that first will work for you and also others will find useful? (YouTrack GraphQL API)
from graphql-mesh.
@Urigo I am happy to push something to Github; The problem is while you could get a free Youtrack account it's not really something a user can just setup to try.
AI user would have to:
- Create a YouTrack account
- Load some test data
- Go into YouTrack and create an access token
Do you think people will do that?
from graphql-mesh.
@garethhallnz maybe who knows?
also after we'll have it maybe we can share it with JetBrains so they could offer their users also a GraphQL API!
from graphql-mesh.
@ardatan I think in my use case I can not really predefine operationHeaders, user logs in and acquires token which is then supposed to be used for querying data. Is is possible still to pass operation headers from the client?
from graphql-mesh.
@ardatan I think in my use case I can not really predefine operationHeaders, user logs in and acquires token which is then supposed to be used for querying data. Is is possible still to pass operation headers from the client?
Yeah I am going to face that issue too. The query header must be injectable.
from graphql-mesh.
@garethhallnz maybe who knows?
also after we'll have it maybe we can share it with JetBrains so they could offer their users also a GraphQL API!
Do you want to create a repo so you have ownership of it? Then I'll fork than and do a pull request; otherwise, I'll set it up under my account.
from graphql-mesh.
@garethhallnz I realized that you need add the name of field specifically in fields
parameter in regular HTTP request. So since Mesh doesn't know this specific thing, you need to do the same thing in GraphQL operations. I am preparing an example for youtrack, you can take a look at.
@petrovalex We use openapi-to-graphql
under the hood for Swagger/OpenAPI specifications, so they don't support additional headers that can be passed from GraphQL Args etc. But we're working on it. For now you can use environmental variables or runtime API to pass those variables. In environmental variables you can use the following syntax;
Authorization: Bearer ${YOUTRACK_TOKEN}
from graphql-mesh.
I submitted a PR that has some changes on Mesh to make things easier for OpenAPI.
#224
from graphql-mesh.
@garethhallnz I also added a working example;
https://codesandbox.io/s/strange-lewin-blnvq?file=/.meshrc.yml
from graphql-mesh.
@ardatan I spoke a little too soon; I did a bunch of testing and simple queries work but more complex onces fail.
These work
query getProjects{
getAdminProjects {
shortName
}
}
query getIssuesByProject{
getAdminProjectsIdIssues (id: "ANZW") {
summary
}
}
query getIssues{
getIssues {
summary
}
}
These fail or more specifically I get null
query getProjectsWithIssues{
getAdminProjects {
shortName
issues {
summary
}
}
}
query getIssuesWithComments{
getIssues{
summary
created
reporter {
email
}
comments {
text
}
}
}
query reallyComplex{
getAdminProjectsIdIssues (
id: "FLTI"
) {
numberInProject
idReadable
summary
comments {
text
author {
fullName
}
}
commentsCount
}
}
Ideally my goal it to reconstruct
axios.get(`/api/admin/projects/${id}/issues?fields=numberInProject,idReadable,summary,comments(text,author(name)),commentsCount,timeTracking(workItems(id,duration(minutes,presentation))),customFields(projectCustomField(field(name)),value(minutes,presentation))&$skip=0&$top=10000`)
from graphql-mesh.
In codesandbox I sent to you, all of them are working. I didn't get null with any of them. Could you confirm? If yes, could you follow the same setup?
https://codesandbox.io/s/strange-lewin-blnvq?file=/.meshrc.yml
from graphql-mesh.
@ardatan My setup is the same other than hardcoded tokens. I think the problem is content in your example. You only have one project called "Kanban Test (KT)" but it has no issues or comments against said issues.
So in your example
query getIssuesByProject{
getAdminProjectsIdIssues (id: "KT") {
summary
}
}
returns [] so there are no issues in KT
So to replicate the problem create an issue againt the KT project
from graphql-mesh.
@garethhallnz I added an issue to the project as you said, and the following query gives me the correct results. You can test in my codesandbox as well. Let me know if I'm missing something.
query getIssuesByProject{
getAdminProjectsIdIssues (id: "KT") {
summary
description
}
}
Result:
{
"data": {
"getAdminProjectsIdIssues": [
{
"summary": "Issue Test",
"description": "Issue Test Description"
}
]
}
}
from graphql-mesh.
Yes, that works (as do my copy) but as we all know the value of Grapdql is the ability the pull data with depth.
So try
query getProjectsWithIssues{
getAdminProjects {
shortName
issues {
summary
}
}
}
You notice issues[0].summary
us null
from graphql-mesh.
Ok so this is a different problem. We solved the previous ones as I understand :) This is good.
Again this is not related to GraphQL Mesh directly but YouTrack specific, so we need update our composer function to resolve the fields of YouTrack as I see in their documentation. So I updated the composer function to resolve subfields as well. You can do console.log(args)
to see how we build our fields parameter.
https://codesandbox.io/s/strange-lewin-blnvq?file=/src/add-fields.js
from graphql-mesh.
@petrovalex We use
openapi-to-graphql
under the hood for Swagger/OpenAPI specifications, so they don't support additional headers that can be passed from GraphQL Args etc. But we're working on it. For now you can use environmental variables or runtime API to pass those variables. In environmental variables you can use the following syntax;
Authorization: Bearer ${YOUTRACK_TOKEN}
@ardatan Now that I am thinking about it the above is a massive problem. The Youtrack token is directly related to the user and their permissions. Therefore some users might have access to operations they should not have; like deleting something. So in a sense, we can only use mesh like an administration connection. This severely limits it's use-case ..... for now at least.
from graphql-mesh.
@garethhallnz I assume that we have solved another problem as well because you didn't say anything about it :)
With that PR, now we are able to pass custom headers using args and context. So using the following configuration, you can pass different API Keys using apiKey
argument on your root operations.
sources:
- name: YouTrack
handler:
openapi:
source: ${YOUTRACK_SERVICE_URL}/api/openapi.json
baseUrl: ${YOUTRACK_SERVICE_URL}/api/
schemaHeaders: # this is admin token to fetch the schema before actual runtime
Authorization: Bearer ${YOUTRACK_TOKEN}
operationHeaders: # This is user-specific api token
Authorization: Bearer {args.apiToken}
# `license` field is not valid
# But it's ok because all YouTrack schemas are in the same structure
# So using this option is safe.
skipSchemaValidation: true
transforms:
# Each YouTrack needs `fields` query parameter
# So thanks to GraphQL, we know which fields client needs
# And we can prevent overfetching in this way
- resolversComposition:
- resolver: Query.*
composer: ./src/add-fields
Or you can pass it using context,
sources:
- name: YouTrack
handler:
openapi:
source: ${YOUTRACK_SERVICE_URL}/api/openapi.json
baseUrl: ${YOUTRACK_SERVICE_URL}/api/
schemaHeaders:
Authorization: Bearer ${YOUTRACK_TOKEN}
operationHeaders:
Authorization: Bearer {context.apiToken}
# `license` field is not valid
# But it's ok because all YouTrack schemas are in the same structure
# So using this option is safe.
skipSchemaValidation: true
transforms:
# Each YouTrack needs `fields` query parameter
# So thanks to GraphQL, we know which fields client needs
# And we can prevent overfetching in this way
- resolversComposition:
- resolver: Query.*
composer: ./src/add-fields
I think this would work for you and I think this doesn't limit your use-case then. But Mesh should never be your public schema in my opinion. Mesh is just a way to query your database with GraphQL using SDK etc.
from graphql-mesh.
Available in 0.1.0!
from graphql-mesh.
Related Issues (20)
- [Rename Transformer]: not all rules are applied to a single target when using mode 'bare'
- (transformer/rename): Results differ depending on which mode is being used HOT 1
- Receiving 401 without body does not throw an error HOT 4
- (transformer/rename): Scalar types can't be renamed when using mode 'bare'
- [openapi]: fallbackFormat is ignored
- [transformer/naming-convention]: data can't be resolved when using in 'bare' mode
- [transformer/naming-convention]: Default enum values for arguments are not renamed properly HOT 1
- @graphql-mesh/grpc: Invalid value used as weak map key HOT 4
- Postman-Handler/Loader: Postman-Collections as source HOT 5
- Troubles with connectionParams on subscriptions
- Filter out header params with the OpenAPI handler doesn't work. HOT 1
- OData MS Graph Type with name "GraphEntityInput" does not exists HOT 1
- Not all `operationHeaders` are forwarded
- Bun - π Resolving [1/1] error: no commit matching "semver:^20" found for "uWebSockets.js" (but repository exists)
- Generated enum values as numbers when using gRPC source
- Transform federation generates incorrect schema
- CORS regex origins work in Firefox but not Chromium-based browsers
- Custom Logger not found with mesh start
- Feedback for βAdding Authentication with Auth0β
- How to run Prometheus with mesh start without giving an error? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphql-mesh.