Merge pull request #16092 from RobotSail/trpc-api

Add tRPC API Definition Widget
This commit is contained in:
Fredrik Adelöw
2023-02-03 10:57:05 +01:00
committed by GitHub
14 changed files with 188 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-api-docs': minor
---
Adds a new tRPC API definition widget which users can utilize to integrate their tRPC API definitions into Backstage.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-model': minor
---
Adds the tRPC API type to the catalog.
+1
View File
@@ -27,6 +27,7 @@
"create-plugin": "echo \"use 'yarn new' instead\"",
"release": "node scripts/prepare-release.js && changeset version && yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}' '.changeset/*.json' && yarn install --no-immutable",
"prettier:check": "prettier --check .",
"prettier:fix": "prettier --write .",
"storybook": "yarn ./storybook run start",
"snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false",
"snyk:test:package": "yarn snyk:test --include",
@@ -6,6 +6,7 @@ metadata:
spec:
targets:
- ./apis/hello-world-api.yaml
- ./apis/hello-world-trpc-api.yaml
- ./apis/petstore-api.yaml
- ./apis/spotify-api.yaml
- ./apis/streetlights-api.yaml
@@ -0,0 +1,25 @@
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: hello-world-trpc
description: Hello World example for tRPC
spec:
type: trpc
lifecycle: experimental
owner: team-c
definition: |
import { z } from 'zod';
import { publicProcedure, router } from '../trpc';
export const apiRouter = router({
version: publicProcedure.query(() => {
return { version: '0.42.0' };
}),
hello: publicProcedure
.input(z.object({ username: z.string().nullish() }).nullish())
.query(({ input, ctx }) => {
return {
text: `hello ${input?.username ?? ctx.user?.name ?? 'world'}`,
};
}),
});
@@ -46,7 +46,7 @@
"type": {
"type": "string",
"description": "The type of the API definition.",
"examples": ["openapi", "asyncapi", "graphql", "grpc"],
"examples": ["openapi", "asyncapi", "graphql", "grpc", "trpc"],
"minLength": 1
},
"lifecycle": {
+10
View File
@@ -173,4 +173,14 @@ export const ProvidedApisCard: (props: {
export const ProvidingComponentsCard: (props: {
variant?: InfoCardVariants;
}) => JSX.Element;
// @public (undocumented)
export const TrpcApiDefinitionWidget: (
props: TrpcApiDefinitionWidgetProps,
) => JSX.Element;
// @public (undocumented)
export type TrpcApiDefinitionWidgetProps = {
definition: string;
};
```
+15
View File
@@ -32,6 +32,7 @@ import graphqlApiEntity from './graphql-example-api.yaml';
import invalidLanguageApiEntity from './invalid-language-example-api.yaml';
import openapiApiEntity from './openapi-example-api.yaml';
import otherApiEntity from './other-example-api.yaml';
import trpcApiEntity from './trpc-example-api.yaml';
const mockEntities = [
openapiApiEntity,
@@ -39,6 +40,7 @@ const mockEntities = [
graphqlApiEntity,
invalidLanguageApiEntity,
otherApiEntity,
trpcApiEntity,
] as unknown as Entity[];
createDevApp()
@@ -137,4 +139,17 @@ createDevApp()
</Page>
),
})
.addPage({
title: 'tRPC',
element: (
<Page themeId="home">
<Header title="tRPC" />
<Content>
<EntityProvider entity={trpcApiEntity as any as Entity}>
<EntityApiDefinitionCard />
</EntityProvider>
</Content>
</Page>
),
})
.render();
@@ -0,0 +1,25 @@
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: hello-world-trpc
description: Hello World example for tRPC
spec:
type: trpc
lifecycle: experimental
owner: team-c
definition: |
import { z } from 'zod';
import { publicProcedure, router } from '../trpc';
export const apiRouter = router({
version: publicProcedure.query(() => {
return { version: '0.42.0' };
}),
hello: publicProcedure
.input(z.object({ username: z.string().nullish() }).nullish())
.query(({ input, ctx }) => {
return {
text: `hello ${input?.username ?? ctx.user?.name ?? 'world'}`,
};
}),
});
@@ -18,6 +18,7 @@ import { AsyncApiDefinitionWidget } from '../AsyncApiDefinitionWidget';
import { GraphQlDefinitionWidget } from '../GraphQlDefinitionWidget';
import { OpenApiDefinitionWidget } from '../OpenApiDefinitionWidget';
import { GrpcApiDefinitionWidget } from '../GrpcApiDefinitionWidget';
import { TrpcApiDefinitionWidget } from '../TrpcDefinitionWidget';
/** @public */
export type ApiDefinitionWidget = {
@@ -61,5 +62,12 @@ export function defaultDefinitionWidgets(): ApiDefinitionWidget[] {
<GrpcApiDefinitionWidget definition={definition} />
),
},
{
type: 'trpc',
title: 'tRPC',
component: definition => (
<TrpcApiDefinitionWidget definition={definition} />
),
},
];
}
@@ -0,0 +1,32 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
import { TrpcApiDefinitionWidget } from './TrpcApiDefinitionWidget';
describe('<TrpcApiDefinitionWidget />', () => {
it('renders plain text', async () => {
const { getAllByText } = await renderInTestApp(
<TrpcApiDefinitionWidget definition="Hello World" />,
);
expect(
getAllByText((_text, element) => element?.textContent === 'Hello World')
.length,
).toBeGreaterThan(0);
});
});
@@ -0,0 +1,40 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { CodeSnippet } from '@backstage/core-components';
import { useTheme } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
/** @public */
export type TrpcApiDefinitionWidgetProps = {
definition: string;
};
/** @public */
export const TrpcApiDefinitionWidget = (
props: TrpcApiDefinitionWidgetProps,
) => {
const { definition } = props;
const theme = useTheme<BackstageTheme>();
return (
<CodeSnippet
customStyle={{ backgroundColor: theme.palette.background.default }}
text={definition}
language="typescript"
showCopyCodeButton
/>
);
};
@@ -0,0 +1,19 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
TrpcApiDefinitionWidget,
type TrpcApiDefinitionWidgetProps,
} from './TrpcApiDefinitionWidget';
+1
View File
@@ -22,3 +22,4 @@ export * from './ComponentsCards';
export * from './GraphQlDefinitionWidget';
export * from './OpenApiDefinitionWidget';
export * from './PlainApiDefinitionWidget';
export * from './TrpcDefinitionWidget';