Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Collection Items extension - facets - filter queries - sort/type #230

Merged
12 changes: 12 additions & 0 deletions src/collections/domain/models/CollectionItemSubset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,17 @@ import { CollectionPreview } from './CollectionPreview'

export interface CollectionItemSubset {
items: (CollectionPreview | DatasetPreview | FilePreview)[]
facets: CollectionItemsFacet[]
totalItemCount: number
}

export interface CollectionItemsFacet {
name: string
friendlyName: string
labels: CollectionItemsFacetLabel[]
}

interface CollectionItemsFacetLabel {
name: string
count: number
}
28 changes: 25 additions & 3 deletions src/collections/domain/models/CollectionSearchCriteria.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
import { CollectionItemType } from './CollectionItemType'
import { FilterQuery, OrderType, SortType } from './GetCollectionItemsQueryParams'

export class CollectionSearchCriteria {
constructor(
public readonly searchText?: string,
public readonly itemTypes?: CollectionItemType[]
public readonly itemTypes?: CollectionItemType[],
public readonly sort?: SortType,
public readonly order?: OrderType,
public readonly filterQueries?: FilterQuery[]
) {}

withSearchText(searchText: string | undefined): CollectionSearchCriteria {
return new CollectionSearchCriteria(searchText, this.itemTypes)
return new CollectionSearchCriteria(searchText, this.itemTypes, this.sort, this.order)
}

withItemTypes(itemTypes: CollectionItemType[] | undefined): CollectionSearchCriteria {
return new CollectionSearchCriteria(this.searchText, itemTypes)
return new CollectionSearchCriteria(this.searchText, itemTypes, this.sort, this.order)
g-saracca marked this conversation as resolved.
Show resolved Hide resolved
}

withSort(sort: SortType | undefined): CollectionSearchCriteria {
return new CollectionSearchCriteria(this.searchText, this.itemTypes, sort, this.order)
g-saracca marked this conversation as resolved.
Show resolved Hide resolved
}

withOrder(order: OrderType | undefined): CollectionSearchCriteria {
return new CollectionSearchCriteria(this.searchText, this.itemTypes, this.sort, order)
g-saracca marked this conversation as resolved.
Show resolved Hide resolved
}

withFilterQueries(filterQueries: FilterQuery[] | undefined): CollectionSearchCriteria {
return new CollectionSearchCriteria(
this.searchText,
this.itemTypes,
this.sort,
this.order,
filterQueries
)
}
}
23 changes: 23 additions & 0 deletions src/collections/domain/models/GetCollectionItemsQueryParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export enum GetCollectionItemsQueryParams {
g-saracca marked this conversation as resolved.
Show resolved Hide resolved
QUERY = 'q',
SHOW_FACETS = 'show_facets',
SORT = 'sort',
ORDER = 'order',
SUBTREE = 'subtree',
PER_PAGE = 'per_page',
START = 'start',
TYPE = 'type',
FILTERQUERY = 'fq'
}

export enum SortType {
NAME = 'name',
DATE = 'date'
}

export enum OrderType {
ASC = 'asc',
DESC = 'desc'
}

export type FilterQuery = `${string}:${string}`
83 changes: 52 additions & 31 deletions src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import { transformCollectionUserPermissionsResponseToCollectionUserPermissions }
import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset'
import { CollectionSearchCriteria } from '../../domain/models/CollectionSearchCriteria'
import { CollectionItemType } from '../../domain/models/CollectionItemType'
import {
GetCollectionItemsQueryParams,
OrderType,
SortType
} from '../../domain/models/GetCollectionItemsQueryParams'

