Skip to content

Commit c919e79

Browse files
committed
Improve docs
1 parent 15123f3 commit c919e79

18 files changed

+384
-56
lines changed

src/ArgMapping.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface ArgMapping<TMapped extends t.Type<any>> extends t.TypeOf<typeof
7272
*
7373
* This opens up the ability to map an argument value to an arbitrary database query operation.
7474
*/
75-
interceptQuery?: (qb: Knex.QueryBuilder, value: t.TypeOf<TMapped>, args: Dict) => Knex.QueryBuilder;
75+
interceptQuery?: (queryBuilder: Knex.QueryBuilder, value: t.TypeOf<TMapped>, args: Dict) => Knex.QueryBuilder;
7676
/**
7777
* Can be used to intercept the derived entity to be used for the operation this argument is part of.
7878
*

src/AssociationMapping.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ export const AssociationMappingRT = t.intersection([
206206
]);
207207

208208
/**
209+
* Configuration used to map an association.
210+
*
211+
* Association mapping determines how two data sources can be linked (through joins, or auxiliary queries) so
212+
* that operations can be performed over multiple data sources.
213+
*
209214
* @api-category ConfigType
210215
*/
211216
export interface AssociationMapping<TSrc extends MappedDataSource = any, TTgt extends MappedDataSource = any>

