Skip to content

Commit 2c29225

Browse files
committed
Merge branch '2.1'
2 parents 3d0ef35 + e29e544 commit 2c29225

24 files changed

+303
-122
lines changed

admin/authentication-support.md

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,25 @@
22

33
Authentication can easily be handled when using the API Platform's admin library.
44
In the following section, we will assume [the API is secured using JWT](https://api-platform.com/docs/core/jwt), but the
5-
process is similar for other authentication mechanisms.
5+
process is similar for other authentication mechanisms. The `login_uri` is the full URI to the route specified by the `firewalls.login.json_login.check_path` config in the [JWT documentation](https://api-platform.com/docs/core/jwt).
66

77
The first step is to create a client to handle the authentication process:
88

99
```javascript
10-
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR } from 'admin-on-rest';
10+
// src/authClient.js
11+
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'admin-on-rest';
1112

12-
const entrypoint = 'https://demo.api-platform.com'; // Change this by your own entrypoint
13+
// Change this to be your own login check route.
14+
const login_uri = 'https://demo.api-platform.com/login_check';
1315

1416
export default (type, params) => {
1517
switch (type) {
1618
case AUTH_LOGIN:
1719
const { username, password } = params;
18-
const request = new Request(`${API_ENTRYPOINT}/login`, {
19-
body: JSON.stringify({
20-
username: params.username,
21-
password: params.password,
22-
}),
23-
headers: new Headers({
24-
'Content-Type': 'application/json',
25-
}),
26-
method: 'POST',
20+
const request = new Request(`${login_uri}`, {
21+
method: 'POST',
22+
body: JSON.stringify({ email: username, password }),
23+
headers: new Headers({ 'Content-Type': 'application/json' }),
2724
});
2825

2926
return fetch(request)
@@ -48,6 +45,9 @@ export default (type, params) => {
4845
return Promise.reject();
4946
}
5047
break;
48+
49+
case AUTH_CHECK:
50+
return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
5151

5252
default:
5353
return Promise.resolve();
@@ -59,24 +59,18 @@ Then, configure the `Admin` component to use the authentication client we just c
5959

6060
```javascript
6161
import React from 'react';
62-
import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
63-
import { HydraAdmin, hydraClient, fetchHydra as baseFetchHydra } from '@api-platform/admin';
62+
import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
63+
import { HydraAdmin, hydraClient, fetchHydra as baseFetchHydra } from '@api-platform/admin';
6464
import authClient from './authClient';
6565
import { Redirect } from 'react-router-dom';
6666

6767
const entrypoint = 'https://demo.api-platform.com'; // Change this by your own entrypoint
68-
69-
const fetchHeaders = {
70-
'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
71-
};
72-
68+
const fetchHeaders = {'Authorization': `Bearer ${window.localStorage.getItem('token')}`};
7369
const fetchHydra = (url, options = {}) => baseFetchHydra(url, {
7470
...options,
7571
headers: new Headers(fetchHeaders),
7672
});
77-
7873
const restClient = api => hydraClient(api, fetchHydra);
79-
8074
const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint, { headers: new Headers(fetchHeaders) })
8175
.then(
8276
({ api }) => ({ api }),
@@ -85,24 +79,19 @@ const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint,
8579
case 401:
8680
return Promise.resolve({
8781
api: result.api,
88-
customRoutes: [
89-
{
90-
props: {
91-
path: '/',
92-
render: () => (
93-
<Redirect to={`/login`}/>
94-
),
95-
},
82+
customRoutes: [{
83+
props: {
84+
path: '/',
85+
render: () => <Redirect to={`/login`}/>,
9686
},
97-
],
87+
}],
9888
});
9989

10090
default:
10191
return Promise.reject(result);
10292
}
10393
},
104-
)
105-
;
94+
);
10695

10796
export default props => (
10897
<HydraAdmin
@@ -112,8 +101,6 @@ export default props => (
112101
restClient={restClient}
113102
/>
114103
);
115-
116-
export default Admin;
117104
```
118105

119106
Refer to [the chapter dedicated to authentication in the Admin On Rest documentation](https://marmelab.com/admin-on-rest/Authentication.html)

admin/getting-started.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,16 @@ Finally, install the `@api-platform/admin` library:
2828
Edit the `src/App.js` file like the following:
2929

3030
```javascript
31-
import React, { Component } from 'react';
31+
import React from 'react';
3232
import { HydraAdmin } from '@api-platform/admin';
3333

34-
class App extends Component {
35-
render() {
36-
return <HydraAdmin entrypoint="https://demo.api-platform.com"/> // Replace with your own API entrypoint
37-
}
38-
}
39-
40-
export default App;
34+
export default () => <HydraAdmin entrypoint="https://demo.api-platform.com"/>; // Replace with your own API entrypoint
4135
```
4236

4337
Your new administration interface is ready! Type `yarn start` to try it!
4438

4539
Note: if you don't want to hardcode the API URL, you can [use an environment variable](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables).
4640

47-
Note: make sure `entrypoint` does not end with a `/`.
48-
4941
## Customizing the Admin
5042

5143
The API Platform's admin parses the Hydra documentation exposed by the API and transforms it to an object data structure. This data structure can be customized to add, remove or customize resources and properties. To do so, we can leverage the `AdminBuilder` component provided by the library. It's a lower level component than the `HydraAdmin` one we used in the previous example. It allows to access to the object storing the structure of admin's screens.
@@ -78,9 +70,7 @@ const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint)
7870
})
7971
;
8072

81-
export default (props) => (
82-
<HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint={entrypoint}/>
83-
);
73+
export default (props) => <HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint={entrypoint}/>;
8474
```
8575

8676
The `fieldComponent` property of the `Field` class allows to set the component used to render a property in list and show screens.
@@ -157,9 +147,7 @@ const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint)
157147
})
158148
;
159149

