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

Add typings for stripes-connect sources #50

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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,16 @@
import Logger from '../../../../util/logger';
import { ApolloConnectedSourceProps, ApolloError, ConnectedSource } from './ConnectedSource';

export default class ApolloConnectedSource implements ConnectedSource {
Copy link
Member Author

Choose a reason for hiding this comment

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

Even though the real class doesn't extend anything, we can use the TS-specific implements to say the class meets the criteria of the ConnectedSource interface. Therefore, when one is passed around, consuming code can just specify a ConnectedSource and not have to do something like ApolloConnectedSource | StripesConnectedSource

constructor(props: ApolloConnectedSourceProps, logger: Logger, resourceName?: string);
records(): unknown[];
resultCount(): number;
totalCount(): number | null | undefined;
pending(): boolean;
loaded(): boolean;
failure(): ApolloError | null | undefined;

failureMessage(): string;
fetchMore(increment: number): void;
successfulMutations(): unknown[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import Logger from '../../../../util/logger';
import { QueryFunction } from '../makeQueryFunction';

type ApolloError = {
message: string;
graphQLErrors: unknown[];
networkError: Error | null;
extraInfo: any;
};

export interface ApolloConnectedSourceProps {
apolloResource?: string;
apolloRecordsKey: string;
queryFunction: QueryFunction;
parentData: {
// properties directly on parentData
loading?: boolean;
successfulMutations?: unknown[];
error?: ApolloError;
fetchMore(params: {
variables: {
cql: string;
offset: number;
limit: number;
};
// record key is `apolloResource`
updateQuery: (
prev: Record<string, { records: unknown[] }>,
params: { fetchMoreResult: unknown },
) => Record<string, { records: unknown[] }>;
}): void;
} & {
// all within resource name (within recordsObj)
[resourceName: string]: {
Copy link
Member Author

Choose a reason for hiding this comment

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

We don't know what the resourceName is here and it's not trivial to provide safety for it (it would require a lot of nasty generics)

totalRecords?: number;
} & {
// equals apolloRecordsKey
[recordsKey: string]: unknown[];
};
};
parentResources: {
query: Record<string, unknown>;
};
}

type StripesError = {
dataKey?: string;
httpStatus?: unknown;
message?: string;
module?: unknown;
resource?: unknown;
throwErrors?: unknown;
};

type StripesResourceType = {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is where my greatest uncertainty is. I see by default it uses the resourceName of records (and some other places in stripes-smart-components seem to assume that?), but I'm not certain of this structure.

resultCount: number;
notes?: string | boolean;
filters?: string;
} & {
// called recordsObj in code
[resourceName: string]: {
records?: unknown[];
hasLoaded?: boolean;
isPending?: boolean;
failed?: StripesError;
other?: unknown;
successfulMutations?: unknown[];
};
};
type StripesMutatorType = {
resultCount: {
replace: (count: number) => void;
};
resultOffset: {
replace: (offset: number) => void;
};
query: {
replace: (query: unknown) => void;
update: (query: unknown) => void;
};
};

export interface StripesConnectedSourceProps {
// key is from resourceName
parentResources: StripesResourceType;
resources?: StripesResourceType;
parentMutator?: StripesMutatorType;
mutator?: StripesMutatorType;
}

export interface ConnectedSource {
records(): unknown[];

/** Number of records retrieved so far */
resultCount(): number;
/**
* Number of records in the result-set, available to be retrieved.
*
* For {@code StripesConnectedSource}, when there are > 10k results to a search, the `totalRecords`
* value comes back as `999999999`, so we use that value to indicate
* that the count is, in fact, undefined vs returning null to indicate
* that the count has not been calculated.
*/
totalCount(): number | null | undefined;

/** True only during a request, false before and after */
pending(): boolean;

/** True only after a request, false before and during */
loaded(): boolean;

failure(): ApolloError | StripesError | undefined | null;

failureMessage(): string;

fetchMore(increment: number): void;

successfulMutations(): unknown[];
}

export type ConnectedSourceProps = ApolloConnectedSourceProps | StripesConnectedSourceProps;

export default function makeConnectedSource(
props: ConnectedSourceProps,
logger: Logger,
resourceName?: string,
): ConnectedSource | null;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { StripesConnectedSourceProps, ConnectedSource, StripesError } from './ConnectedSource';
import Logger from '../../../../util/logger';

export default class StripesConnectedSource implements ConnectedSource {
constructor(props: StripesConnectedSourceProps, logger: Logger, resourceName?: string);
update(props: StripesConnectedSourceProps, resourceName?: string): void;
records(): unknown[];
resultCount(): number;
totalCount(): number | null | undefined;
pending(): boolean;
loaded(): boolean;
failure(): StripesError | null | undefined;
failureMessage(): string;
fetchMore(increment: number): void;
fetchByBrowsePoint(browsePoint: unknown): void;
fetchOffset(index: number): void;
successfulMutations(): unknown[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export {
default,
ConnectedSource,
ConnectedSourceProps,
ApolloConnectedSourceProps,
StripesConnectedSourceProps,
} from './ConnectedSource';
52 changes: 52 additions & 0 deletions smart-components/lib/SearchAndSort/makeQueryFunction.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { FilterGroupsConfig } from '../../../components';
import Logger from '../../../util/logger';
import { NsParamsType } from './nsQueryFunctions';

/**
* Returns a string, or null, which stripes-connect will use to construct a resource query.
*
* Accepts four params:
* @param queryParams An object containing the UI URL's query parameters (as accessed by ?{name}).
* @param pathComponents An object containing the UI URL's path components (as accessed by :{name}).
* @param resourceData An object containing the component's resources' data (as accessed by %{name}).
* @param logger A logger object.
*/
export type QueryFunction = (
queryParams: Record<string, unknown>,
pathComponents: Record<string, unknown>,
resourceData: { query: Record<string, unknown> },
logger: Logger,
) => string | null;

/**
* Builds a {@link QueryFunction}
*
* @param findAll CQL query to retrieve all records when there is a sort clause but no CQL query
* @param queryTemplate CQL query to interpolate, or function which will return CQL
* @param sortMap map from sort keys to CQL fields
* @param filterConfig list of filter objects, see {@link FilterGroupsConfig}
* @param failOnCondition one of the following:
* - 0 (or false (legacy)): do not fail even if query and filters and empty
* - 1 (or true (legacy)): fail if query is empty, whatever the filter state
* - 2: fail if both query and filters and empty
* @param nsParams namespace keys
* @param configOrEscape an object containing configuration parameters:
* - escape: whether to escape the query string (default true)
* - rightTrunc: whether to right-truncate the query string (default true)
* For backwards compatibility, this parameter may also be a boolean, in which case it is used as the `escape` configuration value.
*/
export default function makeQueryFunction(
findAll: string,
queryTemplate:
| string
| ((
nsQueryParams: Record<string, unknown>,
pathComponents: Record<string, unknown>,
queryObj: { query: Record<string, unknown> },
) => string),
sortMap: Record<string, string>,
filterConfig: FilterGroupsConfig,
failOnCondition: 0 | 1 | 2 | true | false,
nsParams: NsParamsType,
configOrEscape: boolean | { escape?: boolean; rightTrunc?: boolean },
): QueryFunction;
32 changes: 32 additions & 0 deletions smart-components/lib/SearchAndSort/nsQueryFunctions.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export type NsParamsType = string | Record<string, unknown> | undefined | null;

export function getNsKey(key: string, params?: NsParamsType): string;

/**
*
* Adds namespace / prefix to keys in whitelist for given values object
*
* @example
* ```
* values = mapNsKeys({ query: "test", filters: 'active', userId: 1 }, 'users')
* // result: { "users.query" : "test", "users.filters": "active", userId: 1 }
* ```
*/
export function mapNsKeys(
values: Record<string, unknown>,
params?: NsParamsType,
): Record<string, unknown>;

/**
* Removes namespace / prefix from keys for given values object
*
* @example
* ```
* values = removeNsKeys({ "users.query" : "test", "users.filters": "active" }, 'users')
* // result: { query: "test", filters: 'active' }
* ```
*/
export function removeNsKeys(
values: Record<string, unknown>,
params?: NsParamsType,
): Record<string, unknown>;
3 changes: 3 additions & 0 deletions util/logger.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// TODO: actual typings for stripes logger
declare type Logger = (...args: any) => void;
export default Logger;
Loading