Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/queries": Upcoming Features
---

Add API queries for MarketplaceV2 ([#13255](https://github.com/linode/manager/pull/13255))
1 change: 1 addition & 0 deletions packages/queries/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './iam';
export * from './images';
export * from './linodes';
export * from './locks';
export * from './marketplace';
export * from './netloadbalancers';
export * from './networking';
export * from './networktransfer';
Expand Down
3 changes: 3 additions & 0 deletions packages/queries/src/marketplace/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './keys';
export * from './marketplace';
export * from './requests';
108 changes: 108 additions & 0 deletions packages/queries/src/marketplace/keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
getMarketplaceCategories,
getMarketplacePartners,
getMarketplaceProduct,
getMarketplaceProducts,
getMarketplaceTypes,
} from '@linode/api-v4';
import { createQueryKeys } from '@lukemorales/query-key-factory';

import {
getAllMarketplaceCategories,
getAllMarketplacePartners,
getAllMarketplaceProducts,
getAllMarketplaceTypes,
} from './requests';

import type { Filter, Params } from '@linode/api-v4';

export const marketplaceQueries = createQueryKeys('marketplace', {
product: (productId: number) => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now the api has changed from /marketplace/products/{id} to /marketplace/products/{id}/details

queryFn: () => getMarketplaceProduct(productId),
queryKey: [productId],
}),
products: {
contextQueries: {
all: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getAllMarketplaceProducts(params, filter),
queryKey: [params, filter],
}),
infinite: (filter: Filter = {}) => ({
queryFn: ({ pageParam }) =>
getMarketplaceProducts(
{ page: pageParam as number, page_size: 25 },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should page size be 25 for products api also?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minimum size for a page is 25

filter,
),
queryKey: [filter],
}),
paginated: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getMarketplaceProducts(params, filter),
queryKey: [params, filter],
}),
},
queryKey: null,
},
categories: {
contextQueries: {
all: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getAllMarketplaceCategories(params, filter),
queryKey: [params, filter],
}),
infinite: (filter: Filter = {}) => ({
queryFn: ({ pageParam }) =>
getMarketplaceCategories(
{ page: pageParam as number, page_size: 25 },
filter,
),
queryKey: [filter],
}),
paginated: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getMarketplaceCategories(params, filter),
queryKey: [params, filter],
}),
},
queryKey: null,
},
types: {
contextQueries: {
all: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getAllMarketplaceTypes(params, filter),
queryKey: [params, filter],
}),
infinite: (filter: Filter = {}) => ({
queryFn: ({ pageParam }) =>
getMarketplaceTypes(
{ page: pageParam as number, page_size: 25 },
filter,
),
queryKey: [filter],
}),
paginated: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getMarketplaceTypes(params, filter),
queryKey: [params, filter],
}),
},
queryKey: null,
},
partners: {
contextQueries: {
all: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getAllMarketplacePartners(params, filter),
queryKey: [params, filter],
}),
infinite: (filter: Filter = {}) => ({
queryFn: ({ pageParam }) =>
getMarketplacePartners(
{ page: pageParam as number, page_size: 25 },
filter,
),
queryKey: [filter],
}),
paginated: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getMarketplaceTypes(params, filter),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be getMarketplacePartners, instead of getMarketplaceTypes