160-
export default (props) => (
161-
<HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint={entrypoint}/>
162-
);
150+
export default (props) => <HydraAdmin apiDocumentationParser={apiDocumentationParser} entrypoint={entrypoint}/>;
163151
```
164152

165153
__Note__: In this example, we choose to send the file via a multi-part form data, but you are totally free to use another solution (like `base64`). But keep in mind that multi-part form data is the most efficient solution.
@@ -176,7 +164,7 @@ import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocument
176164

177165
const entrypoint = 'https://demo.api-platform.com';
178166

179-
class App extends Component {
167+
export default class extends Component {
180168
state = {api: null};
181169

182170
componentDidMount() {
@@ -197,8 +185,6 @@ class App extends Component {
197185
return <AdminBuilder api={this.state.api} restClient={hydraClient(entrypoint)}/>
198186
}
199187
}
200-
201-
export default App;
202188
```
203189

204190
Previous chapter: [Introduction](index.md)

admin/handling-relations-to-collections.md

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Currently, API Platform Admin doesn't handle `to-many` relations. The core library [is being patched](https://github.com/api-platform/core/pull/1189)
44
to document relations to collections through OWL.
55

6-
During the meantime, it is possible to configure manually API Platform to handle relations to collections.
6+
In the meantime, it is possible to manually configure API Platform to handle relations to collections.
77

88
We will create the admin for an API exposing `Person` and `Book` resources linked with a `many-to-many`
99
relation between them (trough the `authors` property).
@@ -77,18 +77,16 @@ Let's customize the components used for the `authors` property:
7777
```javascript
7878
import React, { Component } from 'react';
7979
import { ReferenceArrayField, SingleFieldList, ChipField, ReferenceArrayInput, SelectArrayInput } from 'admin-on-rest';
80-
import { AdminBuilder, hydraClient } from 'api-platform-admin';
80+
import { AdminBuilder, hydraClient } from '@api-platform/admin';
8181
import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation';
8282

8383
const entrypoint = 'https://demo.api-platform.com';
8484

85-
class Admin extends Component {
86-
state = {api: null};
85+
export default class extends Component {
86+
state = {api: null, resources: null};
8787

8888
componentDidMount() {
89-
parseHydraDocumentation(entrypoint).then(api => {
90-
const r = api.resources;
91-
89+
parseHydraDocumentation(entrypoint).then({api, resources} => {
9290
const books = r.find(r => 'books' === r.name);
9391

9492
// Set the field in the list and the show views
@@ -107,31 +105,29 @@ class Admin extends Component {
107105
</ReferenceArrayInput>
108106
;
109107

110-
this.setState({api: api});
108+
this.setState({api, resources});
111109
}
112110
)
113111
}
114112

115113
render() {
116114
if (null === this.state.api) return <div>Loading...</div>;
117115

118-
return <AdminBuilder api={this.state.api} restClient={hydraClient(entrypoint)}/>
116+
return <AdminBuilder api={this.state.api} restClient={hydraClient({entrypoint: entrypoint, resources: this.state.resources})}/>
119117
}
120118
}
121-
122-
export default Admin;
123119
```
124120

125-
The admin now properly handle this `to-many` relation!
121+
The admin now properly handles this `to-many` relation!
126122

127123
## Using an Autocomplete Input for Relations
128124

129-
We'll do a last improvement to our admin: transform the relation selector we just created to use autocompletion.
125+
We'll make one last improvement to our admin: transforming the relation selector we just created to use autocompletion.
130126

131127
Start by adding a "partial search" filter on the `name` property of the `Book` resource class.
132128