export interface NewCollectionRequestPayload {
alias: string
Expand Down Expand Up @@ -40,14 +45,6 @@ export interface NewCollectionInputLevelRequestPayload {
required: boolean
}

export interface GetCollectionItemsQueryParams {
q: string
subtree?: string
per_page?: number
start?: number
type?: string
}

export class CollectionsRepository extends ApiRepository implements ICollectionsRepository {
private readonly collectionsResourceName: string = 'dataverses'

Expand Down Expand Up @@ -119,37 +116,30 @@ export class CollectionsRepository extends ApiRepository implements ICollections
offset?: number,
collectionSearchCriteria?: CollectionSearchCriteria
): Promise<CollectionItemSubset> {
const queryParams: GetCollectionItemsQueryParams = {
q: '*'
}
const queryParams = new URLSearchParams({
[GetCollectionItemsQueryParams.QUERY]: '*',
[GetCollectionItemsQueryParams.SHOW_FACETS]: 'true',
[GetCollectionItemsQueryParams.SORT]: SortType.DATE,
[GetCollectionItemsQueryParams.ORDER]: OrderType.DESC
})

if (collectionId) {
queryParams.subtree = collectionId
queryParams.set(GetCollectionItemsQueryParams.SUBTREE, collectionId)
}

if (limit) {
queryParams.per_page = limit
queryParams.set(GetCollectionItemsQueryParams.PER_PAGE, limit.toString())
}

if (offset) {
queryParams.start = offset
queryParams.set(GetCollectionItemsQueryParams.START, offset.toString())
}

if (collectionSearchCriteria) {
this.applyCollectionSearchCriteriaToQueryParams(queryParams, collectionSearchCriteria)
}

let url = '/search?sort=date&order=desc'

if (collectionSearchCriteria?.itemTypes) {
const itemTypesQueryString = collectionSearchCriteria.itemTypes
.map((itemType: CollectionItemType) => {
const mappedItemType =
itemType === CollectionItemType.COLLECTION ? 'dataverse' : itemType.toString()
return `type=${mappedItemType}`
})
.join('&')

url += `&${itemTypesQueryString}`
}

return this.doGet(url, true, queryParams)
return this.doGet('/search', true, queryParams)
.then((response) => transformCollectionItemsResponseToCollectionItemSubset(response))
.catch((error) => {
throw error
Expand Down Expand Up @@ -201,11 +191,42 @@ export class CollectionsRepository extends ApiRepository implements ICollections
}

private applyCollectionSearchCriteriaToQueryParams(
queryParams: GetCollectionItemsQueryParams,
queryParams: URLSearchParams,
collectionSearchCriteria: CollectionSearchCriteria
) {
if (collectionSearchCriteria.searchText) {
queryParams.q = encodeURIComponent(collectionSearchCriteria.searchText)
queryParams.set(
GetCollectionItemsQueryParams.QUERY,
encodeURIComponent(collectionSearchCriteria.searchText)
)
}

if (collectionSearchCriteria?.itemTypes) {
collectionSearchCriteria.itemTypes.forEach((itemType) => {
const mappedItemType = itemType === CollectionItemType.COLLECTION ? 'dataverse' : itemType

queryParams.append(GetCollectionItemsQueryParams.TYPE, mappedItemType)
})
}

if (collectionSearchCriteria?.sort) {
queryParams.set(GetCollectionItemsQueryParams.SORT, collectionSearchCriteria.sort)
}

if (collectionSearchCriteria?.order) {
queryParams.set(GetCollectionItemsQueryParams.ORDER, collectionSearchCriteria.order)
}

if (collectionSearchCriteria?.filterQueries) {
collectionSearchCriteria.filterQueries.forEach((filterQuery) => {
const [filterQueryKey, filterQueryValue] = filterQuery.split(':')

const filterQueryValueWithQuotes = `"${filterQueryValue}"`

const filterQueryToSet = `${filterQueryKey}:${filterQueryValueWithQuotes}`

queryParams.append(GetCollectionItemsQueryParams.FILTERQUERY, filterQueryToSet)
})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type CollectionItemsFacetPayload = [Record<string, CollectionItemsFacetPayloadValue>]

export interface CollectionItemsFacetPayloadValue {
friendly: string
labels: Record<string, number>[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories
import { transformHtmlToMarkdown } from '../../../../datasets/infra/repositories/transformers/datasetTransformers'
import { CollectionFacet } from '../../../domain/models/CollectionFacet'
import { CollectionFacetPayload } from './CollectionFacetPayload'
import { CollectionItemSubset } from '../../../domain/models/CollectionItemSubset'
import {
CollectionItemsFacet,
CollectionItemSubset
} from '../../../domain/models/CollectionItemSubset'
import { DatasetPreview } from '../../../../datasets'
import { FilePreview } from '../../../../files'
import { DatasetPreviewPayload } from '../../../../datasets/infra/repositories/transformers/DatasetPreviewPayload'
Expand All @@ -21,6 +24,7 @@ import { CollectionPreviewPayload } from './CollectionPreviewPayload'
import { CollectionPreview } from '../../../domain/models/CollectionPreview'
import { CollectionContact } from '../../../domain/models/CollectionContact'
import { CollectionType } from '../../../domain/models/CollectionType'
import { CollectionItemsFacetPayload } from './CollectionItemsFacetsPayload'

export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => {
const collectionPayload = response.data.data
Expand Down Expand Up @@ -79,7 +83,10 @@ export const transformCollectionItemsResponseToCollectionItemSubset = (
): CollectionItemSubset => {
const responseDataPayload = response.data.data
const itemsPayload = responseDataPayload.items
const facets = responseDataPayload.facets as CollectionItemsFacetPayload
g-saracca marked this conversation as resolved.
Show resolved Hide resolved

const items: (DatasetPreview | FilePreview | CollectionPreview)[] = []

itemsPayload.forEach(function (
itemPayload: CollectionPreviewPayload | DatasetPreviewPayload | FilePreviewPayload
) {
Expand All @@ -97,8 +104,21 @@ export const transformCollectionItemsResponseToCollectionItemSubset = (
)
}
})

const transformedFacets: CollectionItemsFacet[] = Object.entries(facets[0]).map(
([key, facetData]) => ({
name: key,
friendlyName: facetData.friendly,
labels: facetData.labels.map((label: Record<string, number>) => {
const [name, count] = Object.entries(label)[0]
return { name, count }
})
})
)

return {
items: items,
facets: transformedFacets,
totalItemCount: responseDataPayload.total_count
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/infra/repositories/ApiRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export abstract class ApiRepository {
public async doGet(
apiEndpoint: string,
authRequired = false,
queryParams: object = {}
queryParams: object | URLSearchParams = {}
): Promise<AxiosResponse> {
return await axios
.get(buildRequestUrl(apiEndpoint), buildRequestConfig(authRequired, queryParams))
Expand Down
2 changes: 1 addition & 1 deletion src/core/infra/repositories/apiConfigBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ApiConstants } from './ApiConstants'

export const buildRequestConfig = (
authRequired: boolean,
queryParams: object,
queryParams: object | URLSearchParams,
contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON,
abortSignal?: AbortSignal
): AxiosRequestConfig => {
Expand Down
Loading
Loading