Merge pull request #8904 from backstage/blam/bump-graphql-dependencies

Bump GraphQL Dependencies in `graphql` plugins
This commit is contained in:
Fredrik Adelöw
2022-01-13 14:49:58 +01:00
committed by GitHub
16 changed files with 663 additions and 957 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog-graphql': minor
'@backstage/plugin-graphql-backend': patch
---
chore: bumping dependencies in the GraphQL modules and bringing them up to date with the latest `graphql-modules` library
-1
View File
@@ -32,7 +32,6 @@
"@backstage/integration": "^0.7.0",
"@backstage/plugin-app-backend": "^0.3.21",
"@backstage/plugin-auth-backend": "^0.6.0",
"@backstage/plugin-azure-devops-backend": "^0.2.6",
"@backstage/plugin-badges-backend": "^0.1.14",
"@backstage/plugin-catalog-backend": "^0.19.4",
"@backstage/plugin-code-coverage-backend": "^0.1.18",
-3
View File
@@ -40,7 +40,6 @@ import { Config } from '@backstage/config';
import healthcheck from './plugins/healthcheck';
import { metricsInit, metricsHandler } from './metrics';
import auth from './plugins/auth';
import azureDevOps from './plugins/azure-devops';
import catalog from './plugins/catalog';
import codeCoverage from './plugins/codecoverage';
import kubernetes from './plugins/kubernetes';
@@ -117,7 +116,6 @@ async function main() {
);
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
const authEnv = useHotMemoize(module, () => createEnv('auth'));
const azureDevOpsEnv = useHotMemoize(module, () => createEnv('azure-devops'));
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar'));
const searchEnv = useHotMemoize(module, () => createEnv('search'));
@@ -141,7 +139,6 @@ async function main() {
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
apiRouter.use('/tech-insights', await techInsights(techInsightsEnv));
apiRouter.use('/auth', await auth(authEnv));
apiRouter.use('/azure-devops', await azureDevOps(azureDevOpsEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/todo', await todo(todoEnv));
@@ -1,26 +0,0 @@
/*
* 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 { createRouter } from '@backstage/plugin-azure-devops-backend';
import { Router } from 'express';
import type { PluginEnvironment } from '../types';
export default function createPlugin({
logger,
config,
}: PluginEnvironment): Promise<Router> {
return createRouter({ logger, config });
}
+2 -2
View File
@@ -4,13 +4,13 @@
```ts
import { Config } from '@backstage/config';
import { GraphQLModule } from '@graphql-modules/core';
import { Logger as Logger_2 } from 'winston';
import { Module } from 'graphql-modules';
// Warning: (ae-missing-release-tag) "createModule" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function createModule(options: ModuleOptions): Promise<GraphQLModule>;
export function createModule(options: ModuleOptions): Promise<Module>;
// Warning: (ae-missing-release-tag) "ModuleOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-1
View File
@@ -9,7 +9,6 @@ generates:
afterOneFileWrite:
- eslint --fix
config:
contextType: '@graphql-modules/core#ModuleContext'
allowParentTypeOverride: true
useIndexSignature: true
defaultMapper: Partial<{T}>
+6 -4
View File
@@ -34,9 +34,9 @@
"@backstage/catalog-model": "^0.9.7",
"@backstage/config": "^0.1.11",
"@backstage/types": "^0.1.1",
"@graphql-modules/core": "^0.7.17",
"apollo-server": "^2.16.1",
"graphql": "^15.3.0",
"graphql-modules": "^1.0.0",
"apollo-server": "^3.0.0",
"graphql": "^16.0.0",
"graphql-tag": "^2.11.0",
"graphql-type-json": "^0.3.2",
"node-fetch": "^2.6.1",
@@ -47,7 +47,9 @@
"@backstage/test-utils": "^0.2.0",
"@graphql-codegen/cli": "^2.3.1",
"@graphql-codegen/typescript": "^2.4.2",
"@graphql-codegen/typescript-resolvers": "^2.4.2",
"@graphql-codegen/typescript-resolvers": "^2.4.3",
"@graphql-codegen/graphql-modules-preset": "^2.3.2",
"@graphql-tools/schema": "^8.3.1",
"eslint-plugin-graphql": "^4.0.0",
"msw": "^0.35.0"
},
@@ -15,7 +15,7 @@
*/
import { createModule } from './module';
import { execute } from 'graphql';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { ConfigReader } from '@backstage/config';
@@ -23,6 +23,7 @@ import { ReaderEntity } from '../service/client';
import { createLogger } from 'winston';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import { gql } from 'apollo-server';
import { Module, createApplication, testkit } from 'graphql-modules';
describe('Catalog Module', () => {
const worker = setupServer();
@@ -33,6 +34,20 @@ describe('Catalog Module', () => {
},
});
const createMockApplication = (module: Module) => {
const application = createApplication({
modules: [module],
schemaBuilder(input) {
return makeExecutableSchema({
...input,
inheritResolversFromInterfaces: true,
});
},
});
return application;
};
setupRequestMockHandlers(worker);
describe('Default Entity', () => {
@@ -59,20 +74,21 @@ describe('Catalog Module', () => {
];
worker.use(
rest.get(`${mockCatalogBaseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${mockCatalogBaseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockResponse)),
),
);
});
it('should call the catalog client when requesting entities', async () => {
const { schema } = await createModule({
config: mockConfig,
logger: createLogger(),
});
const app = createMockApplication(
await createModule({
config: mockConfig,
logger: createLogger(),
}),
);
const result = await execute({
schema,
const result = await testkit.execute(app, {
document: gql`
query {
catalog {
@@ -116,18 +132,19 @@ describe('Catalog Module', () => {
},
];
worker.use(
rest.get(`${mockCatalogBaseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${mockCatalogBaseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockResponse)),
),
);
const { schema } = await createModule({
config: mockConfig,
logger: createLogger(),
});
const app = createMockApplication(
await createModule({
config: mockConfig,
logger: createLogger(),
}),
);
const result = await execute({
schema,
const result = await testkit.execute(app, {
document: gql`
query {
catalog {
@@ -167,18 +184,19 @@ describe('Catalog Module', () => {
},
];
worker.use(
rest.get(`${mockCatalogBaseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${mockCatalogBaseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockResponse)),
),
);
const { schema } = await createModule({
config: mockConfig,
logger: createLogger(),
});
const app = createMockApplication(
await createModule({
config: mockConfig,
logger: createLogger(),
}),
);
const result = await execute({
schema,
const result = await testkit.execute(app, {
document: gql`
query {
catalog {
@@ -217,18 +235,19 @@ describe('Catalog Module', () => {
},
];
worker.use(
rest.get(`${mockCatalogBaseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${mockCatalogBaseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockResponse)),
),
);
const { schema } = await createModule({
config: mockConfig,
logger: createLogger(),
});
const app = createMockApplication(
await createModule({
config: mockConfig,
logger: createLogger(),
}),
);
const result = await execute({
schema,
const result = await testkit.execute(app, {
document: gql`
query {
catalog {
@@ -267,18 +286,19 @@ describe('Catalog Module', () => {
},
];
worker.use(
rest.get(`${mockCatalogBaseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${mockCatalogBaseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockResponse)),
),
);
const { schema } = await createModule({
config: mockConfig,
logger: createLogger(),
});
const app = createMockApplication(
await createModule({
config: mockConfig,
logger: createLogger(),
}),
);
const result = await execute({
schema,
const result = await testkit.execute(app, {
document: gql`
query {
catalog {
+10 -15
View File
@@ -15,8 +15,11 @@
*/
import { Logger } from 'winston';
import { makeExecutableSchema } from 'apollo-server';
import { GraphQLModule } from '@graphql-modules/core';
import {
createModule as createGraphQLModule,
Module,
gql,
} from 'graphql-modules';
import { Resolvers, CatalogQuery } from './types';
import { Config } from '@backstage/config';
import { CatalogClient } from '../service/client';
@@ -29,9 +32,7 @@ export interface ModuleOptions {
config: Config;
}
export async function createModule(
options: ModuleOptions,
): Promise<GraphQLModule> {
export async function createModule(options: ModuleOptions): Promise<Module> {
const catalogClient = new CatalogClient(
options.config.getString('backend.baseUrl'),
);
@@ -62,6 +63,7 @@ export async function createModule(
const {
entity: { kind },
} = rootValue as { entity: Entity };
switch (kind) {
case 'Component':
return 'ComponentMetadata';
@@ -94,16 +96,9 @@ export async function createModule(
},
};
const schema = makeExecutableSchema({
typeDefs,
return createGraphQLModule({
id: 'plugin-catalog-graphql',
typeDefs: gql(typeDefs),
resolvers,
inheritResolversFromInterfaces: true,
});
const module = new GraphQLModule({
extraSchemas: [schema],
logger: options.logger as any,
});
return module;
}
+329 -363
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2020 The Backstage Authors
* Copyright 2022 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.
@@ -18,12 +18,18 @@ import {
GraphQLScalarType,
GraphQLScalarTypeConfig,
} from 'graphql';
import { ModuleContext } from '@graphql-modules/core';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K];
};
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]?: Maybe<T[SubKey]>;
};
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>;
};
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type RequireFields<T, K extends keyof T> = {
[X in Exclude<keyof T, K>]?: T[X];
@@ -39,120 +45,6 @@ export type Scalars = {
JSONObject: any;
};
export type EntityMetadata = {
name: Scalars['String'];
annotations: Scalars['JSONObject'];
annotation?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
label?: Maybe<Scalars['JSON']>;
uid: Scalars['String'];
etag: Scalars['String'];
generation: Scalars['Int'];
};
export type EntityMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type EntityMetadataLabelArgs = {
name: Scalars['String'];
};
export type DefaultEntityMetadata = EntityMetadata & {
__typename?: 'DefaultEntityMetadata';
name: Scalars['String'];
annotations: Scalars['JSONObject'];
annotation?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
label?: Maybe<Scalars['JSON']>;
uid: Scalars['String'];
etag: Scalars['String'];
generation: Scalars['Int'];
};
export type DefaultEntityMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type DefaultEntityMetadataLabelArgs = {
name: Scalars['String'];
};
export type ComponentMetadata = EntityMetadata & {
__typename?: 'ComponentMetadata';
name: Scalars['String'];
annotations: Scalars['JSONObject'];
annotation?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
label?: Maybe<Scalars['JSON']>;
uid: Scalars['String'];
etag: Scalars['String'];
generation: Scalars['Int'];
relationships?: Maybe<Scalars['String']>;
};
export type ComponentMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type ComponentMetadataLabelArgs = {
name: Scalars['String'];
};
export type TemplateMetadata = EntityMetadata & {
__typename?: 'TemplateMetadata';
name: Scalars['String'];
annotations: Scalars['JSONObject'];
annotation?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
label?: Maybe<Scalars['JSON']>;
uid: Scalars['String'];
etag: Scalars['String'];
generation: Scalars['Int'];
updatedBy?: Maybe<Scalars['String']>;
};
export type TemplateMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type TemplateMetadataLabelArgs = {
name: Scalars['String'];
};
export type TemplateEntitySpec = {
__typename?: 'TemplateEntitySpec';
type: Scalars['String'];
path?: Maybe<Scalars['String']>;
schema: Scalars['JSONObject'];
templater: Scalars['String'];
};
export type ComponentEntitySpec = {
__typename?: 'ComponentEntitySpec';
title: Scalars['String'];
lifecycle: Scalars['String'];
owner: Scalars['String'];
};
export type LocationEntitySpec = {
__typename?: 'LocationEntitySpec';
type: Scalars['String'];
target?: Maybe<Scalars['String']>;
targets: Array<Scalars['String']>;
};
export type DefaultEntitySpec = {
__typename?: 'DefaultEntitySpec';
raw?: Maybe<Scalars['JSONObject']>;
};
export type EntitySpec =
| DefaultEntitySpec
| TemplateEntitySpec
| LocationEntitySpec
| ComponentEntitySpec;
export type CatalogEntity = {
__typename?: 'CatalogEntity';
apiVersion: Scalars['String'];
@@ -166,31 +58,128 @@ export type CatalogQuery = {
list: Array<CatalogEntity>;
};
export type ComponentEntitySpec = {
__typename?: 'ComponentEntitySpec';
lifecycle: Scalars['String'];
owner: Scalars['String'];
type: Scalars['String'];
};
export type ComponentMetadata = EntityMetadata & {
__typename?: 'ComponentMetadata';
annotation?: Maybe<Scalars['JSON']>;
annotations: Scalars['JSONObject'];
etag: Scalars['String'];
generation: Scalars['Int'];
label?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
name: Scalars['String'];
relationships?: Maybe<Scalars['String']>;
uid: Scalars['String'];
};
export type ComponentMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type ComponentMetadataLabelArgs = {
name: Scalars['String'];
};
export type DefaultEntityMetadata = EntityMetadata & {
__typename?: 'DefaultEntityMetadata';
annotation?: Maybe<Scalars['JSON']>;
annotations: Scalars['JSONObject'];
etag: Scalars['String'];
generation: Scalars['Int'];
label?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
name: Scalars['String'];
uid: Scalars['String'];
};
export type DefaultEntityMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type DefaultEntityMetadataLabelArgs = {
name: Scalars['String'];
};
export type DefaultEntitySpec = {
__typename?: 'DefaultEntitySpec';
raw?: Maybe<Scalars['JSONObject']>;
};
export type EntityMetadata = {
annotation?: Maybe<Scalars['JSON']>;
annotations: Scalars['JSONObject'];
etag: Scalars['String'];
generation: Scalars['Int'];
label?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
name: Scalars['String'];
uid: Scalars['String'];
};
export type EntityMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type EntityMetadataLabelArgs = {
name: Scalars['String'];
};
export type EntitySpec =
| ComponentEntitySpec
| DefaultEntitySpec
| TemplateEntitySpec;
export type Query = {
__typename?: 'Query';
catalog: CatalogQuery;
};
export type TemplateEntitySpec = {
__typename?: 'TemplateEntitySpec';
path?: Maybe<Scalars['String']>;
schema: Scalars['JSONObject'];
templater: Scalars['String'];
type: Scalars['String'];
};
export type TemplateMetadata = EntityMetadata & {
__typename?: 'TemplateMetadata';
annotation?: Maybe<Scalars['JSON']>;
annotations: Scalars['JSONObject'];
etag: Scalars['String'];
generation: Scalars['Int'];
label?: Maybe<Scalars['JSON']>;
labels: Scalars['JSONObject'];
name: Scalars['String'];
uid: Scalars['String'];
updatedBy?: Maybe<Scalars['String']>;
};
export type TemplateMetadataAnnotationArgs = {
name: Scalars['String'];
};
export type TemplateMetadataLabelArgs = {
name: Scalars['String'];
};
export type WithIndex<TObject> = TObject & Record<string, any>;
export type ResolversObject<TObject> = WithIndex<TObject>;
export type ResolverTypeWrapper<T> = Promise<T> | T;
export type LegacyStitchingResolver<TResult, TParent, TContext, TArgs> = {
fragment: string;
export type ResolverWithResolve<TResult, TParent, TContext, TArgs> = {
resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
};
export type NewStitchingResolver<TResult, TParent, TContext, TArgs> = {
selectionSet: string;
resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
};
export type StitchingResolver<TResult, TParent, TContext, TArgs> =
| LegacyStitchingResolver<TResult, TParent, TContext, TArgs>
| NewStitchingResolver<TResult, TParent, TContext, TArgs>;
export type Resolver<TResult, TParent = {}, TContext = {}, TArgs = {}> =
| ResolverFn<TResult, TParent, TContext, TArgs>
| StitchingResolver<TResult, TParent, TContext, TArgs>;
| ResolverWithResolve<TResult, TParent, TContext, TArgs>;
export type ResolverFn<TResult, TParent, TContext, TArgs> = (
parent: TParent,
@@ -204,7 +193,7 @@ export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = (
args: TArgs,
context: TContext,
info: GraphQLResolveInfo,
) => AsyncIterator<TResult> | Promise<AsyncIterator<TResult>>;
) => AsyncIterable<TResult> | Promise<AsyncIterable<TResult>>;
export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
parent: TParent,
@@ -267,8 +256,9 @@ export type TypeResolveFn<TTypes, TParent = {}, TContext = {}> = (
info: GraphQLResolveInfo,
) => Maybe<TTypes> | Promise<Maybe<TTypes>>;
export type IsTypeOfResolverFn<T = {}> = (
export type IsTypeOfResolverFn<T = {}, TContext = {}> = (
obj: T,
context: TContext,
info: GraphQLResolveInfo,
) => boolean | Promise<boolean>;
@@ -289,66 +279,202 @@ export type DirectiveResolverFn<
/** Mapping between all available schema types and the resolvers types */
export type ResolversTypes = ResolversObject<{
JSON: ResolverTypeWrapper<Partial<Scalars['JSON']>>;
JSONObject: ResolverTypeWrapper<Partial<Scalars['JSONObject']>>;
EntityMetadata:
| ResolversTypes['DefaultEntityMetadata']
| ResolversTypes['ComponentMetadata']
| ResolversTypes['TemplateMetadata'];
String: ResolverTypeWrapper<Partial<Scalars['String']>>;
Int: ResolverTypeWrapper<Partial<Scalars['Int']>>;
DefaultEntityMetadata: ResolverTypeWrapper<Partial<DefaultEntityMetadata>>;
ComponentMetadata: ResolverTypeWrapper<Partial<ComponentMetadata>>;
TemplateMetadata: ResolverTypeWrapper<Partial<TemplateMetadata>>;
TemplateEntitySpec: ResolverTypeWrapper<Partial<TemplateEntitySpec>>;
ComponentEntitySpec: ResolverTypeWrapper<Partial<ComponentEntitySpec>>;
LocationEntitySpec: ResolverTypeWrapper<Partial<LocationEntitySpec>>;
DefaultEntitySpec: ResolverTypeWrapper<Partial<DefaultEntitySpec>>;
EntitySpec: Partial<
| ResolversTypes['DefaultEntitySpec']
| ResolversTypes['TemplateEntitySpec']
| ResolversTypes['LocationEntitySpec']
| ResolversTypes['ComponentEntitySpec']
>;
Boolean: ResolverTypeWrapper<Partial<Scalars['Boolean']>>;
CatalogEntity: ResolverTypeWrapper<
Partial<
Omit<CatalogEntity, 'spec'> & { spec: ResolversTypes['EntitySpec'] }
>
>;
CatalogQuery: ResolverTypeWrapper<Partial<CatalogQuery>>;
ComponentEntitySpec: ResolverTypeWrapper<Partial<ComponentEntitySpec>>;
ComponentMetadata: ResolverTypeWrapper<Partial<ComponentMetadata>>;
DefaultEntityMetadata: ResolverTypeWrapper<Partial<DefaultEntityMetadata>>;
DefaultEntitySpec: ResolverTypeWrapper<Partial<DefaultEntitySpec>>;
EntityMetadata:
| ResolversTypes['ComponentMetadata']
| ResolversTypes['DefaultEntityMetadata']
| ResolversTypes['TemplateMetadata'];
EntitySpec: Partial<
| ResolversTypes['ComponentEntitySpec']
| ResolversTypes['DefaultEntitySpec']
| ResolversTypes['TemplateEntitySpec']
>;
Int: ResolverTypeWrapper<Partial<Scalars['Int']>>;
JSON: ResolverTypeWrapper<Partial<Scalars['JSON']>>;
JSONObject: ResolverTypeWrapper<Partial<Scalars['JSONObject']>>;
Query: ResolverTypeWrapper<{}>;
Boolean: ResolverTypeWrapper<Partial<Scalars['Boolean']>>;
String: ResolverTypeWrapper<Partial<Scalars['String']>>;
TemplateEntitySpec: ResolverTypeWrapper<Partial<TemplateEntitySpec>>;
TemplateMetadata: ResolverTypeWrapper<Partial<TemplateMetadata>>;
}>;
/** Mapping between all available schema types and the resolvers parents */
export type ResolversParentTypes = ResolversObject<{
JSON: Partial<Scalars['JSON']>;
JSONObject: Partial<Scalars['JSONObject']>;
EntityMetadata:
| ResolversParentTypes['DefaultEntityMetadata']
| ResolversParentTypes['ComponentMetadata']
| ResolversParentTypes['TemplateMetadata'];
String: Partial<Scalars['String']>;
Int: Partial<Scalars['Int']>;
DefaultEntityMetadata: Partial<DefaultEntityMetadata>;
ComponentMetadata: Partial<ComponentMetadata>;
TemplateMetadata: Partial<TemplateMetadata>;
TemplateEntitySpec: Partial<TemplateEntitySpec>;
ComponentEntitySpec: Partial<ComponentEntitySpec>;
LocationEntitySpec: Partial<LocationEntitySpec>;
DefaultEntitySpec: Partial<DefaultEntitySpec>;
EntitySpec: Partial<
| ResolversParentTypes['DefaultEntitySpec']
| ResolversParentTypes['TemplateEntitySpec']
| ResolversParentTypes['LocationEntitySpec']
| ResolversParentTypes['ComponentEntitySpec']
>;
Boolean: Partial<Scalars['Boolean']>;
CatalogEntity: Partial<
Omit<CatalogEntity, 'spec'> & { spec: ResolversParentTypes['EntitySpec'] }
>;
CatalogQuery: Partial<CatalogQuery>;
ComponentEntitySpec: Partial<ComponentEntitySpec>;
ComponentMetadata: Partial<ComponentMetadata>;
DefaultEntityMetadata: Partial<DefaultEntityMetadata>;
DefaultEntitySpec: Partial<DefaultEntitySpec>;
EntityMetadata:
| ResolversParentTypes['ComponentMetadata']
| ResolversParentTypes['DefaultEntityMetadata']
| ResolversParentTypes['TemplateMetadata'];
EntitySpec: Partial<
| ResolversParentTypes['ComponentEntitySpec']
| ResolversParentTypes['DefaultEntitySpec']
| ResolversParentTypes['TemplateEntitySpec']
>;
Int: Partial<Scalars['Int']>;
JSON: Partial<Scalars['JSON']>;
JSONObject: Partial<Scalars['JSONObject']>;
Query: {};
Boolean: Partial<Scalars['Boolean']>;
String: Partial<Scalars['String']>;
TemplateEntitySpec: Partial<TemplateEntitySpec>;
TemplateMetadata: Partial<TemplateMetadata>;
}>;
export type CatalogEntityResolvers<
ContextType = any,
ParentType = ResolversParentTypes['CatalogEntity'],
> = ResolversObject<{
apiVersion?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
kind?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
metadata?: Resolver<
Maybe<ResolversTypes['EntityMetadata']>,
ParentType,
ContextType
>;
spec?: Resolver<ResolversTypes['EntitySpec'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type CatalogQueryResolvers<
ContextType = any,
ParentType = ResolversParentTypes['CatalogQuery'],
> = ResolversObject<{
list?: Resolver<
Array<ResolversTypes['CatalogEntity']>,
ParentType,
ContextType
>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type ComponentEntitySpecResolvers<
ContextType = any,
ParentType = ResolversParentTypes['ComponentEntitySpec'],
> = ResolversObject<{
lifecycle?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
owner?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type ComponentMetadataResolvers<
ContextType = any,
ParentType = ResolversParentTypes['ComponentMetadata'],
> = ResolversObject<{
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<ComponentMetadataAnnotationArgs, 'name'>
>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<ComponentMetadataLabelArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
relationships?: Resolver<
Maybe<ResolversTypes['String']>,
ParentType,
ContextType
>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DefaultEntityMetadataResolvers<
ContextType = any,
ParentType = ResolversParentTypes['DefaultEntityMetadata'],
> = ResolversObject<{
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<DefaultEntityMetadataAnnotationArgs, 'name'>
>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<DefaultEntityMetadataLabelArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DefaultEntitySpecResolvers<
ContextType = any,
ParentType = ResolversParentTypes['DefaultEntitySpec'],
> = ResolversObject<{
raw?: Resolver<Maybe<ResolversTypes['JSONObject']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type EntityMetadataResolvers<
ContextType = any,
ParentType = ResolversParentTypes['EntityMetadata'],
> = ResolversObject<{
__resolveType: TypeResolveFn<
'ComponentMetadata' | 'DefaultEntityMetadata' | 'TemplateMetadata',
ParentType,
ContextType
>;
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<EntityMetadataAnnotationArgs, 'name'>
>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<EntityMetadataLabelArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
}>;
export type EntitySpecResolvers<
ContextType = any,
ParentType = ResolversParentTypes['EntitySpec'],
> = ResolversObject<{
__resolveType: TypeResolveFn<
'ComponentEntitySpec' | 'DefaultEntitySpec' | 'TemplateEntitySpec',
ParentType,
ContextType
>;
}>;
export interface JsonScalarConfig
@@ -361,226 +487,66 @@ export interface JsonObjectScalarConfig
name: 'JSONObject';
}
export type EntityMetadataResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['EntityMetadata'],
export type QueryResolvers<
ContextType = any,
ParentType = ResolversParentTypes['Query'],
> = ResolversObject<{
__resolveType: TypeResolveFn<
'DefaultEntityMetadata' | 'ComponentMetadata' | 'TemplateMetadata',
ParentType,
ContextType
>;
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<EntityMetadataAnnotationArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<EntityMetadataLabelArgs, 'name'>
>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
catalog?: Resolver<ResolversTypes['CatalogQuery'], ParentType, ContextType>;
}>;
export type DefaultEntityMetadataResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['DefaultEntityMetadata'],
export type TemplateEntitySpecResolvers<
ContextType = any,
ParentType = ResolversParentTypes['TemplateEntitySpec'],
> = ResolversObject<{
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<DefaultEntityMetadataAnnotationArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<DefaultEntityMetadataLabelArgs, 'name'>
>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type ComponentMetadataResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['ComponentMetadata'],
> = ResolversObject<{
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<ComponentMetadataAnnotationArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<ComponentMetadataLabelArgs, 'name'>
>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
relationships?: Resolver<
Maybe<ResolversTypes['String']>,
ParentType,
ContextType
>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
path?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
schema?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
templater?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type TemplateMetadataResolvers<
ContextType = ModuleContext,
ContextType = any,
ParentType = ResolversParentTypes['TemplateMetadata'],
> = ResolversObject<{
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
annotation?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<TemplateMetadataAnnotationArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
label?: Resolver<
Maybe<ResolversTypes['JSON']>,
ParentType,
ContextType,
RequireFields<TemplateMetadataLabelArgs, 'name'>
>;
labels?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
uid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
etag?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
generation?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
updatedBy?: Resolver<
Maybe<ResolversTypes['String']>,
ParentType,
ContextType
>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type TemplateEntitySpecResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['TemplateEntitySpec'],
> = ResolversObject<{
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
path?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
schema?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
templater?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type ComponentEntitySpecResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['ComponentEntitySpec'],
> = ResolversObject<{
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
lifecycle?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
owner?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type LocationEntitySpecResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['LocationEntitySpec'],
> = ResolversObject<{
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
target?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
targets?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type DefaultEntitySpecResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['DefaultEntitySpec'],
> = ResolversObject<{
raw?: Resolver<Maybe<ResolversTypes['JSONObject']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type EntitySpecResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['EntitySpec'],
> = ResolversObject<{
__resolveType: TypeResolveFn<
| 'DefaultEntitySpec'
| 'TemplateEntitySpec'
| 'LocationEntitySpec'
| 'ComponentEntitySpec',
ParentType,
ContextType
>;
}>;
export type CatalogEntityResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['CatalogEntity'],
> = ResolversObject<{
apiVersion?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
kind?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
metadata?: Resolver<
Maybe<ResolversTypes['EntityMetadata']>,
ParentType,
ContextType
>;
spec?: Resolver<ResolversTypes['EntitySpec'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type CatalogQueryResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['CatalogQuery'],
> = ResolversObject<{
list?: Resolver<
Array<ResolversTypes['CatalogEntity']>,
ParentType,
ContextType
>;
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
}>;
export type QueryResolvers<
ContextType = ModuleContext,
ParentType = ResolversParentTypes['Query'],
> = ResolversObject<{
catalog?: Resolver<ResolversTypes['CatalogQuery'], ParentType, ContextType>;
}>;
export type Resolvers<ContextType = ModuleContext> = ResolversObject<{
JSON?: GraphQLScalarType;
JSONObject?: GraphQLScalarType;
EntityMetadata?: EntityMetadataResolvers<ContextType>;
DefaultEntityMetadata?: DefaultEntityMetadataResolvers<ContextType>;
ComponentMetadata?: ComponentMetadataResolvers<ContextType>;
TemplateMetadata?: TemplateMetadataResolvers<ContextType>;
TemplateEntitySpec?: TemplateEntitySpecResolvers<ContextType>;
ComponentEntitySpec?: ComponentEntitySpecResolvers<ContextType>;
LocationEntitySpec?: LocationEntitySpecResolvers<ContextType>;
DefaultEntitySpec?: DefaultEntitySpecResolvers<ContextType>;
EntitySpec?: EntitySpecResolvers<ContextType>;
export type Resolvers<ContextType = any> = ResolversObject<{
CatalogEntity?: CatalogEntityResolvers<ContextType>;
CatalogQuery?: CatalogQueryResolvers<ContextType>;
ComponentEntitySpec?: ComponentEntitySpecResolvers<ContextType>;
ComponentMetadata?: ComponentMetadataResolvers<ContextType>;
DefaultEntityMetadata?: DefaultEntityMetadataResolvers<ContextType>;
DefaultEntitySpec?: DefaultEntitySpecResolvers<ContextType>;
EntityMetadata?: EntityMetadataResolvers<ContextType>;
EntitySpec?: EntitySpecResolvers<ContextType>;
JSON?: GraphQLScalarType;
JSONObject?: GraphQLScalarType;
Query?: QueryResolvers<ContextType>;
TemplateEntitySpec?: TemplateEntitySpecResolvers<ContextType>;
TemplateMetadata?: TemplateMetadataResolvers<ContextType>;
}>;
/**
* @deprecated
* Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config.
*/
export type IResolvers<ContextType = ModuleContext> = Resolvers<ContextType>;
+39 -19
View File
@@ -14,41 +14,58 @@
* limitations under the License.
*/
const metadataFields = /* GraphQL */ `
name: String!
annotations: JSONObject!
annotation(name: String!): JSON
labels: JSONObject!
label(name: String!): JSON
uid: String!
etag: String!
generation: Int!
`;
const schema = /* GraphQL */ `
scalar JSON
scalar JSONObject
interface EntityMetadata {
${metadataFields}
name: String!
annotations: JSONObject!
annotation(name: String!): JSON
labels: JSONObject!
label(name: String!): JSON
uid: String!
etag: String!
generation: Int!
}
type DefaultEntityMetadata implements EntityMetadata {
${metadataFields}
name: String!
annotations: JSONObject!
annotation(name: String!): JSON
labels: JSONObject!
label(name: String!): JSON
uid: String!
etag: String!
generation: Int!
}
type ComponentMetadata implements EntityMetadata {
${metadataFields}
name: String!
annotations: JSONObject!
annotation(name: String!): JSON
labels: JSONObject!
label(name: String!): JSON
uid: String!
etag: String!
generation: Int!
# mock field to prove extensions working
relationships: String
}
type TemplateMetadata implements EntityMetadata {
${metadataFields}
name: String!
annotations: JSONObject!
annotation(name: String!): JSON
labels: JSONObject!
label(name: String!): JSON
uid: String!
etag: String!
generation: Int!
# mock field to prove extensions working
updatedBy: String
}
# TODO: move this definition into plugin-scaffolder-graphql
type TemplateEntitySpec {
type: String!
@@ -67,8 +84,11 @@ const schema = /* GraphQL */ `
raw: JSONObject
}
union EntitySpec = DefaultEntitySpec | TemplateEntitySpec | ComponentEntitySpec
union EntitySpec =
DefaultEntitySpec
| TemplateEntitySpec
| ComponentEntitySpec
type CatalogEntity {
apiVersion: String!
kind: String!
@@ -28,7 +28,7 @@ describe('Catalog GraphQL Module', () => {
const expectedResponse = [{ id: 'something' }];
worker.use(
rest.get(`${baseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${baseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(expectedResponse)),
),
);
@@ -44,7 +44,7 @@ describe('Catalog GraphQL Module', () => {
const expectedResponse = 'something broke';
worker.use(
rest.get(`${baseUrl}/catalog/entities`, (_, res, ctx) =>
rest.get(`${baseUrl}/api/catalog/entities`, (_, res, ctx) =>
res(ctx.status(500), ctx.text(expectedResponse)),
),
);
@@ -16,7 +16,6 @@
import { Entity, EntityMeta } from '@backstage/catalog-model';
import fetch from 'node-fetch';
import { JsonObject } from '@backstage/types';
export interface ReaderEntityMeta extends EntityMeta {
uid: string;
@@ -28,14 +27,15 @@ export interface ReaderEntityMeta extends EntityMeta {
}
export interface ReaderEntity extends Entity {
metadata: JsonObject & ReaderEntityMeta;
metadata: ReaderEntityMeta;
}
export class CatalogClient {
constructor(private baseUrl: string) {}
async list(): Promise<ReaderEntity[]> {
const res = await fetch(`${this.baseUrl}/catalog/entities`);
const res = await fetch(`${this.baseUrl}/api/catalog/entities`);
if (!res.ok) {
// todo(blam): need some better way to handle errors here
// experiment with throwing the input errors etc and having graphql versions of that
+5 -4
View File
@@ -34,13 +34,14 @@
"@backstage/backend-common": "^0.10.0",
"@backstage/config": "^0.1.8",
"@backstage/plugin-catalog-graphql": "^0.2.14",
"@graphql-modules/core": "^0.7.17",
"@graphql-tools/schema": "^8.3.1",
"graphql-modules": "^1.0.0",
"@types/express": "^4.17.6",
"apollo-server": "^2.16.1",
"apollo-server-express": "^2.16.1",
"apollo-server": "^3.0.0",
"apollo-server-express": "^3.0.0",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"graphql": "^15.3.0",
"graphql": "^16.0.0",
"helmet": "^4.0.0",
"reflect-metadata": "^0.1.13",
"winston": "^3.2.1",
+14 -16
View File
@@ -14,21 +14,16 @@
* limitations under the License.
*/
import { errorHandler, resolvePackagePath } from '@backstage/backend-common';
import { errorHandler } from '@backstage/backend-common';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import fs from 'fs';
import { GraphQLModule } from '@graphql-modules/core';
import { createApplication } from 'graphql-modules';
import { ApolloServer } from 'apollo-server-express';
import { createModule as createCatalogModule } from '@backstage/plugin-catalog-graphql';
import { Config } from '@backstage/config';
import helmet from 'helmet';
const schemaPath = resolvePackagePath(
'@backstage/plugin-graphql-backend',
'schema.gql',
);
import { makeExecutableSchema } from '@graphql-tools/schema';
export interface RouterOptions {
logger: Logger;
@@ -38,22 +33,26 @@ export interface RouterOptions {
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const typeDefs = await fs.promises.readFile(schemaPath, 'utf-8');
const catalogModule = await createCatalogModule(options);
const { schema } = new GraphQLModule({
imports: [catalogModule],
typeDefs,
const { createSchemaForApollo } = createApplication({
modules: [catalogModule],
schemaBuilder(input) {
return makeExecutableSchema({
...input,
inheritResolversFromInterfaces: true,
});
},
});
const server = new ApolloServer({
schema,
schema: createSchemaForApollo(),
logger: options.logger,
introspection: true,
playground: process.env.NODE_ENV === 'development',
});
await server.start();
const router = Router();
router.get('/health', (_, response) => {
@@ -72,7 +71,6 @@ export async function createRouter(
);
router.use(apolloMiddleware);
router.use(errorHandler());
return router;
+191 -462
View File
File diff suppressed because it is too large Load Diff