133129
```yaml
134-
# etc/packages/api_platform.yaml
130+
# config/api_filters.yml
135131

136132
services:
137133
person.search_filter:

client-generator/index.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
# The API Platform Client Generator
22

3-
API Platform Client Generator is a generator to scaffold app with Create-Retrieve-Update-Delete features for any API exposing a Hydra documentation for:
3+
API Platform Client Generator is a generator for scaffolding apps with Create-Retrieve-Update-Delete features for any API exposing a Hydra documentation. Currently the following targets are available:
4+
45
* React/Redux
56
* Vue.js
67

7-
Works especially well with APIs built with the [API Platform](https://api-platform.com) framework.
8+
The generator works especially well with APIs built with the [API Platform](https://api-platform.com) framework.
89

910
## Features
1011

1112
* Generate high-quality ES6 components and files built with [React](https://facebook.github.io/react/), [Redux](http://redux.js.org), [React Router](https://reacttraining.com/react-router/) and [Redux Form](http://redux-form.com/) including:
1213
* A list view
1314
* A creation form
14-
* An edition form
15-
* A deletion button
15+
* An editing form
16+
* A delete button
1617
* Use the Hydra API documentation to generate the code
1718
* Generate the suitable HTML5 input type (`number`, `date`...) according to the type of the API property
1819
* Display of the server-side validation errors under the related input (if using API Platform Core)
1920
* Client-side validation (`required` attributes)
20-
* The generated HTML is compatible with [Bootstrap](https://getbootstrap.com/) and include mandatory classes
21+
* The generated HTML is compatible with [Bootstrap](https://getbootstrap.com/) and includes mandatory classes
2122
* The generated HTML code is accessible to people with disabilities ([ARIA](https://www.w3.org/WAI/intro/aria) support)
2223
* The Redux and the React Router configuration is also generated
2324

client-generator/react.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,27 @@ Install the generator globally:
1515

1616
Reference the Bootstrap CSS stylesheet in `public/index.html` (optional):
1717

18+
Bootstrap 3 - last release 0.1.15
1819
```html
1920
<!-- ... -->
2021
<title>React App</title>
21-
22+
2223
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
2324
</head>
2425
<!-- ... -->
2526
```
27+
Bootstrap 4 - from release 0.1.16
28+
29+
```html
30+
<!-- ... -->
31+
<title>React App</title>
32+
33+
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
34+
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
35+
</head>
36+
<!-- ... -->
37+
```
38+
2639

2740
In the app directory, generate the files for the resource you want:
2841

@@ -35,6 +48,7 @@ The code is ready to be executed! Register the generated reducers and components
3548
```javascript
3649
import React from 'react';
3750
import ReactDom from 'react-dom';
51+
import registerServiceWorker from './registerServiceWorker';
3852
import { createStore, combineReducers, applyMiddleware } from 'redux';
3953
import { Provider } from 'react-redux';
4054
import thunk from 'redux-thunk';
@@ -43,7 +57,7 @@ import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
4357
import createBrowserHistory from 'history/createBrowserHistory';
4458
import { syncHistoryWithStore, routerReducer as routing } from 'react-router-redux'
4559

46-
// Replace "foo" by the name of the resource type
60+
// Replace "foo" with the name of the resource type
4761
import foo from './reducers/foo/';
4862
import fooRoutes from './routes/foo';
4963

@@ -65,6 +79,8 @@ ReactDom.render(
6579
</Provider>,
6680
document.getElementById('root')
6781
);
82+
83+
registerServiceWorker();
6884
```
6985

7086
Previous chapter: [Introduction](index.md)

client-generator/troubleshooting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Troubleshooting
22

3-
* The generator does not perform any authentication, so you must ensure that all referenced hydra paths for your API are
3+
* The generator does not perform any authentication, so you must ensure that all referenced Hydra paths for your API are
44
accessible anonymously. If you are using API Platform this will at least include:
55

66
```

client-generator/vuejs.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import Vuex from 'vuex';
3838
import VueRouter from 'vue-router';
3939
import App from './App.vue';
4040

41-
// Replace "foo" by the name of the resource type
41+
// Replace "foo" with the name of the resource type
4242
import foo from './store/modules/foo/';
4343
import fooRoutes from './routes/foo';
4444

@@ -89,7 +89,7 @@ Replace the `App.vue` file with the following :
8989
<div class="container">
9090
<nav>
9191
<ul class="nav nav-justified">
92-
<!-- Replace Foo by your resource name -->
92+
<!-- Replace Foo with your resource name -->
9393
<li class="active"><router-link :to="{ name: 'FooList' }" class="active">Foos</router-link></li>
9494
</ul>
9595
</nav>

core/content-negotiation.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ Both XML and JSON formats are experimental and there are no assurance that we wi
1111

1212
API Platform Core will automatically detect the best resolving format depending on:
1313

14-
* enabled formats (link to docs for this / see below)
15-
* the `Accept` HTTP header
16-
* Alternatively to send the proper `Accept` HTTP header, you can also request a specific format by adding its name as the extension of the URL.
17-
* Available formats are :
14+
* enabled formats (see below)
15+
* the requested format, specified in either the `Accept` HTTP header or as an extension appended to the URL
16+
17+
Available formats are:
1818

1919
Format | Format name | MIME types | Backward Compatibility guaranteed
2020
----------------------------------------------------------------|--------------|-------------------------------|----------------------------------------
@@ -134,8 +134,8 @@ final class CustomItemNormalizer implements NormalizerInterface, DenormalizerInt
134134
}
135135
```
136136

137-
For example if you want to make the `csv` format to work for even complex entities with a lot of hierarchy, you have
138-
to flatten or remove too complex relations:
137+
For example if you want to make the `csv` format work for even complex entities with a lot of hierarchy, you have to
138+
flatten or remove too complex relations:
139139

140140
```php
141141
<?php

0 commit comments

Comments
 (0)