queryKey: [params, filter],
}),
},
queryKey: null,
},
});
165 changes: 165 additions & 0 deletions packages/queries/src/marketplace/marketplace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import {

Check warning on line 1 in packages/queries/src/marketplace/marketplace.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (queries)

[eslint] reported by reviewdog 🐢 Imports "APIError", "Filter", "MarketplaceCategory", "MarketplacePartner", "MarketplacePartnerReferralPayload", "MarketplaceProduct", "Params" and "ResourcePage" are only used as type. Raw Output: {"ruleId":"@typescript-eslint/consistent-type-imports","severity":1,"message":"Imports \"APIError\", \"Filter\", \"MarketplaceCategory\", \"MarketplacePartner\", \"MarketplacePartnerReferralPayload\", \"MarketplaceProduct\", \"Params\" and \"ResourcePage\" are only used as type.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"someImportsAreOnlyTypes","endLine":11,"endColumn":25,"fix":{"range":[0,185],"text":"import type {
APIError,
createPartnerReferral,
Filter,
MarketplaceCategory,
MarketplacePartner,
MarketplacePartnerReferralPayload,
MarketplaceProduct,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's separate types from value imports and use import type { way instead for importing the types

Params,
ResourcePage,
} from '@linode/api-v4';
import {
keepPreviousData,
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query';

import { accountQueries } from '../account';
import { marketplaceQueries } from './keys';

export const useMarketplaceProductsQuery = (
params: Params,
filter: Filter,
enabled: boolean = false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we defaulting enabled to false? it means queries won't run unless explicitly enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I'd prefer to keep it this way until we have the feature flag. I'm open to changing the value if needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd lean towards keeping the default as true and controlling this query by passing enabled from the consumer. That way, we only disable it when the feature flag is off, which is consistent with how we handle this in other places and is technically safer

) => {
useQuery<ResourcePage<MarketplaceProduct>, APIError[]>({
...marketplaceQueries.products._ctx.paginated(params, filter),
enabled,
placeholderData: keepPreviousData,
});
};

export const useAllMarketplaceProductsQuery = (
params: Params = {},
filter: Filter = {},
enabled: boolean = false,
) =>
useQuery<MarketplaceProduct[], APIError[]>({
...marketplaceQueries.products._ctx.all(params, filter),
enabled,
});

export const useInfiniteMarketplaceProductsQuery = (
filter: Filter,
enabled: boolean,
) => {
return useInfiniteQuery<ResourcePage<MarketplaceProduct>, APIError[]>({
...marketplaceQueries.products._ctx.infinite(filter),
enabled,
getNextPageParam: ({ page, pages }) => {
if (page === pages) {
return undefined;
}
return page + 1;
},
initialPageParam: 1,
retry: false,
});
};

export const useMarketplaceProductQuery = (
productId: number,
enabled: boolean = false,
) =>
useQuery<MarketplaceProduct, APIError[]>({
...marketplaceQueries.product(productId),
enabled,
});

export const useMarketplaceCategoriesQuery = (
params: Params,
filter: Filter,
enabled: boolean = false,
) => {
useQuery<ResourcePage<MarketplaceCategory>, APIError[]>({
...marketplaceQueries.categories._ctx.paginated(params, filter),
enabled,
placeholderData: keepPreviousData,
});
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no types defined in here like (like useMarketplaceTypesQuery, useAllMarketplaceTypesQuery, etc.). In keys we have queries defined for types api though. Hook should be added here for types.

export const useAllMarketplaceCategoriesQuery = (
params: Params = {},
filter: Filter = {},
enabled: boolean = false,
) =>
useQuery<MarketplaceCategory[], APIError[]>({
...marketplaceQueries.categories._ctx.all(params, filter),
enabled,
});

export const useInfiniteMarketplaceCategoriesQuery = (
filter: Filter,
enabled: boolean,
) => {
return useInfiniteQuery<ResourcePage<MarketplaceCategory>, APIError[]>({
...marketplaceQueries.categories._ctx.infinite(filter),
enabled,
getNextPageParam: ({ page, pages }) => {
if (page === pages) {
return undefined;
}
return page + 1;
},
initialPageParam: 1,
retry: false,
});
};

export const useMarketplacePartnersQuery = (
params: Params,
filter: Filter,
enabled: boolean = false,
) => {
useQuery<ResourcePage<MarketplacePartner>, APIError[]>({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to have a return statement before useQuery

...marketplaceQueries.partners._ctx.paginated(params, filter),
enabled,
placeholderData: keepPreviousData,
});
};

export const useAllMarketplacePartnersQuery = (
params: Params = {},
filter: Filter = {},
enabled: boolean = false,
) =>
useQuery<MarketplacePartner[], APIError[]>({
...marketplaceQueries.partners._ctx.all(params, filter),
enabled,
});

export const useInfiniteMarketplacePartnersQuery = (
filter: Filter,
enabled: boolean,
) => {
return useInfiniteQuery<ResourcePage<MarketplacePartner>, APIError[]>({
...marketplaceQueries.partners._ctx.infinite(filter),
enabled,
getNextPageParam: ({ page, pages }) => {
if (page === pages) {
return undefined;
}
return page + 1;
},
initialPageParam: 1,
retry: false,
});
};

export const useCreatePartnerReferralMutation = () => {
const queryClient = useQueryClient();
return useMutation<{}, APIError[], MarketplacePartnerReferralPayload>({

Check warning on line 154 in packages/queries/src/marketplace/marketplace.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (queries)

[eslint] reported by reviewdog 🐢 The `{}` ("empty object") type allows any non-nullish value, including literals like `0` and `""`. - If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option. - If you want a type meaning "any object", you probably want `object` instead. - If you want a type meaning "any value", you probably want `unknown` instead. Raw Output: {"ruleId":"@typescript-eslint/no-empty-object-type","severity":1,"message":"The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.
mutationFn: createPartnerReferral,
onSuccess: () => {
setTimeout(() => {
// Refetch notifications after 1.5 seconds. The API needs some time to process.
queryClient.invalidateQueries({
queryKey: accountQueries.notifications.queryKey,
});
}, 1500);
},
});
};
60 changes: 60 additions & 0 deletions packages/queries/src/marketplace/requests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
getMarketplaceCategories,
getMarketplacePartners,
getMarketplaceProducts,
getMarketplaceTypes,
} from '@linode/api-v4';
import { getAll } from '@linode/utilities';

import type {
Filter,
MarketplaceCategory,
MarketplacePartner,
MarketplaceProduct,
MarketplaceType,
Params,
} from '@linode/api-v4';

export const getAllMarketplaceProducts = (
passedParams: Params = {},
passedFilter: Filter = {},
) =>
getAll<MarketplaceProduct>((params, filter) =>
getMarketplaceProducts(
{ ...params, ...passedParams },
{ ...filter, ...passedFilter },
),
)().then((data) => data.data);

export const getAllMarketplaceCategories = (
passedParams: Params = {},
passedFilter: Filter = {},
) =>
getAll<MarketplaceCategory>((params, filter) =>
getMarketplaceCategories(
{ ...params, ...passedParams },
{ ...filter, ...passedFilter },
),
)().then((data) => data.data);

export const getAllMarketplaceTypes = (
passedParams: Params = {},
passedFilter: Filter = {},
) =>
getAll<MarketplaceType>((params, filter) =>
getMarketplaceTypes(
{ ...params, ...passedParams },
{ ...filter, ...passedFilter },
),
)().then((data) => data.data);

export const getAllMarketplacePartners = (
passedParams: Params = {},
passedFilter: Filter = {},
) =>
getAll<MarketplacePartner>((params, filter) =>
getMarketplacePartners(
{ ...params, ...passedParams },
{ ...filter, ...passedFilter },
),
)().then((data) => data.data);