Merge pull request #1425 from spotify/rugvip/graphiql
graphiql: add GitHub endpoint and add to App
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.12",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.12",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.12",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.12",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.12",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.12",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.12",
|
||||
|
||||
@@ -45,6 +45,10 @@ import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci';
|
||||
import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog';
|
||||
|
||||
import { gitOpsApiRef, GitOpsRestApi } from '@backstage/plugin-gitops-profiles';
|
||||
import {
|
||||
graphQlBrowseApiRef,
|
||||
GraphQLEndpoints,
|
||||
} from '@backstage/plugin-graphiql';
|
||||
|
||||
export const apis = (config: ConfigApi) => {
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -78,7 +82,7 @@ export const apis = (config: ConfigApi) => {
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
const githubAuthApi = builder.add(
|
||||
githubAuthApiRef,
|
||||
GithubAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
@@ -105,5 +109,22 @@ export const apis = (config: ConfigApi) => {
|
||||
|
||||
builder.add(gitOpsApiRef, new GitOpsRestApi('http://localhost:3008'));
|
||||
|
||||
builder.add(
|
||||
graphQlBrowseApiRef,
|
||||
GraphQLEndpoints.from([
|
||||
GraphQLEndpoints.create({
|
||||
id: 'gitlab',
|
||||
title: 'GitLab',
|
||||
url: 'https://gitlab.com/api/graphql',
|
||||
}),
|
||||
GraphQLEndpoints.github({
|
||||
id: 'github',
|
||||
title: 'GitHub',
|
||||
errorApi,
|
||||
githubAuthApi,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
return builder.build();
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
SidebarPinButton,
|
||||
} from '@backstage/core';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { graphiQLRouteRef } from '@backstage/plugin-graphiql';
|
||||
|
||||
const useSidebarLogoStyles = makeStyles({
|
||||
root: {
|
||||
@@ -94,6 +95,11 @@ const Root: FC<{}> = ({ children }) => (
|
||||
<SidebarItem icon={MapIcon} to="tech-radar" text="Tech Radar" />
|
||||
<SidebarItem icon={RuleIcon} to="lighthouse" text="Lighthouse" />
|
||||
<SidebarItem icon={BuildIcon} to="circleci" text="CircleCI" />
|
||||
<SidebarItem
|
||||
icon={graphiQLRouteRef.icon!}
|
||||
to={graphiQLRouteRef.path}
|
||||
text={graphiQLRouteRef.title}
|
||||
/>
|
||||
<SidebarSpace />
|
||||
<SidebarDivider />
|
||||
<SidebarThemeToggle />
|
||||
|
||||
@@ -24,3 +24,4 @@ export { plugin as RegisterComponent } from '@backstage/plugin-register-componen
|
||||
export { plugin as Sentry } from '@backstage/plugin-sentry';
|
||||
export { plugin as GitopsProfiles } from '@backstage/plugin-gitops-profiles';
|
||||
export { plugin as TechDocs } from '@backstage/plugin-techdocs';
|
||||
export { plugin as GraphiQL } from '@backstage/plugin-graphiql';
|
||||
|
||||
@@ -24,6 +24,10 @@ import {
|
||||
AlertApiForwarder,
|
||||
oauthRequestApiRef,
|
||||
OAuthRequestManager,
|
||||
GoogleAuth,
|
||||
googleAuthApiRef,
|
||||
GithubAuth,
|
||||
githubAuthApiRef,
|
||||
} from '@backstage/core';
|
||||
|
||||
// TODO(rugvip): We should likely figure out how to reuse all of these between apps
|
||||
@@ -49,3 +53,25 @@ export const oauthRequestApiFactory = createApiFactory({
|
||||
deps: {},
|
||||
factory: () => new OAuthRequestManager(),
|
||||
});
|
||||
|
||||
export const googleAuthApiFactory = createApiFactory({
|
||||
implements: googleAuthApiRef,
|
||||
deps: { oauthRequestApi: oauthRequestApiRef },
|
||||
factory: ({ oauthRequestApi }) =>
|
||||
GoogleAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
});
|
||||
|
||||
export const githubAuthApiFactory = createApiFactory({
|
||||
implements: githubAuthApiRef,
|
||||
deps: { oauthRequestApi: oauthRequestApiRef },
|
||||
factory: ({ oauthRequestApi }) =>
|
||||
GithubAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -15,15 +15,25 @@
|
||||
*/
|
||||
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { githubAuthApiRef, errorApiRef } from '@backstage/core';
|
||||
import { plugin, GraphQLEndpoints, graphQlBrowseApiRef } from '../src';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(plugin)
|
||||
.registerApiFactory({
|
||||
implements: graphQlBrowseApiRef,
|
||||
deps: {},
|
||||
factory() {
|
||||
deps: {
|
||||
errorApi: errorApiRef,
|
||||
githubAuthApi: githubAuthApiRef,
|
||||
},
|
||||
factory({ errorApi, githubAuthApi }) {
|
||||
return GraphQLEndpoints.from([
|
||||
GraphQLEndpoints.github({
|
||||
id: 'github',
|
||||
title: 'GitHub',
|
||||
errorApi,
|
||||
githubAuthApi,
|
||||
}),
|
||||
GraphQLEndpoints.create({
|
||||
id: 'gitlab',
|
||||
title: 'GitLab',
|
||||
|
||||
@@ -1,64 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g id="surface1">
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 3.449219 18.160156 L 2.585938 17.660156 L 12.195312 1.019531 L 13.058594 1.515625 Z M 3.449219 18.160156 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 2.386719 16.332031 L 21.605469 16.332031 L 21.605469 17.328125 L 2.386719 17.328125 Z M 2.386719 16.332031 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 12.382812 22.441406 L 2.769531 16.890625 L 3.265625 16.027344 L 12.878906 21.578125 Z M 12.382812 22.441406 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 20.730469 7.976562 L 11.117188 2.425781 L 11.617188 1.5625 L 21.230469 7.113281 Z M 20.730469 7.976562 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 3.269531 7.972656 L 2.769531 7.109375 L 12.382812 1.558594 L 12.882812 2.421875 Z M 3.269531 7.972656 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 20.554688 18.160156 L 10.945312 1.515625 L 11.808594 1.019531 L 21.417969 17.660156 Z M 20.554688 18.160156 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 3.148438 6.449219 L 4.144531 6.449219 L 4.144531 17.550781 L 3.148438 17.550781 Z M 3.148438 6.449219 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 19.855469 6.449219 L 20.851562 6.449219 L 20.851562 17.550781 L 19.855469 17.550781 Z M 19.855469 6.449219 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 12.210938 22.019531 L 11.777344 21.265625 L 20.136719 16.441406 L 20.570312 17.191406 Z M 12.210938 22.019531 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 22.171875 17.875 C 21.59375 18.875 20.308594 19.21875 19.308594 18.640625 C 18.304688 18.066406 17.964844 16.78125 18.539062 15.78125 C 19.117188 14.777344 20.398438 14.4375 21.402344 15.011719 C 22.410156 15.59375 22.753906 16.871094 22.171875 17.875 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 5.453125 8.21875 C 4.878906 9.222656 3.59375 9.5625 2.59375 8.988281 C 1.589844 8.410156 1.246094 7.128906 1.824219 6.125 C 2.398438 5.125 3.683594 4.78125 4.6875 5.359375 C 5.6875 5.941406 6.03125 7.21875 5.453125 8.21875 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 1.828125 17.875 C 1.253906 16.871094 1.597656 15.59375 2.597656 15.011719 C 3.601562 14.4375 4.878906 14.777344 5.460938 15.78125 C 6.035156 16.78125 5.695312 18.058594 4.691406 18.640625 C 3.683594 19.21875 2.40625 18.875 1.828125 17.875 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 18.546875 8.21875 C 17.96875 7.21875 18.3125 5.941406 19.3125 5.359375 C 20.316406 4.78125 21.59375 5.125 22.175781 6.125 C 22.753906 7.128906 22.410156 8.40625 21.40625 8.988281 C 20.40625 9.5625 19.121094 9.222656 18.546875 8.21875 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 12 23.746094 C 10.84375 23.746094 9.90625 22.8125 9.90625 21.652344 C 9.90625 20.496094 10.84375 19.558594 12 19.558594 C 13.15625 19.558594 14.09375 20.496094 14.09375 21.652344 C 14.09375 22.804688 13.15625 23.746094 12 23.746094 "
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M 12 4.441406 C 10.84375 4.441406 9.90625 3.503906 9.90625 2.347656 C 9.90625 1.1875 10.84375 0.253906 12 0.253906 C 13.15625 0.253906 14.09375 1.1875 14.09375 2.347656 C 14.09375 3.503906 13.15625 4.441406 12 4.441406 "
|
||||
/>
|
||||
<g>
|
||||
<path d="M 3.449219 18.160156 L 2.585938 17.660156 L 12.195312 1.019531 L 13.058594 1.515625 Z M 3.449219 18.160156" />
|
||||
<path d="M 2.386719 16.332031 L 21.605469 16.332031 L 21.605469 17.328125 L 2.386719 17.328125 Z M 2.386719 16.332031" />
|
||||
<path d="M 12.382812 22.441406 L 2.769531 16.890625 L 3.265625 16.027344 L 12.878906 21.578125 Z M 12.382812 22.441406" />
|
||||
<path d="M 20.730469 7.976562 L 11.117188 2.425781 L 11.617188 1.5625 L 21.230469 7.113281 Z M 20.730469 7.976562" />
|
||||
<path d="M 3.269531 7.972656 L 2.769531 7.109375 L 12.382812 1.558594 L 12.882812 2.421875 Z M 3.269531 7.972656" />
|
||||
<path d="M 20.554688 18.160156 L 10.945312 1.515625 L 11.808594 1.019531 L 21.417969 17.660156 Z M 20.554688 18.160156" />
|
||||
<path d="M 3.148438 6.449219 L 4.144531 6.449219 L 4.144531 17.550781 L 3.148438 17.550781 Z M 3.148438 6.449219" />
|
||||
<path d="M 19.855469 6.449219 L 20.851562 6.449219 L 20.851562 17.550781 L 19.855469 17.550781 Z M 19.855469 6.449219" />
|
||||
<path d="M 12.210938 22.019531 L 11.777344 21.265625 L 20.136719 16.441406 L 20.570312 17.191406 Z M 12.210938 22.019531" />
|
||||
<path d="M 22.171875 17.875 C 21.59375 18.875 20.308594 19.21875 19.308594 18.640625 C 18.304688 18.066406 17.964844 16.78125 18.539062 15.78125 C 19.117188 14.777344 20.398438 14.4375 21.402344 15.011719 C 22.410156 15.59375 22.753906 16.871094 22.171875 17.875" />
|
||||
<path d="M 5.453125 8.21875 C 4.878906 9.222656 3.59375 9.5625 2.59375 8.988281 C 1.589844 8.410156 1.246094 7.128906 1.824219 6.125 C 2.398438 5.125 3.683594 4.78125 4.6875 5.359375 C 5.6875 5.941406 6.03125 7.21875 5.453125 8.21875" />
|
||||
<path d="M 1.828125 17.875 C 1.253906 16.871094 1.597656 15.59375 2.597656 15.011719 C 3.601562 14.4375 4.878906 14.777344 5.460938 15.78125 C 6.035156 16.78125 5.695312 18.058594 4.691406 18.640625 C 3.683594 19.21875 2.40625 18.875 1.828125 17.875" />
|
||||
<path d="M 18.546875 8.21875 C 17.96875 7.21875 18.3125 5.941406 19.3125 5.359375 C 20.316406 4.78125 21.59375 5.125 22.175781 6.125 C 22.753906 7.128906 22.410156 8.40625 21.40625 8.988281 C 20.40625 9.5625 19.121094 9.222656 18.546875 8.21875" />
|
||||
<path d="M 12 23.746094 C 10.84375 23.746094 9.90625 22.8125 9.90625 21.652344 C 9.90625 20.496094 10.84375 19.558594 12 19.558594 C 13.15625 19.558594 14.09375 20.496094 14.09375 21.652344 C 14.09375 22.804688 13.15625 23.746094 12 23.746094" />
|
||||
<path d="M 12 4.441406 C 10.84375 4.441406 9.90625 3.503906 9.90625 2.347656 C 9.90625 1.1875 10.84375 0.253906 12 0.253906 C 13.15625 0.253906 14.09375 1.1875 14.09375 2.347656 C 14.09375 3.503906 13.15625 4.441406 12 4.441406" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { GraphQLBrowseApi, GraphQLEndpoint } from './types';
|
||||
import { ErrorApi, OAuthApi } from '@backstage/core';
|
||||
|
||||
// Helper for generic http endpoints
|
||||
export type EndpointConfig = {
|
||||
@@ -28,6 +29,23 @@ export type EndpointConfig = {
|
||||
headers?: { [name in string]: string };
|
||||
};
|
||||
|
||||
export type GithubEndpointConfig = {
|
||||
id: string;
|
||||
title: string;
|
||||
/**
|
||||
* Github GraphQL API url, defaults to https://api.github.com/graphql
|
||||
*/
|
||||
url?: string;
|
||||
/**
|
||||
* Errors will be posted to the ErrorApi if it is provided.
|
||||
*/
|
||||
errorApi?: ErrorApi;
|
||||
/**
|
||||
* GitHub Auth API used to authenticate requests.
|
||||
*/
|
||||
githubAuthApi: OAuthApi;
|
||||
};
|
||||
|
||||
export class GraphQLEndpoints implements GraphQLBrowseApi {
|
||||
// Create a support
|
||||
static create(config: EndpointConfig): GraphQLEndpoint {
|
||||
@@ -51,6 +69,84 @@ export class GraphQLEndpoints implements GraphQLBrowseApi {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GitHub GraphQL endpoint that uses the GithubAuth API to authenticate requests.
|
||||
*
|
||||
* If a request requires more permissions than is granted by the existing session,
|
||||
* the fetcher will automatically ask for the additional scopes that are required.
|
||||
*/
|
||||
static github(config: GithubEndpointConfig): GraphQLEndpoint {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
url = 'https://api.github.com/graphql',
|
||||
errorApi,
|
||||
githubAuthApi,
|
||||
} = config;
|
||||
type ResponseBody = {
|
||||
errors?: Array<{ type: string; message: string }>;
|
||||
};
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
fetcher: async (params: any) => {
|
||||
let retried = false;
|
||||
|
||||
const doRequest = async (): Promise<any> => {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${await githubAuthApi.getAccessToken()}`,
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Request failed with status ${res.status} ${res.statusText}`,
|
||||
);
|
||||
}
|
||||
const data = (await res.json()) as ResponseBody;
|
||||
|
||||
if (!data.errors || retried) {
|
||||
return data;
|
||||
}
|
||||
retried = true;
|
||||
|
||||
const missingScopes = data.errors
|
||||
.filter(({ type }) => type === 'INSUFFICIENT_SCOPES')
|
||||
.flatMap(({ message }) => {
|
||||
const scopesMatch = message.match(
|
||||
/one of the following scopes: (\[.*?\])/,
|
||||
);
|
||||
if (!scopesMatch) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const scopes = JSON.parse(scopesMatch[1].replace(/'/g, '"'));
|
||||
return scopes;
|
||||
} catch (error) {
|
||||
if (errorApi) {
|
||||
errorApi.post(
|
||||
new Error(
|
||||
`Failed to parse scope string "${scopesMatch[1]}", ${error}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
await githubAuthApi.getAccessToken(missingScopes);
|
||||
return doRequest();
|
||||
};
|
||||
|
||||
return await doRequest();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static from(endpoints: GraphQLEndpoint[]) {
|
||||
return new GraphQLEndpoints(endpoints);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user