src/DataSourceMapping.ts

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,42 @@ import { MappedAssociation } from "./MappedAssociation";
99
export const DataSourceMappingRT = t.intersection([
1010
t.type({
1111
/**
12-
* Name of data source
12+
* Name of the data source.
1313
*
14-
* This can either be a string or an object with stored and mapped properties
14+
* This can either be a string or an object with stored and mapped properties.
15+
*
16+
* If provided as a string, then the mapped name of the data source
17+
* will be PascalCased singular variant of the name, and the stored name
18+
* will be the snake_cased pluralized variant.
19+
*
20+
* Eg. if name is specified either as "ProductManager" or "product_managers",
21+
* in both cases they will be normalized as:
22+
*
23+
* ```
24+
* {
25+
* stored: 'product_managers',
26+
* mapped: 'ProductManager'
27+
* }
28+
* ```
29+
*
30+
* Unless rootQuery is specified, the stored name will be used to identify the table to connect to.
31+
*
32+
* If exposed, the GraphQL type names are derived from the mapped name:
33+
*
34+
* - `ProductManager` - primary GraphQL output type for the entity
35+
* - `ShallowProductManager` - GraphQL output type containing only the fields and not associations
36+
* - `ProductManagerInput` - GraphQL input type for the entity
37+
* - `ShallowProductManagerInput` - Graphql input type for shallow entity (excludes associations)
1538
*
1639
* @property
1740
* @memberof DataSourceMapping
1841
*/
1942
name: MaybeMapped(t.string, t.string),
2043
}),
2144
t.partial({
45+
/**
46+
* Human friendly description of an entity from this data source.
47+
*/
2248
description: t.string,
2349
fields: t.Function,
2450
associations: t.Function,
@@ -28,20 +54,47 @@ export const DataSourceMappingRT = t.intersection([
2854
]);
2955

3056
/**
31-
* Configuration for mapping a data source.
57+
* Make sure you have gone through the [DataSource Mapping](guide:mapping-data-sources) guide first, which provides a high level overview of how data mapping works in practice
58+
* in GRelDAL.
59+
*
60+
* A DataSource mapping facilitates the mapping between the persistence layer and the application level entities.
3261
*
33-
* Make sure you have seen the Data Mapping Guide
62+
* It has following major responsibilities:
63+
*
64+
* 1. To specify how the data source connects to the persistence layer.
65+
* 2. To describe the shape of a `row` stored in the persistence layer
66+
* 3. To describe the shape of an `entity` exposed to the application layer.
67+
* 4. To define the mapping between the two representations so an entity can be coverted to a row and vice versa.
68+
* 5. To define how this data source can connect to other data sources through joins or side-loading
3469
*
3570
* @api-category ConfigType
3671
*/
37-
export type DataSourceMapping = t.TypeOf<typeof DataSourceMappingRT> & {
72+
export interface DataSourceMapping extends t.TypeOf<typeof DataSourceMappingRT> {
3873
/**
39-
* Mapping of fieldNames to Configuration for fields
40-
*
41-
* Refer [FieldMapping](api:FieldMapping) for specifics
74+
* Field mapping obtained from [mapFields](api:mapFields)
4275
*/
4376
fields?: (dataSource: MappedDataSource) => Dict<MappedField>;
77+
78+
/**
79+
* Association mapping obtained from [mapAssociations](api:mapAssociations)
80+
*/
4481
associations?: (dataSource: MappedDataSource) => Dict<MappedAssociation>;
82+
83+
/**
84+
* By default dataSources will connect to a table matching storedName.
85+
*
86+
* Using a rootQuery we can configure the data source to connect to a different table, or a join of multiple tables.
87+
* Note that when working with joins, it is generally recommended to use associations over mapping to joined tables.
88+
*/
4589
rootQuery?: (alias: Maybe<AliasHierarchyVisitor>) => Knex.QueryBuilder;
90+
91+
/**
92+
* Dedicated Knex instance to be used for this data source.
93+
*
94+
* If not provided, the knex instance globally configured through [useDatabaseConnector](api:useDatabaseConnector) will be used.
95+
*
96+
* DataSource specific connector is primarily useful for supporting polyglot persistence and enables use of different
97+
* databases within the application.
98+
*/
4699
connector?: Knex;
47-
};
100+
}

src/MappedAssociation.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ export class MappedAssociation<TSrc extends MappedDataSource = any, TTgt extends
4141
);
4242
}
4343

44+
/**
45+
* If the association will resolve to at most one associated entity
46+
*/
4447
@MemoizeGetter
4548
get singular() {
4649
if (isBoolean(this.mapping.singular)) {
@@ -49,22 +52,38 @@ export class MappedAssociation<TSrc extends MappedDataSource = any, TTgt extends
4952
return singularize(this.mappedName) === this.mappedName;
5053
}
5154

55+
/**
56+
* If the association will be exposed through GraphQL API
57+
*/
5258
get exposed() {
5359
return this.mapping.exposed !== false;
5460
}
5561

62+
/**
63+
* Linked data source
64+
*/
5665
get target(): TTgt {
5766
return this.mapping.target.apply(this);
5867
}
5968

69+
/**
70+
* Association description made available through the GraphQL API
71+
*/
6072
get description() {
6173
return this.mapping.description;
6274
}
6375

76+
/**
77+
* If the association supports paginated response
78+
*/
6479
get isPaginated() {
6580
return !!this.mapping.paginate;
6681
}
6782

83+
/**
84+
* For a given operation, identify one of the (potentially) many many fetch configurations specified
85+
* using the fetchThrough mapping property.
86+
*/
6887
getFetchConfig<
6988
TCtx extends ResolverContext<TMappedOperation, TRootSrc, TGQLArgs, TGQLSource, TGQLContext>,
7089
TRootSrc extends MappedDataSource<any>,
@@ -186,27 +205,68 @@ export class MappedAssociation<TSrc extends MappedDataSource = any, TTgt extends
186205
};
187206
}
188207

208+
/**
209+
* Columns used to link the data sources at the persistence layer
210+
*/
189211
get associatorColumns() {
190212
return this.mapping.associatorColumns;
191213
}
192214

215+
/**
216+
* Getter to obtain the type of source DataSource.
217+
*
218+
* This is expected to be used only in mapped typescript types. Invoking the getter directly
219+
* at runtime will throw.
220+
*/
193221
get DataSourceType(): TSrc {
194222
throw getTypeAccessorError("DataSourceType", "MappedAssociation");
195223
}
196224

225+
/**
226+
* Getter to obtain the type of associated DataSource.
227+
*
228+
* This is expected to be used only in mapped typescript types. Invoking the getter directly
229+
* at runtime will throw.
230+
*/
197231
get AssociatedDataSourceType(): TTgt {
198232
throw getTypeAccessorError("AssociatedDataSourceType", "MappedAssociation");
199233
}
200234

235+
/**
236+
* Getter to obtain the type of entity from source DataSource.
237+
*
238+
* This is expected to be used only in mapped typescript types. Invoking the getter directly
239+
* at runtime will throw.
240+
*/
201241
get SourceEntityType(): TSrc["EntityType"] {
202242
throw getTypeAccessorError("SourceEntityType", "MappedAssociation");
203243
}
204244

245+
/**
246+
* Getter to obtain the type of entity from associated DataSource.
247+
*
248+
* This is expected to be used only in mapped typescript types. Invoking the getter directly
249+
* at runtime will throw.
250+
*/
205251
get AssociatedEntityType(): TTgt["EntityType"] {
206252
throw getTypeAccessorError("AssociatedEntityType", "MappedAssociation");
207253
}
208254
}
209255

