Skip to content

Commit

Permalink
added options for introspection querying + deprecation UX
Browse files Browse the repository at this point in the history
  • Loading branch information
imolorhe committed Jul 2, 2024
1 parent 36fdbbb commit 971c93e
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 67 deletions.
2 changes: 1 addition & 1 deletion bin/update_version.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash

if [ -z "$1" ]
then
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@if (item?.isDeprecated) {
@if (isDeprecated) {
<div class="doc-viewer-item-query-deprecated">
<span class="doc-viewer-item-query-deprecated-title">
{{ 'DOCS_DEPRECATED_TEXT' | translate }}
</span>
<span markdown [data]="item?.deprecationReason ?? ''"> </span>
<span markdown [data]="deprecatedReason || ''"> </span>
</div>
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Component, Input } from '@angular/core';
import { GraphQLEnumValue, GraphQLField } from 'graphql';

@Component({
selector: 'app-doc-viewer-deprecated',
templateUrl: './doc-viewer-deprecated.component.html',
styles: [],
})
export class DocViewerDeprecatedComponent {
@Input() item?: GraphQLField<any, any> | GraphQLEnumValue;
@Input() isDeprecated = false;
@Input() deprecatedReason = '';
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,78 +11,76 @@
[data]="data.description || ''"
></div>
<app-doc-viewer-deprecated
[item]="data"
[isDeprecated]="data.isDeprecated"
[deprecatedReason]="data.deprecationReason"
></app-doc-viewer-deprecated>
<div class="doc-viewer-item-query-inner">
<button
class="doc-viewer-item-query-add-btn"
(click)="addToEditor({ name: data.name, parentType: parentType })"
track-id="add_query"
>
>
@if (isRootType(parentType)) {
{{
'DOCS_ADD_QUERY_TEXT' | translate
}}
{{ 'DOCS_ADD_QUERY_TEXT' | translate }}
}
@if (!isRootType(parentType)) {
{{
'DOCS_ADD_FRAGMENT_TEXT' | translate
}}
{{ 'DOCS_ADD_FRAGMENT_TEXT' | translate }}
}
</button>
</div>
</div>
</div>
<!--Field arguments-->
@if (data.args && data.args.length) {
<div
class="doc-viewer-section doc-viewer-arguments"
>
<div class="doc-viewer-section doc-viewer-arguments">
<div class="doc-viewer-section-title">
{{ 'DOCS_ARGUMENTS_TEXT' | translate }}
</div>
@for (arg of data.args; track argTrackBy($index, arg)) {
<div
class="doc-viewer-item doc-viewer-item-query"
>
<div class="doc-viewer-item doc-viewer-item-query">
<div class="doc-viewer-item-query-inner">
<span class="no-link-link" (click)="goToType(arg.type.inspect())">
{{ arg.name }}
@if (getDefaultValue(arg)) {
<span class="doc-viewer-item-value"
>= {{ getDefaultValue(arg) }}</span
>
}
</span>
<span
class="doc-viewer-item-type doc-viewer-item-query-type no-link-link"
(click)="goToType(arg.type.inspect())"
>
{{ arg.type.inspect() }}
</span>
</div>
<div
class="doc-viewer-item-query-description"
markdown
[data]="arg.description || ''"
></div>
}
</span>
<span
class="doc-viewer-item-type doc-viewer-item-query-type no-link-link"
(click)="goToType(arg.type.inspect())"
>
{{ arg.type.inspect() }}
</span>
</div>
}
</div>
}
}
<div
class="doc-viewer-item-query-description"
markdown
[data]="arg.description || ''"
></div>

<!--Field type-->
<div class="doc-viewer-section-title">{{ 'DOCS_TYPE_TEXT' | translate }}</div>
@if (gqlSchema && data && gqlSchema.getType(cleanName(data.type.inspect()))) {
<app-doc-viewer-type
[data]="gqlSchema.getType(cleanName(data.type.inspect()))"
[gqlSchema]="gqlSchema"
[sortByOption]="sortByOption"
[hideDeprecatedDocItems]="hideDeprecatedDocItems"
(goToFieldChange)="goToField($event.name, $event.parentType)"
(goToTypeChange)="goToType($event.name)"
(addToEditorChange)="addToEditor($event)"
(sortFieldsByChange)="sortFieldsByChange.emit($event)"
></app-doc-viewer-type>
<app-doc-viewer-deprecated
[isDeprecated]="!!arg.deprecationReason"
[deprecatedReason]="arg.deprecationReason"
></app-doc-viewer-deprecated>
</div>
}
</div>
}
}

