Skip to content

v2.3.0 #15

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

Merged
merged 13 commits into from
Jul 9, 2018
Merged
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
19 changes: 6 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Easily write offline-first react-native applications with your own REST API. Thi
- [Middlewares](#middlewares)
- [Using your own driver for caching](#using-your-own-driver-for-caching)
- [Types](#types)
- [Roadmap](#roadmap)

## Installation

Expand Down Expand Up @@ -141,6 +140,7 @@ A couple of notes :
Name | Description | Parameters | Return value
------ | ------ | ------ | ------
`fetch` | Fires a network request to one of your service with additional options, see [fetch options](#fetch-options) | `service: string, options?: IFetchOptions` | `Promise<any>`
`get`, `post`, `head`... | Each HTTP method has a dedicated method that acts just like `fetch` | `service: string, options?: IFetchOptions` | `Promise<any>`
`fetchHeaders` | Just like `fetch` but only returns the HTTP headers of the reponse | `service: string, options?: IFetchOptions` | `Promise<any>`
`clearCache` | Clears all the cache, or just the one of a specific service | `service?: string` | `Promise<void>`
`setOptions` | Sets or update the API options of the wrapper | `options: IAPIOptions` | `void`
Expand All @@ -156,6 +156,7 @@ Key | Type | Description | Example
`domains` | `{ default: string, [key: string]: string }` | **Required**, full URL to your domains | `domains: {default: 'http://myapi.tld', staging: 'http://staging.myapi.tld' },`
`prefixes` | `{ default: string, [key: string]: string }` | **Required**, prefixes your API uses, `default` is required, leave it blank if you don't have any | `{ default: '/api/v1', apiV2: '/api/v2' }`
`middlewares` | `APIMiddleware[]` | Optionnal middlewares, see [middlewares](#middlewares) | `[authFunc, trackFunc]`
`responseMiddleware` | `ResponseMiddleware` | Optionnal middleware to alter your API responses, see [middlewares](#middlewares) | `alterFunction`
`debugAPI` | `boolean` | Optional, enables debugging mode, printing what's the wrapper doing
`printNetworkRequests` | `boolean` | Optional, prints all your network requests
`disableCache` | `boolean` | Optional, completely disables caching (overriden by service definitions & `fetch`'s `option` parameter)
Expand All @@ -177,7 +178,8 @@ Key | Type | Description | Example
`method` | `GET` | Optional HTTP method of your request, defaults to `GET` | `OPTIONS...`
`domain` | `string` | Optional specific domain to use for this service, provide the key you set in your `domains` API option
`prefix` | `string` | Optional specific prefix to use for this service, provide the key you set in your `prefixes` API option
`middlewares` | `APIMiddleware[]` | Optional array of middlewares that override the ones set globally in your `middlewares` API option, , see [middlewares](#middlewares)
`middlewares` | `APIMiddleware[]` | Optional array of middlewares that override the ones set globally in your `middlewares` API option, see [middlewares](#middlewares)
`responseMiddleware` | `ResponseMiddleware` | Optionnal middleware to alter your API responses that override the one set globally in your `middlewares` API option, see [middlewares](#middlewares) | `alterFunction`
`disableCache` | `boolean` | Optional, disables the cache for this service (override your [API's global options](#api-options))
`capService` | `boolean` | Optional, enable or disable capping for this specific service, see [limiting the size of your cache](#limiting-the-size-of-your-cache)
`capLimit` | `number` | Optional quantity of cached items for this specific service, defaults to `50`, see [limiting the size of your cache](#limiting-the-size-of-your-cache)
Expand All @@ -199,6 +201,7 @@ Key | Type | Description | Example
`queryParameters` | `{ [key: string]: string }` | Query parameters that will be appended to your service's path, , see [path and query parameters](#path-and-query-parameters) | `{ refresh: true, orderBy: 'date' }`
`headers` | `{ [key: string]: string }` | HTTP headers you need to pass in your request
`middlewares` | `APIMiddleware[]` | Optional array of middlewares that override the ones set globally in your `middlewares` API option and in your service's definition, , see [middlewares](#middlewares)
`responseMiddleware` | `ResponseMiddleware` | Optionnal middleware to alter your API responses that override the one set globally in your `middlewares` API option and in your service's definition, see [middlewares](#middlewares) | `alterFunction`
`fetchOptions` | `any` | Optional, any value passed here will be merged into the options of react-native's `fetch` method so you'll be able to configure anything not provided by the wrapper itself

## Path and query parameters
Expand Down Expand Up @@ -229,14 +232,4 @@ The URL to your endpoints are being constructed with **your domain name, your op

Every API interfaces [can be seen here](src/interfaces.ts) so you don't need to poke around the parameters in your console to be aware of what's available to you :)

> 💡 These are Typescript defintions, so they should be displayed in your editor/IDE if it supports it.

## Roadmap

Pull requests are more than welcome for these items, or for any feature that might be missing.

- [ ] Improve capping performance by storing how many items are cached for each service so we don't have to parse the whole service's dictionary each time
- [ ] Add a method to check for the total size of the cache, which would be useful to trigger a clearing if it reaches a certain size
- [ ] Add automated testing
- [x] Thoroughly test custom caching drivers, maybe provide one (realm or sqlite)
- [x] Write a demo
> 💡 These are Typescript defintions, so they should be displayed in your editor/IDE if it supports it.
3 changes: 0 additions & 3 deletions demo/.babelrc

This file was deleted.

6 changes: 0 additions & 6 deletions demo/.buckconfig

This file was deleted.

8 changes: 8 additions & 0 deletions demo/.expo/packager-info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"expoServerPort": null,
"packagerPort": null,
"packagerPid": null,
"expoServerNgrokUrl": null,
"packagerNgrokUrl": null,
"ngrokPid": null
}
7 changes: 7 additions & 0 deletions demo/.expo/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"hostType": "localhost",
"lanType": "ip",
"dev": true,
"minify": false,
"urlRandomness": "p8-un3"
}
45 changes: 0 additions & 45 deletions demo/.flowconfig

This file was deleted.

1 change: 0 additions & 1 deletion demo/.gitattributes

This file was deleted.

53 changes: 0 additions & 53 deletions demo/.gitignore

This file was deleted.

1 change: 0 additions & 1 deletion demo/.watchmanconfig

This file was deleted.

101 changes: 89 additions & 12 deletions demo/src/Demo.js → demo/App.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from 'react-native';
import OfflineFirstAPI from 'react-native-offline-api';
import styles from 'app/src/styles';

const NativeModules = require('NativeModules');

const API_OPTIONS = {
domains: { default: 'https://icanhazdadjoke.com' },
prefixes: { default: '' },
middlewares: [setHeadersMiddleware],
debugAPI: false,
debugAPI: true,
printNetworkRequests: true
};

const API_SERVICES = {
random: { path: '', expiration: 5 * 1000 },
search: { path: 'search' }
random: { path: '', expiration: 5 * 1000, responseMiddleware: (res) => ({ ...res, timestamp: Date.now() }) },
search: {
path: 'search',
responseMiddleware: (res) => (
{
...res,
results: res.results.map((result) => ({ ...result, timestamp: Date.now()} ))
}
)
}
};

const api = new OfflineFirstAPI(API_OPTIONS, API_SERVICES);
Expand All @@ -36,9 +43,12 @@ export default class Demo extends Component {
}

async _fetchRandomJoke () {
// api.get('random', { disableCache: true });
// return;
// api.get('random', { disableCache: true });
try {
this.setState({ randomJokeStatus: 1, searchJokeStatus: 0, fetchStart: Date.now() });
const req = await api.fetch('random');
const req = await api.get('random');

if (req.status === 200) {
this.setState({ randomJokeStatus: 2, randomJoke: req, fetchEnd: Date.now() });
Expand All @@ -53,7 +63,7 @@ export default class Demo extends Component {
async _searchJoke (term) {
try {
this.setState({ searchJokeStatus: 1, randomJokeStatus: 0, fetchStart: Date.now() });
const req = await api.fetch('search', { queryParameters: { term } });
const req = await api.get('search', { queryParameters: { term } });

if (req.status === 200 && req.results && req.results.length) {
this.setState({ searchJokeStatus: 2, searchedJokes: req.results, fetchEnd: Date.now() });
Expand Down Expand Up @@ -82,11 +92,22 @@ export default class Demo extends Component {
);
}

get description () {
return (
<ScrollView style={styles.descriptionContainer}>
<Text style={styles.desc}>This demo showcases a very basic usage of the plugin. It uses the default cache driver (AsyncStorage).</Text>
<Text style={styles.desc}>Two endpoints are registered as services (random & search). Both are using the GET method.</Text>
<Text style={styles.desc}>You can see how fast the cache resolution is once your request has already been fired and when its cached data is fresh...</Text>
<Text style={styles.desc}>Restart this application with your phone in offline mode to see how easy it is to make your app offline-first !</Text>
</ScrollView>
);
}

get btns () {
return (
<View style={styles.btnsContainer}>
{ this.btn(this._fetchRandomJoke, 'Fetch a random dad joke', styles.fetchBtn) }
{ this.btn(() => { this._searchJoke('dogs') }, 'Look for a joke about dogs', styles.fetchBtn) }
{ this.btn(this._fetchRandomJoke, 'Fetch a random dad joke \n (cache expires after 5sec)', styles.fetchBtn) }
{ this.btn(() => { this._searchJoke('dogs') }, "Look for a joke about dogs \n (default 5' expiration)", styles.fetchBtn) }
{ this.btn(this._clearCache, 'Clear cache', styles.clearBtn) }
</View>
);
Expand All @@ -104,11 +125,12 @@ export default class Demo extends Component {
<Text>Loading the dadliest dad joke...</Text>
);
} else if (randomJokeStatus === 2 && randomJoke ) {
const { id, joke } = randomJoke;
const { id, joke, timestamp } = randomJoke;
content = (
<View style={styles.jokeContainer}>
<Text>Joke id : { id}</Text>
<Text>Joke fetched in { fetchEnd - fetchStart } ms</Text>
<Text>Joke parsed at date : { timestamp } (added to response through responseMiddleware)</Text>
<Text>{ joke }</Text>
</View>
);
Expand Down Expand Up @@ -136,12 +158,14 @@ export default class Demo extends Component {
<Text>Looking for dad jokes...</Text>
);
} else if (searchJokeStatus === 2 && searchedJokes ) {
console.log('SEARCHED JOKES', searchedJokes)
content = searchedJokes.map((result, index) => {
const { id, joke } = result;
const { id, joke, timestamp } = result;
return (
<View style={styles.jokeContainer} key={`key-${index}`}>
<Text>Joke id : { id}</Text>
<Text>Joke fetched in { fetchEnd - fetchStart } ms</Text>
<Text>Joke parsed at date : { timestamp } (added to response through responseMiddleware)</Text>
<Text>{ joke }</Text>
</View>
);
Expand All @@ -152,7 +176,7 @@ export default class Demo extends Component {
);
}
return (
<ScrollView contentContainerStyle={styles.randomJokeContainer}>
<ScrollView style={styles.randomJokeContainer}>
{ content }
</ScrollView>
);
Expand All @@ -162,10 +186,63 @@ export default class Demo extends Component {
return (
<View style={styles.container}>
<Text style={styles.title}>react-native-offline-api</Text>
{ this.description }
{ this.btns }
{ this.randomJoke }
{ this.searchedJokes }
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ACCFCC',
padding: 40
},
title: {
textAlign: 'center',
color: '#FFFFFF',
fontSize: 18,
marginBottom: 10
},
descriptionContainer: {
flex: 1
},
desc: {
marginBottom: 5
},
btnsContainer: {
flex: 1,
marginVertical: 10
},
btn: {
alignSelf: 'center',
justifyContent: 'center',
backgroundColor: 'white',
width: '75%',
height: 50,
marginVertical: 10
},
btnLabel: {
textAlign: 'center',
color: '#FFFFFF',
fontSize: 15
},
clearBtn: {
backgroundColor: '#8A0917'
},
fetchBtn: {
backgroundColor: '#595241'
},
randomJokeContainer: {
flex: 1,
marginTop: 30
},
jokeContainer: {
flex: 1,
padding: 5,
marginBottom: 10
}
});
Loading