256+
/**
257+
* Used to define an association between two data sources.
258+
*
259+
* Make sure you have gone through the [Association Mapping](guide:mapping-associations) guide first which elaborates on
260+
* the concepts behind association mapping.
261+
*
262+
* Association mapping determines how two data sources can be linked (through joins, or auxiliary queries) so
263+
* that operations can be performed over multiple data sources.
264+
*
265+
* Accepts an [AssociationMapping](api:AssociationMapping) configuration.
266+
*
267+
* @api-category PrimaryAPI
268+
* @param associations
269+
*/
210270
export const mapAssociations = <TMapping extends Dict<AssociationMapping<any, MappedDataSource>>>(
211271
associations: TMapping,
212272
) => <TSrc extends MappedDataSource>(

src/MappedDataSource.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ type FieldKeysIn<T extends DataSourceMapping> = keyof FieldsIn<T>;
3434

3535
type AssociatedDataSource<T extends DataSourceMapping, K extends AssociationKeysIn<T>> = AssociationsIn<T>[K]["target"];
3636

37-
type ShallowEntityType<T extends DataSourceMapping> = {
38-
[K in FieldKeysIn<T>]: t.TypeOf<FieldsIn<T>[K]["type"]>
39-
};
37+
type ShallowEntityType<T extends DataSourceMapping> = { [K in FieldKeysIn<T>]: t.TypeOf<FieldsIn<T>[K]["type"]> };
4038

4139
type NestedEntityType<T extends DataSourceMapping> = ShallowEntityType<T> &
4240
{ [K in AssociationKeysIn<T>]: AssociatedDataSource<T, K>["EntityType"] };

src/MappedField.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ export class MappedField<
257257
}
258258
}
259259

