Skip to content

Commit

Permalink
feat(core): Enable Collection query by slug
Browse files Browse the repository at this point in the history
Relates to #335
  • Loading branch information
michaelbromley committed Jun 4, 2020
1 parent 5b4d3db commit d5586bc
Show file tree
Hide file tree
Showing 21 changed files with 160 additions and 50 deletions.
4 changes: 3 additions & 1 deletion packages/admin-ui/src/lib/core/src/common/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2905,6 +2905,7 @@ export type Query = {
assets: AssetList;
channel?: Maybe<Channel>;
channels: Array<Channel>;
/** Get a Collection either by id or slug. If neither id nor slug is speicified, an error will result. */
collection?: Maybe<Collection>;
collectionFilters: Array<ConfigurableOperationDefinition>;
collections: CollectionList;
Expand Down Expand Up @@ -2982,7 +2983,8 @@ export type QueryChannelArgs = {


export type QueryCollectionArgs = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ export type CreateCollectionTranslationInput = {
name: Scalars['String'];
slug: Scalars['String'];
description: Scalars['String'];
customFields?: Maybe<Scalars['JSON']>;
};

export type CreateCountryInput = {
Expand Down Expand Up @@ -2783,6 +2784,7 @@ export type Query = {
channel?: Maybe<Channel>;
activeChannel: Channel;
collections: CollectionList;
/** Get a Collection either by id or slug. If neither id nor slug is speicified, an error will result. */
collection?: Maybe<Collection>;
collectionFilters: Array<ConfigurableOperationDefinition>;
countries: CountryList;
Expand Down Expand Up @@ -2853,7 +2855,8 @@ export type QueryCollectionsArgs = {
};

export type QueryCollectionArgs = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};

export type QueryCountriesArgs = {
Expand Down Expand Up @@ -3419,6 +3422,7 @@ export type UpdateCollectionTranslationInput = {
name?: Maybe<Scalars['String']>;
slug?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateCountryInput = {
Expand Down
3 changes: 2 additions & 1 deletion packages/common/src/generated-shop-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1928,7 +1928,8 @@ export type QueryCollectionsArgs = {
};

export type QueryCollectionArgs = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};

export type QueryOrderArgs = {
Expand Down
6 changes: 5 additions & 1 deletion packages/common/src/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ export type CreateCollectionTranslationInput = {
name: Scalars['String'];
slug: Scalars['String'];
description: Scalars['String'];
customFields?: Maybe<Scalars['JSON']>;
};

export type CreateCountryInput = {
Expand Down Expand Up @@ -2867,6 +2868,7 @@ export type Query = {
channel?: Maybe<Channel>;
activeChannel: Channel;
collections: CollectionList;
/** Get a Collection either by id or slug. If neither id nor slug is speicified, an error will result. */
collection?: Maybe<Collection>;
collectionFilters: Array<ConfigurableOperationDefinition>;
countries: CountryList;
Expand Down Expand Up @@ -2944,7 +2946,8 @@ export type QueryCollectionsArgs = {


export type QueryCollectionArgs = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};


Expand Down Expand Up @@ -3539,6 +3542,7 @@ export type UpdateCollectionTranslationInput = {
name?: Maybe<Scalars['String']>;
slug?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateCountryInput = {
Expand Down
34 changes: 31 additions & 3 deletions packages/core/e2e/collection.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ describe('Collection resolver', () => {
});
});

it('collection query', async () => {
it('collection by id', async () => {
const result = await adminClient.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {
id: computersCollection.id,
});
Expand All @@ -342,6 +342,34 @@ describe('Collection resolver', () => {
expect(result.collection.id).toBe(computersCollection.id);
});

it('collection by slug', async () => {
const result = await adminClient.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {
slug: computersCollection.slug,
});
if (!result.collection) {
fail(`did not return the collection`);
return;
}
expect(result.collection.id).toBe(computersCollection.id);
});

it(
'throws if neither id nor slug provided',
assertThrowsWithMessage(async () => {
await adminClient.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {});
}, 'Either the Collection id or slug must be provided'),
);

it(
'throws if id and slug do not refer to the same Product',
assertThrowsWithMessage(async () => {
await adminClient.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {
id: computersCollection.id,
slug: pearCollection.slug,
});
}, 'The provided id and slug refer to different Collections'),
);

it('parent field', async () => {
const result = await adminClient.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {
id: computersCollection.id,
Expand Down Expand Up @@ -1266,8 +1294,8 @@ describe('Collection resolver', () => {
});

export const GET_COLLECTION = gql`
query GetCollection($id: ID!) {
collection(id: $id) {
query GetCollection($id: ID, $slug: String) {
collection(id: $id, slug: $slug) {
...Collection
productVariants {
items {
Expand Down
22 changes: 14 additions & 8 deletions packages/core/e2e/graphql/generated-e2e-admin-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ export type CreateCollectionTranslationInput = {
name: Scalars['String'];
slug: Scalars['String'];
description: Scalars['String'];
customFields?: Maybe<Scalars['JSON']>;
};

export type CreateCountryInput = {
Expand Down Expand Up @@ -2783,6 +2784,7 @@ export type Query = {
channel?: Maybe<Channel>;
activeChannel: Channel;
collections: CollectionList;
/** Get a Collection either by id or slug. If neither id nor slug is speicified, an error will result. */
collection?: Maybe<Collection>;
collectionFilters: Array<ConfigurableOperationDefinition>;
countries: CountryList;
Expand Down Expand Up @@ -2853,7 +2855,8 @@ export type QueryCollectionsArgs = {
};

export type QueryCollectionArgs = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};

export type QueryCountriesArgs = {
Expand Down Expand Up @@ -3419,6 +3422,7 @@ export type UpdateCollectionTranslationInput = {
name?: Maybe<Scalars['String']>;
slug?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateCountryInput = {
Expand Down Expand Up @@ -3710,7 +3714,8 @@ export type GetProductsWithVariantIdsQuery = { __typename?: 'Query' } & {
};

export type GetCollectionQueryVariables = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};

export type GetCollectionQuery = { __typename?: 'Query' } & {
Expand Down Expand Up @@ -5457,16 +5462,17 @@ export type DisableProductMutation = { __typename?: 'Mutation' } & {
};

export type GetCollectionVariantsQueryVariables = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};

export type GetCollectionVariantsQuery = { __typename?: 'Query' } & {
collection?: Maybe<
{ __typename?: 'Collection' } & {
productVariants: { __typename?: 'ProductVariantList' } & {
items: Array<{ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id' | 'name'>>;
};
}
{ __typename?: 'Collection' } & Pick<Collection, 'id'> & {
productVariants: { __typename?: 'ProductVariantList' } & {
items: Array<{ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id' | 'name'>>;
};
}
>;
};

Expand Down
3 changes: 2 additions & 1 deletion packages/core/e2e/graphql/generated-e2e-shop-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1928,7 +1928,8 @@ export type QueryCollectionsArgs = {
};

export type QueryCollectionArgs = {
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};

export type QueryOrderArgs = {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/e2e/product.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import gql from 'graphql-tag';
import path from 'path';

import { initialData } from '../../../e2e-common/e2e-initial-data';
import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';

import {
AddOptionGroupToProduct,
Expand Down Expand Up @@ -249,7 +249,7 @@ describe('Product resolver', () => {
GET_PRODUCT_SIMPLE,
{},
);
}, 'Either the product id or slug must be provided'),
}, 'Either the Product id or slug must be provided'),
);

it(
Expand Down
13 changes: 11 additions & 2 deletions packages/core/e2e/shop-catalog.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,14 @@ describe('Shop catalog', () => {
]);
});

it('collection by slug', async () => {
const result = await shopClient.query<
GetCollectionVariants.Query,
GetCollectionVariants.Variables
>(GET_COLLECTION_VARIANTS, { slug: collection.slug });
expect(result.collection?.id).toBe(collection.id);
});

it('omits variants from disabled products', async () => {
await adminClient.query<DisableProduct.Mutation, DisableProduct.Variables>(DISABLE_PRODUCT, {
id: 'T_17',
Expand Down Expand Up @@ -393,8 +401,9 @@ const DISABLE_PRODUCT = gql`
`;

const GET_COLLECTION_VARIANTS = gql`
query GetCollectionVariants($id: ID!) {
collection(id: $id) {
query GetCollectionVariants($id: ID, $slug: String) {
collection(id: $id, slug: $slug) {
id
productVariants {
items {
id
Expand Down
39 changes: 24 additions & 15 deletions packages/core/src/api/config/graphql-custom-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,23 +130,32 @@ export function addGraphQLCustomFields(
`;
}

if (writeableLocaleStringFields && schema.getType(`${entityName}TranslationInput`)) {
if (writeableLocaleStringFields.length) {
customFieldTypeDefs += `
input ${entityName}TranslationCustomFieldsInput {
${mapToFields(writeableLocaleStringFields, getGraphQlType)}
}
if (writeableLocaleStringFields) {
const translationInputs = [
`${entityName}TranslationInput`,
`Create${entityName}TranslationInput`,
`Update${entityName}TranslationInput`,
];
for (const inputName of translationInputs) {
if (schema.getType(inputName)) {
if (writeableLocaleStringFields.length) {
customFieldTypeDefs += `
input ${inputName}CustomFields {
${mapToFields(writeableLocaleStringFields, getGraphQlType)}
}
extend input ${entityName}TranslationInput {
customFields: ${entityName}TranslationCustomFieldsInput
}
`;
} else {
customFieldTypeDefs += `
extend input ${entityName}TranslationInput {
customFields: JSON
extend input ${inputName} {
customFields: ${inputName}CustomFields
}
`;
} else {
customFieldTypeDefs += `
extend input ${inputName} {
customFields: JSON
}
`;
}
`;
}
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion packages/core/src/api/resolvers/admin/collection.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@vendure/common/lib/generated-types';
import { PaginatedList } from '@vendure/common/lib/shared-types';

import { UserInputError } from '../../../common/error/errors';
import { Translated } from '../../../common/types/locale-types';
import { Collection } from '../../../entity/collection/collection.entity';
import { CollectionService } from '../../../service/services/collection.service';
Expand Down Expand Up @@ -56,7 +57,19 @@ export class CollectionResolver {
@Ctx() ctx: RequestContext,
@Args() args: QueryCollectionArgs,
): Promise<Translated<Collection> | undefined> {
return this.collectionService.findOne(ctx, args.id).then(this.encodeFilters);
let collection: Translated<Collection> | undefined;
if (args.id) {
collection = await this.collectionService.findOne(ctx, args.id);
if (args.slug && collection && collection.slug !== args.slug) {
throw new UserInputError(`error.collection-id-slug-mismatch`);
}
} else if (args.slug) {
collection = await this.collectionService.findOneBySlug(ctx, args.slug);
} else {
throw new UserInputError(`error.collection-id-or-slug-must-be-provided`);
}

return this.encodeFilters(collection);
}

@Mutation()
Expand Down
12 changes: 11 additions & 1 deletion packages/core/src/api/resolvers/shop/shop-products.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,17 @@ export class ShopProductsResolver {
@Ctx() ctx: RequestContext,
@Args() args: QueryCollectionArgs,
): Promise<Translated<Collection> | undefined> {
const collection = await this.collectionService.findOne(ctx, args.id);
let collection: Translated<Collection> | undefined;
if (args.id) {
collection = await this.collectionService.findOne(ctx, args.id);
if (args.slug && collection && collection.slug !== args.slug) {
throw new UserInputError(`error.collection-id-slug-mismatch`);
}
} else if (args.slug) {
collection = await this.collectionService.findOneBySlug(ctx, args.slug);
} else {
throw new UserInputError(`error.collection-id-or-slug-must-be-provided`);
}
if (collection && collection.isPrivate) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
type Query {
collections(options: CollectionListOptions): CollectionList!
collection(id: ID!): Collection
"Get a Collection either by id or slug. If neither id nor slug is speicified, an error will result."
collection(id: ID, slug: String): Collection
collectionFilters: [ConfigurableOperationDefinition!]!
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/api/schema/shop-api/shop.api.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type Query {
activeOrder: Order
availableCountries: [Country!]!
collections(options: CollectionListOptions): CollectionList!
collection(id: ID!): Collection
collection(id: ID, slug: String): Collection
eligibleShippingMethods: [ShippingMethodQuote!]!
me: CurrentUser
nextOrderStates: [String!]!
Expand Down
Loading

0 comments on commit d5586bc

Please sign in to comment.