Skip to content

Commit 03373dc

Browse files
committed
Merge branch 'main' into release/0.x
2 parents 50bf5b2 + 8e8d2b3 commit 03373dc

31 files changed

Lines changed: 1630 additions & 1087 deletions

.commitlintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = {
1818
'collection',
1919
'model',
2020
'model-collection',
21+
'ancestry-collection',
2122
'paginator',
2223
'factory',
2324
'query-builder',

.github/semantic.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ scopes:
2222
- 'collection'
2323
- 'model'
2424
- 'model-collection'
25+
- 'ancestry-collection'
2526
- 'paginator'
2627
- 'factory'
2728
- 'query-builder'

.github/workflows/deploy-api-docs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ jobs:
3838
git commit -m "Updates from ${{ github.ref }} - {{ github.sha }}" --no-verify
3939
git fetch
4040
git switch gh-pages
41-
git push gh-pages
41+
git merge origin/main
42+
git push

docs/.vuepress/links.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ const sidebar: SidebarConfig = [
2828
'/calliope/query-building',
2929
'/calliope/relationships',
3030
'/calliope/timestamps',
31-
'/calliope/model-collection'
31+
'/calliope/model-collection',
32+
'/calliope/ancestry-collection'
3233
]
3334
},
3435
{
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Ancestry Collection
2+
3+
Sometimes you may encounter data structures that are meant to be sorted based on their relation to each other. For example messages and their replies, folders and their sub folders, tasks and their subtasks, etc... However, sometimes the data sources might not return the data in an easily digestible structured format but as a flat array.
4+
5+
For these occasions you may use the AncestryCollection. The AncestryCollection is a subclass of the [ModelCollection](./model-collection.md), therefore all methods are inherited. Keep in mind that inherited methods will be operating on the top level items as one might expect.
6+
7+
## Properties
8+
9+
#### depthName
10+
<Badge text="static" type="warning"/>
11+
12+
This string determines the name of the attribute that is set on the models when constructing the collection using the [treeOf](#treeof) method.
13+
14+
## Methods
15+
16+
[[toc]]
17+
18+
#### treeOf
19+
<Badge text="static" type="warning"/>
20+
21+
The `treeOf` static method creates the AncestryCollection from the given [ModelCollection](./model-collection.md). This arranges the models to as the child of their respective models. Optionally the method takes 2 more arguments:
22+
- `parentKey` (default: `'parentId'`) - the name of the attribute that contains the parent's identifier.
23+
- `childrenRelation` (default: `'children'`): - the name of the relation the child models are nested under.
24+
25+
This will also assign the [depth](#depthname) value to the model based on its position on the tree.
26+
```js
27+
import { AncestryCollection } from '@upfrontjs/framework';
28+
import Folder from '~/Models/Folder';
29+
30+
const folders = await Folder.get();
31+
const folderTree = AncestryCollection.treeOf(folders);
32+
```
33+
34+
#### flatten
35+
36+
The `flatten` method deconstructs the tree to a flat [ModelCollection](./model-collection.md).
37+
38+
```js
39+
import { AncestryCollection } from '@upfrontjs/framework';
40+
import Folder from '~/Models/Folder';
41+
42+
const folders = await Folder.get();
43+
const folderTree = AncestryCollection.treeOf(folders);
44+
45+
folderTree.flatten(); // ModelCollection
46+
```
47+
48+
#### leaves
49+
50+
The `leaves` method returns a [ModelCollection](./model-collection.md) containing all the models that does not have any children. With the analogy of a tree, it will not include roots, branches, only the models at the end of the bloodline.
51+
52+
```js
53+
import { AncestryCollection } from '@upfrontjs/framework';
54+
import Folder from '~/Models/Folder';
55+
56+
const folders = await Folder.get();
57+
const folderTree = AncestryCollection.treeOf(folders);
58+
59+
folderTree.leaves(); // ModelCollection
60+
```
61+
62+
#### isAncestryCollection
63+
<Badge text="static" type="warning"/>
64+
65+
The `isAncestryCollection` static method same as the [isModelCollection](./model-collection.md#ismodelcollection) method on the ModelCollection, is used to evaluate that the given value is an AncestryCollection.
66+
```js
67+
import { AncestryCollection, ModelCollection, Collection } from '@upfrontjs/framework';
68+
import Folder from '~/Models/Folder';
69+
70+
const modelCollection = await Folder.get();
71+
const folderTree = AncestryCollection.treeOf(modelCollection);
72+
73+
AncestryCollection.isAncestryCollection(modelCollection); // false
74+
AncestryCollection.isAncestryCollection(folderTree); // true
75+
76+
ModelCollection.isModelCollection(folderTree); // true
77+
Collection.isCollection(folderTree); // true
78+
```

docs/calliope/attributes.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ Casting transforms values when accessing or setting attributes on a model.
1414
To define the casters on your model you should define a getter for the `casts` property.
1515

1616
<CodeGroup>
17+
1718
<CodeGroupItem title="Javascript">
19+
1820
```js
1921
// User.js
2022
import { Model } from '@upfrontjs/framework';
@@ -27,9 +29,11 @@ export default class User extends Model {
2729
}
2830
}
2931
```
32+
3033
</CodeGroupItem>
3134

3235
<CodeGroupItem title="Typescript">
36+
3337
```ts
3438
// User.ts
3539
import { Model } from '@upfrontjs/framework';
@@ -44,7 +48,9 @@ export default class User extends Model {
4448
}
4549
}
4650
```
51+
4752
</CodeGroupItem>
53+
4854
</CodeGroup>
4955

5056
**The following cast types are available:**
@@ -66,14 +72,17 @@ Casts the values to a [Collection](../helpers/collection.md) by calling the coll
6672

6773
#### `'datetime'`
6874

69-
Cast the values to the [given date time](../helpers/global-config.md#datetime) by calling the method or its constructor. If no date time defined in the config by default it will construct a new [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object with the value.
75+
Cast the values to the [given date time](../helpers/global-config.md#datetime) by calling the method or its constructor.
76+
Default: If no date time defined in the config by default it will construct a new [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object with the value. This when receives a `null` value it will return `null` instead of a `Date` set to unix epoch.
7077

7178
#### custom object
7279

7380
This is an object which implements the `AttributeCaster` type. Meaning it has a `get` and a `set` method both of which accepts a value, and an `Attributes` object (the equivalent of [getRawAttributes](#getrawattributes)) argument.
7481

7582
<CodeGroup>
83+
7684
<CodeGroupItem title="Javascript">
85+
7786
```js
7887
// User.js
7988
import { Model } from '@upfrontjs/framework';
@@ -96,6 +105,7 @@ export default class User extends Model {
96105
</CodeGroupItem>
97106

98107
<CodeGroupItem title="Typescript">
108+
99109
```ts
100110
// User.ts
101111
import { Model } from '@upfrontjs/framework';
@@ -116,7 +126,9 @@ export default class User extends Model {
116126
}
117127
}
118128
```
129+
119130
</CodeGroupItem>
131+
120132
</CodeGroup>
121133

122134
### Further casting methods

docs/calliope/model-collection.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ ModelCollection is a subclass of the [Collection](../helpers/collection.md), the
44

55
## Methods
66

7+
[[toc]]
8+
79
#### modelKeys
810

911
The `modelKeys` method returns the [primary key](./readme.md#getkey) of the models on a Collection.
@@ -29,6 +31,7 @@ modelCollection.findByKey(43, defaultModel); // Model
2931
```
3032

3133
#### isModelCollection
34+
<Badge text="static" type="warning"/>
3235

3336
The `isModelCollection` static method same as the [isCollection](../helpers/collection.md#iscollection) method on the collection, is used to evaluate that the given value is a ModelCollection.
3437
```js

docs/cookbook.md

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ In the documentation simple and concise examples are favoured to avoid too much
1313
- [Sending requests without models](#sending-requests-without-models)
1414
- [Alias methods](#alias-methods)
1515
- [Extend query builder functionality](#extend-query-builder-functionality)
16+
- [Using it with automated query builder packages](#using-it-with-automated-query-builder-packages)
1617
- [Send paginated requests](#send-paginated-requests)
1718

1819
#### Extend the collections to fit your needs.
@@ -46,7 +47,7 @@ const users = await User.latest()
4647
.get()
4748
.then(users => new UserCollection(users.toArray()));
4849

49-
if (users.areAwake().length === modelCollection.length) {
50+
if (users.areAwake().length === users.length) {
5051
await users.markAsReady();
5152
} else {
5253
console.log('Oh no, somebody\'s not ready yet!');
@@ -184,19 +185,21 @@ Extending/overwriting the model should not be a daunting task. If we wanted we c
184185
import type { FormatsQueryParameters, QueryParams, StaticToThis } from '@upfrontjs/framework';
185186
import { Model as BaseModel } from '@upfrontjs/framework';
186187

188+
type Appendage = string;
189+
187190
export default class Model extends BaseModel implements FormatsQueryParameters {
188191
protected appends: string[] = [];
189192

190-
public append(name: string): this {
193+
public append(name: Appendage): this {
191194
this.appends.push(name);
192195
return this;
193196
}
194197

195-
public static append<T extends StaticToThis<Model>>(this: T, name: string): T['prototype'] {
198+
public static append<T extends StaticToThis<Model>>(this: T, name: Appendage): T['prototype'] {
196199
this.newQuery().append(name);
197200
}
198201

199-
public withoutAppend(name: string): this {
202+
public withoutAppend(name: Appendage): this {
200203
this.appends = this.appends.filter(appended => appended !== name);
201204
return this;
202205
}
@@ -218,6 +221,85 @@ export default class Model extends BaseModel implements FormatsQueryParameters {
218221

219222
Now if our other models extend our own model they will have the option to set the `appends` field on the outgoing requests.
220223

224+
#### Using it with automated query builder packages
225+
226+
For even more convenience the package can be adjusted to be used with some sort of automated query parsing code on the backend.
227+
Closely similar to [query builder extending](#extend-query-builder-functionality) you may create a more generalised approach to allow setting such query params.
228+
229+
```ts
230+
import type { AttributeKeys, FormatsQueryParameters, StaticToThis, QueryParams } from '@upfrontjs/framework';
231+
import { Model as BaseModel } from '@upfrontjs/framework';
232+
233+
// example query parameters the back-end might be looking for.
234+
interface RequestParameters extends Record<string, unknown> {
235+
/**
236+
* Append these values if set.
237+
*/
238+
append?: string[];
239+
/**
240+
* Return with these relations if set.
241+
*/
242+
include?: (string | `${string}Count`)[];
243+
/**
244+
* Return only these fields if set.
245+
*/
246+
fields?: string[];
247+
/**
248+
* Only return records where these filters return true when this is set.
249+
*/
250+
filter?: Record<string, string>;
251+
/**
252+
* Sort by this property if set.
253+
*/
254+
sort?: string;
255+
}
256+
257+
export default class Model extends BaseModel implements FormatsQueryParameters {
258+
/**
259+
* The parameters that should be sent along in the next request.
260+
*
261+
* @protected
262+
*/
263+
protected requestParameters: RequestParameters = {};
264+
265+
/**
266+
* Set url parameters for the next request.
267+
*/
268+
public withParameter(params: RequestParameters): this {
269+
this.requestParameters = Object.assign(this.requestParameters, params);
270+
271+
return this;
272+
}
273+
274+
/**
275+
* Static version of the withParameter method.
276+
*
277+
* @param {RequestParameters} params
278+
*/
279+
public static withParameters<T extends StaticToThis<Model>>(this: T, params: RequestParameters): T['prototype'] {
280+
return new this().withParameter(params);
281+
}
282+
283+
/**
284+
* @inheritDoc
285+
*/
286+
public resetQueryParameters(): this {
287+
this.requestParameters = {};
288+
289+
return super.resetQueryParameters();
290+
}
291+
292+
/**
293+
* @inheritDoc
294+
*
295+
* @protected
296+
*/
297+
public formatQueryParameters(parameters: QueryParams & Record<string, any>): Record<string, any> {
298+
return Object.assign(parameters, this.requestParameters);
299+
}
300+
}
301+
```
302+
221303
#### Send paginated requests
222304

223305
While it's nice to be able to [paginate locally](./helpers/pagination.md) it might not be desired to get too much data upfront. In this case a pagination can be implemented that will only get the pages in question on an explicit request. Of course, you might change the typings and the implementation to fit your needs.

docs/getting-started/installation.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
# Installation
22

33
<CodeGroup>
4+
45
<CodeGroupItem title="npm">
6+
57
```shell
68
npm install @upfrontjs/framework
79
```
10+
811
</CodeGroupItem>
912

1013
<CodeGroupItem title="yarn">
14+
1115
```shell
1216
yarn install @upfrontjs/framework
1317
```
18+
1419
</CodeGroupItem>
20+
1521
</CodeGroup>
1622

1723
The library is transpiled to ES6 (currently the lowest supported version), but if you're using [Typescript](https://www.typescriptlang.org/), you could choose to use the source `.ts` files. To do so, import files from `/src` folder as opposed to the library root.

docs/getting-started/readme.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ const excellentStudentNames = students
1414
```
1515

1616
## What does it solve?
17-
There are number of solutions out there for fetching data and working with the response. However not all of these might be as scalable as one would hope. With state management, you might have a getter for users, but those users include all users, meaning for a custom collection you would need a new getter method. An on-demand ajax request written specifically to solve a single issue, while it is simple to do, it quickly gets repetitive and hard to maintain. [Upfront](./installation.md) solves the above by creating abstraction over the data in a unified api. Just like the above examples it can be used to fetch data on demand or be complimentary to state management libraries.
17+
There are number of solutions out there for fetching data and working with the response. However not all of these might be as maintainable as one would hope. With state management, you might have a getter for users, but those users include all users, meaning for a custom collection you would need a new getter method. An on-demand ajax request written specifically to solve a single issue, while it is simple to do, it quickly gets repetitive and laborious to maintain. [Upfront](./installation.md) solves the above by creating abstraction over the data in a unified api. Just like the above examples it can be used to fetch data on demand or be complimentary to state management libraries.
1818

1919
## Caveats
2020
While using this package increases ease of access and cuts down development time, it can also have unintended consequences. By pushing more logic to the client side you may expose backend logic such as data relations. Furthermore, if you're incorrectly implementing the [backend requirements](./installation.md#backend-requirements) you may introduce vulnerabilities such as sql injection.
2121

2222
---
2323

2424
As always you're encouraged to explore the [source](https://github.com/upfrontjs/framework) yourself or look at the [api reference](https://upfrontjs.github.io/framework/) to gain insight on how the package works, and the [tests](https://github.com/upfrontjs/framework/tree/main/tests) to see how it's used.
25-

0 commit comments

Comments
 (0)