Skip to content

Commit 971c93e

Browse files
committed
added options for introspection querying + deprecation UX
1 parent 36fdbbb commit 971c93e

File tree

10 files changed

+158
-67
lines changed

10 files changed

+158
-67
lines changed

bin/update_version.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
22

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

43
@Component({
54
selector: 'app-doc-viewer-deprecated',
65
templateUrl: './doc-viewer-deprecated.component.html',
76
styles: [],
87
})
98
export class DocViewerDeprecatedComponent {
10-
@Input() item?: GraphQLField<any, any> | GraphQLEnumValue;
9+
@Input() isDeprecated = false;
10+
@Input() deprecatedReason = '';
1111
}

packages/altair-app/src/app/modules/altair/components/doc-viewer/doc-viewer-field/doc-viewer-field.component.html

+43-45
Original file line numberDiff line numberDiff line change
@@ -11,78 +11,76 @@
1111
[data]="data.description || ''"
1212
></div>
1313
<app-doc-viewer-deprecated
14-
[item]="data"
14+
[isDeprecated]="data.isDeprecated"
15+
[deprecatedReason]="data.deprecationReason"
1516
></app-doc-viewer-deprecated>
1617
<div class="doc-viewer-item-query-inner">
1718
<button
1819
class="doc-viewer-item-query-add-btn"
1920
(click)="addToEditor({ name: data.name, parentType: parentType })"
2021
track-id="add_query"
21-
>
22+
>
2223
@if (isRootType(parentType)) {
23-
{{
24-
'DOCS_ADD_QUERY_TEXT' | translate
25-
}}
24+
{{ 'DOCS_ADD_QUERY_TEXT' | translate }}
2625
}
2726
@if (!isRootType(parentType)) {
28-
{{
29-
'DOCS_ADD_FRAGMENT_TEXT' | translate
30-
}}
27+
{{ 'DOCS_ADD_FRAGMENT_TEXT' | translate }}
3128
}
3229
</button>
3330
</div>
3431
</div>
3532
</div>
3633
<!--Field arguments-->
3734
@if (data.args && data.args.length) {
38-
<div
39-
class="doc-viewer-section doc-viewer-arguments"
40-
>
35+
<div class="doc-viewer-section doc-viewer-arguments">
4136
<div class="doc-viewer-section-title">
4237
{{ 'DOCS_ARGUMENTS_TEXT' | translate }}
4338
</div>
4439
@for (arg of data.args; track argTrackBy($index, arg)) {
45-
<div
46-
class="doc-viewer-item doc-viewer-item-query"
47-
>
40+
<div class="doc-viewer-item doc-viewer-item-query">
4841
<div class="doc-viewer-item-query-inner">
4942
<span class="no-link-link" (click)="goToType(arg.type.inspect())">
5043
{{ arg.name }}
5144
@if (getDefaultValue(arg)) {
5245
<span class="doc-viewer-item-value"
5346
>= {{ getDefaultValue(arg) }}</span
54-
>
55-
}
56-
</span>
57-
<span
58-
class="doc-viewer-item-type doc-viewer-item-query-type no-link-link"
59-
(click)="goToType(arg.type.inspect())"
6047
>
61-
{{ arg.type.inspect() }}
62-
</span>
63-
</div>
64-
<div
65-
class="doc-viewer-item-query-description"
66-
markdown
67-
[data]="arg.description || ''"
68-
></div>
48+
}
49+
</span>
50+
<span
51+
class="doc-viewer-item-type doc-viewer-item-query-type no-link-link"
52+
(click)="goToType(arg.type.inspect())"
53+
>
54+
{{ arg.type.inspect() }}
55+
</span>
6956
</div>
70-
}
71-
</div>
72-
}
73-
}
57+
<div
58+
class="doc-viewer-item-query-description"
59+
markdown
60+
[data]="arg.description || ''"
61+
></div>
7462

75-
<!--Field type-->
76-
<div class="doc-viewer-section-title">{{ 'DOCS_TYPE_TEXT' | translate }}</div>
77-
@if (gqlSchema && data && gqlSchema.getType(cleanName(data.type.inspect()))) {
78-
<app-doc-viewer-type
79-
[data]="gqlSchema.getType(cleanName(data.type.inspect()))"
80-
[gqlSchema]="gqlSchema"
81-
[sortByOption]="sortByOption"
82-
[hideDeprecatedDocItems]="hideDeprecatedDocItems"
83-
(goToFieldChange)="goToField($event.name, $event.parentType)"
84-
(goToTypeChange)="goToType($event.name)"
85-
(addToEditorChange)="addToEditor($event)"
86-
(sortFieldsByChange)="sortFieldsByChange.emit($event)"
87-
></app-doc-viewer-type>
63+
<app-doc-viewer-deprecated
64+
[isDeprecated]="!!arg.deprecationReason"
65+
[deprecatedReason]="arg.deprecationReason"
66+
></app-doc-viewer-deprecated>
67+
</div>
68+
}
69+
</div>
8870
}
71+
}
72+
73+
<!--Field type-->
74+
<div class="doc-viewer-section-title">{{ 'DOCS_TYPE_TEXT' | translate }}</div>
75+
@if (gqlSchema && data && gqlSchema.getType(cleanName(data.type.inspect()))) {
76+
<app-doc-viewer-type
77+
[data]="gqlSchema.getType(cleanName(data.type.inspect()))"
78+
[gqlSchema]="gqlSchema"
79+
[sortByOption]="sortByOption"
80+
[hideDeprecatedDocItems]="hideDeprecatedDocItems"
81+
(goToFieldChange)="goToField($event.name, $event.parentType)"
82+
(goToTypeChange)="goToType($event.name)"
83+
(addToEditorChange)="addToEditor($event)"
84+
(sortFieldsByChange)="sortFieldsByChange.emit($event)"
85+
></app-doc-viewer-type>
86+
}

