-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/#873 retrieve remote schemas #877
Feat/#873 retrieve remote schemas #877
Conversation
Co-authored-by: Anders Richardsson <[email protected]>
Co-authored-by: Anders Richardsson <[email protected]>
…arranger into feat/#872_initial_config
Co-authored-by: Anders Richardsson <[email protected]>
…eat/#873_retrieve_remote_schemas
…eat/#873_retrieve_remote_schemas
…eat/#873_retrieve_remote_schemas
modules/server/src/network/index.ts
Outdated
if (response.status === 200 && response.statusText === 'OK') { | ||
// axios response "data" field, graphql response "data" field | ||
const responseData = response.data?.data; | ||
if (isGqlIntrospectionQuery(responseData)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (isGqlIntrospectionQuery(responseData)) { | |
if (isGqlIntrospectionQuery(responseData)) { |
Co-authored-by: Anders Richardsson <[email protected]>
modules/server/src/network/index.ts
Outdated
// type cast because rejected errors are handled | ||
const networkQueries = (await Promise.allSettled( | ||
networkQueryPromises, | ||
)) as PromiseFulfilledResult<FetchRemoteSchemaResult>[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not use this type assertion. Promise.allSettled
returns a PromiseSettledResult<T>
where T is your type.
This is important to use as any rejected response will NOT have a .value
property, we need to always check for the .status
in each element of network queries array.
See type information for PromiseSettledResult<T>
: https://github.com/microsoft/TypeScript/blob/main/src/lib/es2020.promise.d.ts#L11
modules/server/src/network/index.ts
Outdated
|
||
// build schema | ||
const schemaResults = networkQueries.map((networkResult) => { | ||
const { config, introspectionResult } = networkResult.value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.value
won't exist on rejected promises. We can filter those out if we only care about succeses:
const schemaResults = networkQueries
.filter(
(result): result is PromiseFulfilledResult<FetchRemoteSchemaResult> =>
result.status === 'fulfilled',
)
.map((networkResult) => {
const { config, introspectionResult } = networkResult.value;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's how the code is currently handling
@joneubank addressed feedback. TS building. please take another look when you have some time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking strong throughout.
The axios request failure handling I think is not what you would expect to see so consider that comment and if there are changes needed.
/** | ||
* Creates a graphql query string with variables for use in a POST request | ||
* @returns string | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙏
modules/server/src/network/gql.ts
Outdated
/** | ||
* TODO: Add typing for variables. Type will be dependant on gql endpoint being queried. | ||
*/ | ||
variables: unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at how this is used, we don't really care about the specific contents of the variables. This function is doing no work to ensure that the variables expected in the query
are included in variables
(or vice versa). The only thing we know is we want to be able to run JSON.stringify(variables)
which takes any
. In practice, we expect this parameter to be an object with some variable values which themselves could be anything, and we probably don't want it to be string
or number
etc. So in this case I might reccomend:
variables: Record<string, any>
* Creates a graphql query string with variables for use in a POST request | ||
* @returns string | ||
*/ | ||
export const createGqlQuery = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed this function isn't actually used in your code here, is that expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected. #877 (comment)
// queries need to be strings when using regular http | ||
const query = print(queryAST); | ||
return JSON.stringify({ query, variables }); | ||
}; | ||
|
||
export const fetchGql = ({ url, gqlRequest }) => { | ||
export const fetchGql = ({ url, gqlRequest }: { url: string; gqlRequest: { query: string } }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TSDoc will be wanted before feature branch is complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your TSDoc is incorrect, instead of returning an axios instance that you can use to send future requests, this sends your defined request with axios and returns the AxiosResponse (or throws error, stupid axios).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great feedback, useful insight... but let's try keeping unhelpful personal opinions to a minimum.
modules/server/src/network/index.ts
Outdated
@@ -18,7 +19,7 @@ const isGqlIntrospectionQuery = (input: unknown): input is IntrospectionQuery => | |||
|
|||
type FetchRemoteSchemaResult = { | |||
config: NetworkAggregationConfigInput; | |||
introspectionResult: IntrospectionQuery | null; | |||
introspectionResult: IntrospectionQuery | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just:
introspectionResult?: IntrospectionQuery
modules/server/src/network/index.ts
Outdated
throw new NetworkAggregationError( | ||
`Unexpected data in response object. Please verify the endpoint at ${graphqlUrl} is returning a valid GQL Schema.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
modules/server/src/network/index.ts
Outdated
} catch (error) { | ||
console.log(`failed to retrieve schema from url: ${config.graphqlUrl}`); | ||
console.error(error); | ||
return { config, introspectionResult: undefined }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Axios throws errors on status 4xx and 5xx. That means that the else block you have above is behaviour that you are likely expecting to be handled here as well. You should try checking if the error here is an instance of AxiosError, and if so you can handle these error statuses in the catch block. I imagine you will want to throw your NetworkAggregationError.
modules/server/src/network/index.ts
Outdated
.filter( | ||
(result): result is PromiseFulfilledResult<FetchRemoteSchemaResult> => | ||
result.status === 'fulfilled', | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With TS 5.5 we no longer require the result is
return type :D
modules/server/src/network/index.ts
Outdated
} catch (error) { | ||
console.log(`failed to retrieve schema from url: ${config.graphqlUrl}`); | ||
console.error(error); | ||
return { config, introspectionResult: undefined }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PS. don't need to do introsepectionResult: undefined
if you have the type defined as introspectionResult?: IntrospectionQuery
// queries need to be strings when using regular http | ||
const query = print(queryAST); | ||
return JSON.stringify({ query, variables }); | ||
}; | ||
|
||
export const fetchGql = ({ url, gqlRequest }) => { | ||
export const fetchGql = ({ url, gqlRequest }: { url: string; gqlRequest: { query: string } }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your TSDoc is incorrect, instead of returning an axios instance that you can use to send future requests, this sends your defined request with axios and returns the AxiosResponse (or throws error, stupid axios).
// environment config | ||
export type NetworkAggregationConfigInput = { | ||
graphqlUrl: string; | ||
documentType: string; | ||
displayName: string; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment here doesn't tell me anything new. You can use TSDoc on types as well to leave a description of the type in intellisense for future devs that use it.
// additional properties after initialising network | ||
export type NetworkAggregationConfig = NetworkAggregationConfigInput & { | ||
schema?: GraphQLSchema; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a helpful comment but it wasn't easily understandable. I had to read the type information to understand what you mean. Maybe something like:
// additional properties after initialising network | |
export type NetworkAggregationConfig = NetworkAggregationConfigInput & { | |
schema?: GraphQLSchema; | |
}; | |
/** | |
* Complete aggregation config that defines the network search setup. This includes the original | |
* config information plus computed fields that are generated in the network search initialization process. | |
*/ | |
export type NetworkAggregationConfig = NetworkAggregationConfigInput & { | |
schema?: GraphQLSchema; | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a nice improvement, for sure!
Naive question: am I the only one who gets bugged by the line length inconsistency?
personally, I'd chop it like this
* Complete aggregation config that defines the network search setup.
* This includes the original config information plus computed fields
* that are generated in the network search initialization process.
* @returns | ||
*/ | ||
const isGqlIntrospectionQuery = (input: unknown): input is IntrospectionQuery => | ||
!!input && typeof input === 'object' && '__schema' in input && typeof input.__schema === 'object'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't typeof input === 'object'
inherently cover the !!
?
(an object
cannot be falsy)
!!input && typeof input === 'object' && '__schema' in input && typeof input.__schema === 'object'; | |
typeof input === 'object' && '__schema' in input && typeof input.__schema === 'object'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it matter that this function would allow input
to be an array customised to include __schema
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1st point:
typeof null
is object - <3 js (so '__schema' in input would break)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2nd point:
I don't think that's a concern. I'm using the getIntrospectionQuery
which is part of the graphql library (conforming to spec) and this check is only happening to check response data from a GQL server.
It queries an automatically generated field in a graphql server, not a user created field.
For an array to occur the graphql spec would need to be changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a null.__schema
? otherwise, !!
is still redundant
introspectionSchema !== undefined ? buildClientSchema(introspectionSchema) : undefined; | ||
return { ...config, schema }; | ||
} catch (error) { | ||
console.error('build schema error', error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if these messages were more descriptive. The best error messages let the reader know what was being attempted, why it failed, and (if possible) what action should be taken.
It is likely that this is about on par with other error messaging in this repo, but working to improve these as we go would be a huge help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for sure - this is just the "retrieve schema" step
buildClientSchema
is not implemented
will be keeping this in mind working on the "build schema" ticket
Please note this is part of the bigger schema federation work merging into feat/featured_search
The broken build issues are for some missing TS types, implicit
any
sThese will be addressed before merging full feature into
develop
branch