<!--Field type-->
<div class="doc-viewer-section-title">{{ 'DOCS_TYPE_TEXT' | translate }}</div>
@if (gqlSchema && data && gqlSchema.getType(cleanName(data.type.inspect()))) {
<app-doc-viewer-type
[data]="gqlSchema.getType(cleanName(data.type.inspect()))"
[gqlSchema]="gqlSchema"
[sortByOption]="sortByOption"
[hideDeprecatedDocItems]="hideDeprecatedDocItems"
(goToFieldChange)="goToField($event.name, $event.parentType)"
(goToTypeChange)="goToType($event.name)"
(addToEditorChange)="addToEditor($event)"
(sortFieldsByChange)="sortFieldsByChange.emit($event)"
></app-doc-viewer-type>
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,21 @@
</div>
@for (item of getTypeEnumValues(data); track schemaItemTrackBy($index, item)) {
@if (!(hideDeprecatedDocItems && item?.isDeprecated)) {
<div class="doc-viewer-item doc-viewer-item-query">
<div
class="doc-viewer-item doc-viewer-item-query"
[ngClass]="{ 'doc-viewer-item--deprecated': item.isDeprecated }"
>
<div class="doc-viewer-item-query-inner">
<span class="doc-viewer-item-field doc-viewer-item-value">
{{ item.name }}
</span>
<span class="doc-viewer-item-type">
{{ item.value }}
</span>
<app-doc-viewer-deprecated [item]="item"></app-doc-viewer-deprecated>
<app-doc-viewer-deprecated
[isDeprecated]="item.isDeprecated"
[deprecatedReason]="item.deprecationReason"
></app-doc-viewer-deprecated>
</div>
@if (item.description) {
<div
Expand Down Expand Up @@ -127,23 +133,30 @@
track schemaItemTrackBy($index, item)
) {
@if (!(hideDeprecatedDocItems && item?.isDeprecated)) {
<div class="doc-viewer-item doc-viewer-item-query">
<div
class="doc-viewer-item doc-viewer-item-query"
[ngClass]="{ 'doc-viewer-item--deprecated': item.isDeprecated }"
>
<div class="doc-viewer-item-query-inner">
<span
class="no-link-link"
(click)="goToField(item.name, data.name)"
track-id="gotofield_docs"
>
{{ item.name }}
</span>
</span>
@if (item?.args?.length) {
(
@for (
arg of item.args;
track schemaItemTrackBy($index, arg);
let last = $last
) {
<span>
<span
[ngClass]="{
'doc-viewer-item--deprecated': arg.deprecationReason
}"
>
<span
class="doc-viewer-item-field no-link-link"
(click)="goToType(arg.type.inspect())"
Expand Down Expand Up @@ -174,7 +187,10 @@
{{ item.type.inspect() }}
</span>
</div>
<app-doc-viewer-deprecated [item]="item"></app-doc-viewer-deprecated>
<app-doc-viewer-deprecated
[isDeprecated]="item.isDeprecated"
[deprecatedReason]="item.deprecationReason"
></app-doc-viewer-deprecated>
<div
class="doc-viewer-item-query-description"
markdown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ export class QueryEffects {
return EMPTY;
})
);
}),
})
);
},
{ dispatch: false }
Expand Down Expand Up @@ -598,6 +598,22 @@ export class QueryEffects {
response.state.settings['request.withCredentials'],
handler,
additionalParams: requestHandlerAdditionalParams,
descriptions:
response.state.settings['introspection.options.description'],
specifiedByUrl:
response.state.settings['introspection.options.specifiedByUrl'],
directiveIsRepeatable:
response.state.settings[
'introspection.options.directiveIsRepeatable'
],
schemaDescription:
response.state.settings[
'introspection.options.schemaDescription'
],
inputValueDeprecation:
response.state.settings[
'introspection.options.inputValueDeprecation'
],
})
.pipe(
switchMap((introspectionResponse) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { throwError as observableThrowError, Observable, of, throwError } from 'rxjs';
import {
throwError as observableThrowError,
Observable,
of,
throwError,
} from 'rxjs';

import { map, catchError, switchMap, toArray } from 'rxjs/operators';
import {
Expand Down Expand Up @@ -53,7 +58,10 @@ import { ElectronAppService } from '../electron-app/electron-app.service';
import { ELECTRON_ALLOWED_FORBIDDEN_HEADERS } from '@altairgraphql/electron-interop/build/constants';
import { SendRequestResponse } from 'altair-graphql-core/build/script/types';
import { HttpRequestHandler } from 'altair-graphql-core/build/request/handlers/http';
import { GraphQLRequestHandler, MultiResponseStrategy } from 'altair-graphql-core/build/request/types';
import {
GraphQLRequestHandler,
MultiResponseStrategy,
} from 'altair-graphql-core/build/request/types';
import { PerWindowState } from 'altair-graphql-core/build/types/state/per-window.interfaces';
import { buildResponse } from 'altair-graphql-core/build/request/response-builder';

Expand All @@ -78,7 +86,17 @@ interface ResolvedFileVariable {
name: string;
data: File;
}
type IntrospectionRequestOptions = Omit<SendRequestOptions, 'query'>;
interface IntrospectionRequestOptions
extends Omit<
SendRequestOptions,
'query' | 'batchedRequest' | 'files' | 'selectedOperation'
> {
inputValueDeprecation?: boolean;
descriptions?: boolean;
directiveIsRepeatable?: boolean;
schemaDescription?: boolean;
specifiedByUrl?: boolean;
}

interface GraphQLRequestData {
query: string;
Expand Down Expand Up @@ -197,23 +215,29 @@ export class GqlService {

// concatenate the responses to get the full introspection data
const builtResponse = buildResponse(
resps.map(r => ({
resps.map((r) => ({
content: r.body,
timestamp: r.requestEndTime
timestamp: r.requestEndTime,
})),
MultiResponseStrategy.CONCATENATE
);

lastResponse.body = builtResponse[0]?.content ?? '';
return of(lastResponse)
}),
)
return of(lastResponse);
})
);
}

private _getIntrospectionRequest(opts: IntrospectionRequestOptions) {
const requestOpts: SendRequestOptions = {
url: opts.url,
query: getIntrospectionQuery(),
query: getIntrospectionQuery({
descriptions: opts.descriptions ?? true,
inputValueDeprecation: opts.inputValueDeprecation,
directiveIsRepeatable: opts.directiveIsRepeatable,
schemaDescription: opts.schemaDescription,
specifiedByUrl: opts.specifiedByUrl,
}),
headers: opts.headers,
method: opts.method,
withCredentials: opts.withCredentials,
Expand Down
12 changes: 12 additions & 0 deletions packages/altair-app/src/scss/components/_doc-viewer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,18 @@ app-doc-viewer {
}
}

.doc-viewer-item--deprecated {
// text-decoration: line-through;
// text-decoration-line: grammar-error;
text-decoration-color: rgba(var(--rgb-red), 0.5);
text-decoration-style: wavy;
opacity: 0.6;
transition: all 1s ease;
&:hover {
opacity: 1;
}
}

.doc-viewer-item-query {
padding-left: 10px;
padding-right: 75px;
Expand Down
25 changes: 25 additions & 0 deletions packages/altair-core/src/types/state/settings.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,31 @@ export interface SettingsState {
* Enable the scrollbar in the tab list
*/
enableTablistScrollbar?: boolean;

/**
* Whether to include descriptions in the introspection result
*/
'introspection.options.description'?: boolean;

/**
* Whether to include `specifiedByUrl` in the introspection result
*/
'introspection.options.specifiedByUrl'?: boolean;

/**
* Whether to include `isRepeatable` flag on directives
*/
'introspection.options.directiveIsRepeatable'?: boolean;

/**
* Whether to include `description` field on schema
*/
'introspection.options.schemaDescription'?: boolean;

/**
* Whether target GraphQL server supports deprecation of input values
*/
'introspection.options.inputValueDeprecation'?: boolean;
}

// Partial settings state for generating partial validator
Expand Down
2 changes: 1 addition & 1 deletion test-server/src/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const rootTypeDef = `#graphql
A field that resolves slowly.
Maybe you want to @defer this field ;)
"""
slowField(waitFor: Int! = 5000): String
slowField(waitFor: Int! = 5000, forDepth: Int @deprecated(reason: "For testing arg deprecation")): String
}
type Mutation
type Subscription {
Expand Down

0 comments on commit 971c93e

Please sign in to comment.