packages/altair-app/src/app/modules/altair/components/doc-viewer/doc-viewer-type/doc-viewer-type.component.html

+22-6
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,21 @@
3131
</div>
3232
@for (item of getTypeEnumValues(data); track schemaItemTrackBy($index, item)) {
3333
@if (!(hideDeprecatedDocItems && item?.isDeprecated)) {
34-
<div class="doc-viewer-item doc-viewer-item-query">
34+
<div
35+
class="doc-viewer-item doc-viewer-item-query"
36+
[ngClass]="{ 'doc-viewer-item--deprecated': item.isDeprecated }"
37+
>
3538
<div class="doc-viewer-item-query-inner">
3639
<span class="doc-viewer-item-field doc-viewer-item-value">
3740
{{ item.name }}
3841
</span>
3942
<span class="doc-viewer-item-type">
4043
{{ item.value }}
4144
</span>
42-
<app-doc-viewer-deprecated [item]="item"></app-doc-viewer-deprecated>
45+
<app-doc-viewer-deprecated
46+
[isDeprecated]="item.isDeprecated"
47+
[deprecatedReason]="item.deprecationReason"
48+
></app-doc-viewer-deprecated>
4349
</div>
4450
@if (item.description) {
4551
<div
@@ -127,23 +133,30 @@
127133
track schemaItemTrackBy($index, item)
128134
) {
129135
@if (!(hideDeprecatedDocItems && item?.isDeprecated)) {
130-
<div class="doc-viewer-item doc-viewer-item-query">
136+
<div
137+
class="doc-viewer-item doc-viewer-item-query"
138+
[ngClass]="{ 'doc-viewer-item--deprecated': item.isDeprecated }"
139+
>
131140
<div class="doc-viewer-item-query-inner">
132141
<span
133142
class="no-link-link"
134143
(click)="goToField(item.name, data.name)"
135144
track-id="gotofield_docs"
136145
>
137146
{{ item.name }}
138-
</span>
147+
</span>
139148
@if (item?.args?.length) {
140149
(
141150
@for (
142151
arg of item.args;
143152
track schemaItemTrackBy($index, arg);
144153
let last = $last
145154
) {
146-
<span>
155+
<span
156+
[ngClass]="{
157+
'doc-viewer-item--deprecated': arg.deprecationReason
158+
}"
159+
>
147160
<span
148161
class="doc-viewer-item-field no-link-link"
149162
(click)="goToType(arg.type.inspect())"
@@ -174,7 +187,10 @@
174187
{{ item.type.inspect() }}
175188
</span>
176189
</div>
177-
<app-doc-viewer-deprecated [item]="item"></app-doc-viewer-deprecated>
190+
<app-doc-viewer-deprecated
191+
[isDeprecated]="item.isDeprecated"
192+
[deprecatedReason]="item.deprecationReason"
193+
></app-doc-viewer-deprecated>
178194
<div
179195
class="doc-viewer-item-query-description"
180196
markdown

packages/altair-app/src/app/modules/altair/effects/query.effect.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ export class QueryEffects {
427427
return EMPTY;
428428
})
429429
);
430-
}),
430+
})
431431
);
432432
},
433433
{ dispatch: false }
@@ -598,6 +598,22 @@ export class QueryEffects {
598598
response.state.settings['request.withCredentials'],
599599
handler,
600600
additionalParams: requestHandlerAdditionalParams,
601+
descriptions:
602+
response.state.settings['introspection.options.description'],
603+
specifiedByUrl:
604+
response.state.settings['introspection.options.specifiedByUrl'],
605+
directiveIsRepeatable:
606+
response.state.settings[
607+
'introspection.options.directiveIsRepeatable'
608+
],
609+
schemaDescription:
610+
response.state.settings[
611+
'introspection.options.schemaDescription'
612+
],
613+
inputValueDeprecation:
614+
response.state.settings[
615+
'introspection.options.inputValueDeprecation'
616+
],
601617
})
602618
.pipe(
603619
switchMap((introspectionResponse) => {

packages/altair-app/src/app/modules/altair/services/gql/gql.service.ts

+33-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { throwError as observableThrowError, Observable, of, throwError } from 'rxjs';
1+
import {
2+
throwError as observableThrowError,
3+
Observable,
4+
of,
5+
throwError,
6+
} from 'rxjs';
27

38
import { map, catchError, switchMap, toArray } from 'rxjs/operators';
49
import {
@@ -53,7 +58,10 @@ import { ElectronAppService } from '../electron-app/electron-app.service';
5358
import { ELECTRON_ALLOWED_FORBIDDEN_HEADERS } from '@altairgraphql/electron-interop/build/constants';
5459
import { SendRequestResponse } from 'altair-graphql-core/build/script/types';
5560
import { HttpRequestHandler } from 'altair-graphql-core/build/request/handlers/http';
56-
import { GraphQLRequestHandler, MultiResponseStrategy } from 'altair-graphql-core/build/request/types';
61+
import {
62+
GraphQLRequestHandler,
63+
MultiResponseStrategy,
64+
} from 'altair-graphql-core/build/request/types';
5765
import { PerWindowState } from 'altair-graphql-core/build/types/state/per-window.interfaces';
5866
import { buildResponse } from 'altair-graphql-core/build/request/response-builder';
5967

@@ -78,7 +86,17 @@ interface ResolvedFileVariable {
7886
name: string;
7987
data: File;
8088
}
81-
type IntrospectionRequestOptions = Omit<SendRequestOptions, 'query'>;
89+
interface IntrospectionRequestOptions
90+
extends Omit<
91+
SendRequestOptions,
92+
'query' | 'batchedRequest' | 'files' | 'selectedOperation'
93+
> {
94+
inputValueDeprecation?: boolean;
95+
descriptions?: boolean;
96+
directiveIsRepeatable?: boolean;
97+
schemaDescription?: boolean;
98+
specifiedByUrl?: boolean;
99+
}
82100

83101
interface GraphQLRequestData {
84102
query: string;
@@ -197,23 +215,29 @@ export class GqlService {
197215

198216
// concatenate the responses to get the full introspection data
199217
const builtResponse = buildResponse(
200-
resps.map(r => ({
218+
resps.map((r) => ({
201219
content: r.body,
202-
timestamp: r.requestEndTime
220+
timestamp: r.requestEndTime,
203221
})),
204222
MultiResponseStrategy.CONCATENATE
205223
);
206224

207225
lastResponse.body = builtResponse[0]?.content ?? '';
208-
return of(lastResponse)
209-
}),
210-
)
226+
return of(lastResponse);
227+
})
228+
);
211229
}
212230

213231
private _getIntrospectionRequest(opts: IntrospectionRequestOptions) {
214232
const requestOpts: SendRequestOptions = {
215233
url: opts.url,
216-
query: getIntrospectionQuery(),
234+
query: getIntrospectionQuery({
235+
descriptions: opts.descriptions ?? true,
236+
inputValueDeprecation: opts.inputValueDeprecation,
237+
directiveIsRepeatable: opts.directiveIsRepeatable,
238+
schemaDescription: opts.schemaDescription,
239+
specifiedByUrl: opts.specifiedByUrl,
240+
}),
217241
headers: opts.headers,
218242
method: opts.method,
219243
withCredentials: opts.withCredentials,

packages/altair-app/src/scss/components/_doc-viewer.scss

+12
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,18 @@ app-doc-viewer {
314314
}
315315
}
316316

317+
.doc-viewer-item--deprecated {
318+
// text-decoration: line-through;
319+
// text-decoration-line: grammar-error;
320+
text-decoration-color: rgba(var(--rgb-red), 0.5);
321+
text-decoration-style: wavy;
322+
opacity: 0.6;
323+
transition: all 1s ease;
324+
&:hover {
325+
opacity: 1;
326+
}
327+
}
328+
317329
.doc-viewer-item-query {
318330
padding-left: 10px;
319331
padding-right: 75px;

packages/altair-core/src/types/state/settings.interfaces.ts

+25
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,31 @@ export interface SettingsState {
157157
* Enable the scrollbar in the tab list
158158
*/
159159
enableTablistScrollbar?: boolean;
160+
161+
/**
162+
* Whether to include descriptions in the introspection result
163+
*/
164+
'introspection.options.description'?: boolean;
165+
166+
/**
167+
* Whether to include `specifiedByUrl` in the introspection result
168+
*/
169+
'introspection.options.specifiedByUrl'?: boolean;
170+
171+
/**
172+
* Whether to include `isRepeatable` flag on directives
173+
*/
174+
'introspection.options.directiveIsRepeatable'?: boolean;
175+
176+
/**
177+
* Whether to include `description` field on schema
178+
*/
179+
'introspection.options.schemaDescription'?: boolean;
180+
181+
/**
182+
* Whether target GraphQL server supports deprecation of input values
183+
*/
184+
'introspection.options.inputValueDeprecation'?: boolean;
160185
}
161186

162187
// Partial settings state for generating partial validator

test-server/src/schema/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const rootTypeDef = `#graphql
3333
A field that resolves slowly.
3434
Maybe you want to @defer this field ;)
3535
"""
36-
slowField(waitFor: Int! = 5000): String
36+
slowField(waitFor: Int! = 5000, forDepth: Int @deprecated(reason: "For testing arg deprecation")): String
3737
}
3838
type Mutation
3939
type Subscription {

0 commit comments

Comments
 (0)