260+
/**
261+
* @api-category PrimaryAPI
262+
*/
260263
export const mapFields = <TFieldMapping extends Dict<FieldMapping<t.Type<any>, TArgs>>, TArgs extends {}>(
261264
fields: TFieldMapping,
262265
) => <TSrc extends MappedDataSource>(

src/connector.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export const assertConnectorConfigured = (connector?: Maybe<Knex>) => {
5151
* Register a database connector as the global default for the library.
5252
*
5353
* Connector options can be found in [knex documentation](https://knexjs.org/#Installation-client).
54+
*
55+
* @api-category PrimaryAPI
5456
*/
5557
export const useDatabaseConnector = (connector: Knex) => {
5658
globalConnector = assertSupportedConnector(connector);

src/docs/components/APIBody.js

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import APIEntityContainer from "./APIEntityContainer";
1+
import APIEntityContainer, {APIContainer} from "./APIEntityContainer";
22
import styled from "styled-components";
3+
import { Link } from "./Link";
34

45
export const EmptyMsgContainer = styled.div`
56
color: silver;
@@ -11,9 +12,101 @@ export const EmptyMsgContainer = styled.div`
1112
line-height: 4rem;
1213
`;
1314

15+
export function APIIntro(props) {
16+
return (
17+
<APIContainer>
18+
<h1>Welcome to GRelDAL's API Documentation </h1>
19+
<div>
20+
Please make sure that you have first gone through the <Link href="guides">Guides</Link> before diving
21+
deep into the API docs.
22+
</div>
23+
<div>
24+
<h1>Terms</h1>
25+
<p>Summary of the terminology used in the API docs</p>
26+
<ul>
27+
<li>
28+
<strong>DataSource</strong>
29+
<p>
30+
A DataSource is a mapper that mediates operations against a table/view in the persistence
31+
layer.
32+
</p>
33+
<p>
34+
DataSources are expected to have a fixed set of <strong>fields</strong> (see below) and can
35+
interact with other data sources through <strong>associations</strong> (see below).
36+
</p>
37+
</li>
38+
<li>
39+
<strong>Entity</strong>
40+
<p>
41+
Entities are plain JavaScript objects returned by operations on data sources.
42+
</p>
43+
<p>
44+
When application logic is written against Entities, it is sheilded from the specifics
45+
of persistence layer. Using plain js entities instead of ORM models or managed instances
46+
prevents accidental coupling of application logic and persistence specific concerns.
47+
</p>
48+
<p>
49+
The shape of an entity may not be same as rows in the underlying persistence layer (table or
50+
view) and whenever required, the DataSource will perform coversion between rows and
51+
entities.
52+
</p>
53+
</li>
54+
<li>
55+
<strong>Row</strong>
56+
<p>
57+
In the documentation here, rows always refer to rows in a table/view in the persistence
58+
layer.
59+
</p>
60+
</li>
61+
<li>
62+
<strong>Association</strong>
63+
<p>An association is a link using which operations can span over multiple data source</p>
64+
</li>
65+
<li>
66+
<strong>Field</strong>
67+
<p>
68+
A field refers to an attribute in an Entity. A field can either be directly mapped from a
69+
column or be derived from a combination of multiple columns.
70+
</p>
71+
<p>
72+
The <Link href="mapping-data-sources">DataSource mapping guide</Link> elaborates more on
73+
mapping fields.
74+
</p>
75+
</li>
76+
</ul>
77+
</div>
78+
<div>
79+
<h1>Important top level functions</h1>
80+
<ul>
81+
<li>
82+
<strong>useDatabaseConnector</strong>
83+
<p>
84+
Configures a knex instance to be used for connecting to the database. For polyglot
85+
persistence, we can also specify a knex instance at a data source level.
86+
</p>
87+
</li>
88+
<li>
89+
<strong>mapDataSource</strong>
90+
<p>
91+
Map tables (or views) in database to data sources which operations exposed through the
92+
GraphQL schema can interact with.
93+
</p>
94+
</li>
95+
<li>
96+
<strong>mapSchema</strong>
97+
<p>Derive a GraphQL schema from GRelDAL data sources and operations.</p>
98+
</li>
99+
</ul>
100+
</div>
101+
<h2>Looking for a particular class/function ?</h2>
102+
<p>← Find it through the sidebar</p>
103+
</APIContainer>
104+
);
105+
}
106+
14107
export default function APIBody(props) {
15108
if (!props.activeCategory || !props.rootEntity) {
16-
return <EmptyMsgContainer>Select an entity from sidebar</EmptyMsgContainer>;
109+
return <APIIntro />;
17110
}
18111
return <APIEntityContainer entity={props.rootEntity} activeEntityName={props.activeEntityName} />;
19112
}

0 commit comments

Comments
 (0)