-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
specify ID name and type #41
Comments
Hi ! And thanks for your feedback.
2.1 Embedded documents are not yet supported, but they should be fairly easy to deal with. You'll need to write your own |
Hi, Francois - thanks for the quick response!
EG (using posts and comments as the example - embedding comments with posts) |
No, if you need some form of intermediate persistence / caching, you'll have to implement it yourself in the REST client. Alternatively, you could put some of this logic in a custom saga. |
Thanks for clarifying. Perhaps it's an item for a feature request |
@fzaninotto Could you please give more details about how to map our "_id" and "id" in our admin-on-rest? |
How about something like that: const convertHTTPResponseToREST = (response, type, resource, params) => {
const { headers, json } = response;
switch (type) {
case GET_LIST:
case GET_MANY_REFERENCE:
if (!headers.has('content-range')) {
throw new Error('The Content-Range header is missing in the HTTP Response. The simple REST client expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare Content-Range in the Access-Control-Expose-Headers header?');
}
return {
data: json.map(record => ({ id: record._id, ...record })),
total: parseInt(headers.get('content-range').split('/').pop(), 10),
};
case CREATE:
return { data: { id: json._id, ...params.data } };
default:
return { data: { id: json._id, ...json } };
}
}; |
Excuse my maybe dumb question, but how do I actually get to a custom rest client? I mean everything is in place, I don't want to reinvent the wheel, just map the primary key. My main question being: Where should I put the code displayed here: https://marmelab.com/admin-on-rest/FAQ.html#can-i-have-custom-identifiers-primary-keys-for-my-resources? |
Coming back to my question, I'd like to answer it myself, for people coming from search-engines 🤓 Rest clients are now called Basically, what the exported function in the To create a dataProvider, start by creating a import jsonServerProvider from './dataProvider';
const dataProvider = jsonServerProvider('https://example.org/rest-api');
const App = () => (
<Admin dataProvider={dataProvider}>
...
</Admin>
}; |
@te-online thanks for that heads up on the documentation. I actually got pretty far into building a demo app that worked with a real server to CRUD objects against SQL via an auto-generated data access layer, when I found the subtle requirement that your key has to be "id" to use the ra-data-simple-rest data provider. I guess in their latest parlance I need to write some sort of data adapter that changes "payload" for the query for those types to use the key name I need? It seems like it would be much LESS of a headache and less work to supply the data provider some sort of key-value JSON blob that defines the key map overrides by their type, in a simple init method option. That's literally all I need, as it wasn't too much work to create the server-side API that conforms to the client's needs otherwise. Without this, it seems I either have to go touch a lot of files (4 per data type on the server side) to fudge an id alias property, or I have to go extend the data provider to add a lot of wrapping calls just to get it to use a different key name for queries. Too bad, this tool otherwise really lowers the bar for adding a CRUD interface for an existing system. I have 250+ types of objects I have to touch...That's a lot of work. Someone please tell me I'm missing something? |
So I started into what I was proposing above, adding another argument to the constructor to ra-data-simple-rest import call to specify a blob of key names by resource. What I quickly found is that unfortunately the ra-core itself builds the payloads and has a pretty tight dependency on key names being "id." It seems baked in the module at multiple levels, unfortunately. In other words, there is a tight/hard API dependency between ra-core and anything that would work with it, not just at the data provider, that it plays by ra-core's rules - which is that 'id' is your id parameter. |
I'm not sure if I understand your problem. But I've used // dataProvider.js
// ... imports
export default (apiUrls, httpClient = fetchUtils.fetchJson) => {
// ...
const convertHTTPResponse = (response, type, resource, params) => {
const { headers, json } = response;
switch (type) {
case GET_LIST:
case GET_MANY_REFERENCE:
// Apply transformations
if (json.data) {
json.data = json.data.map((document) => {
return applyDocumentFilters(document);
});
}
if (!headers.has('x-total-count')) {
throw new Error(
'The X-Total-Count header is missing in the HTTP Response. The jsonServer Data Provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?'
);
}
return {
data: json.data,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10)
};
case CREATE:
return { data: { ...params.data, id: json.data.uuid } };
default:
if (json.data && json.data.length > 0) {
// Apply transformations
json.data = json.data.map((document) => {
return applyDocumentFilters(document);
});
return { data: json.data };
} else if (json.data && json.data.constructor.name.toLowerCase() === 'object') {
json.data = applyDocumentFilters(json.data);
return { data: json.data, id: json.data.uuid };
}
return { data: [] };
}
};
// ...
const applyDocumentFilters = (document) => {
// Apply id from uuid parameter if necessary.
document.id = document.id || document.uuid;
// ...
return document;
};
return (type, resource, params) => {
const apiUrl = getAPIUrl(resource);
// ...
const { url, options } = convertDataRequestToHTTP(type, resource, params);
return httpClient(url, options).then((response) => convertHTTPResponse(response, type, resource, params));
};
}; |
@te-online not exactly, no. |
My Typescript and ECMAScript is a little rusty, but I think I managed to figure out how to roll and npm publish my own data provider, based on ra-data-simple-rest, since that seems to be what is needed to support my existing real-world data. It allows you to specify via a single simple json hash, say in your app.js, the resources that don't actually use 'id' for their identifier. React-admin will operate fine b/c on its side they use 'id', but the data provider translates to the real identifier property name on your behalf. It can be found in my github at zachrybaker/ra-data-rest-client and in npm.
Hopefully this will help others get up and running with a large pre-existing web service that can't be modified to play by react-admin's terms. @fzaninotto I saw in documentation an appeal to let your team know of data providers. Hopefully this is helpful! |
@zachrybaker Okay, cool that you solved it :-) Just to be clear, you don't have to publish an npm package for your custom data provider. You can create a JavaScript file in your project and then import your data provider from that file. This page didn't help you in terms of documentation? https://marmelab.com/react-admin/DataProviders.html |
The package is for reuse and for others, as packages go. There's a PR out there #5290 to bring that into the provider list, as this is undoubtedly generic enough yet helpful for others. Honestly I'm pretty fresh on react and react-admin, so I may not be the best person to ask, b/c I'm still learning to think more react-y, but IMO that page would do well to re-emphasize the hard requirement on 'id' being your identifier property name, at a minimum. For me there was a lot to understand even after reading that page before I knew what I needed to do conceptually, and then as is sometimes typical, actually implementing an alternate data provider really brought it all together for me, mentally-speaking. |
See https://marmelab.com/react-admin/DataProviders.html#response-format
I think it does already. Anyway, your dataProvider is indeed a good idea. Good job 👍 |
Quick documentation question. I've published a .NetCore REST server zachrybaker/React-Admin-RestServer that speaks the react-admin dialect you guys chose and works nicely with #5290 or the default provider. It basically bootstraps the process of getting your resources exposed by [re]generating your data access layer from examining a database for you, and you just have to pick what you actually expose from that. Do you guys document any server API projects or know any existing place to mention this that others might find it? Thanks in advance. |
I think you can mention it in the miscelanous section of the documentation :) |
@fzaninotto can you create an example in sandbox or repo for that. I am trying to find solution for that. I couldn't understand the document to change my primary key to _id. I take my data from MongoDB and my objects id are start with _id. So please make example... |
@sayinmehmet47 Sorry, we can't provide a CodeSandbox running against MongoDB as MongoDB doesn't come with a native HTTP API. Also, we've documented how to map _id to id in https://marmelab.com/admin-on-rest/FAQ.html#can-i-have-custom-identifiers-primary-keys-for-my-resources. If that's not enough for you, then I suggest you hire a React / React-admin expert to guide you through the dataProvider logic. |
@fzaninotto hiring someone before hired is very reasonable thanks |
Hi,
First let me say that this tool is awesome.
Looks great and has introduced me to React as well.
I have a few issues / questions.. I'm not sure where to put them and if they should be separate threads. Let me know if I should be doing things differently.
2.1 Is there a way to specify inline/embed rather than reference
2.2 Or is there an obvious way to get access to other objects/resources so that I can embed with my own custom rest client in convertRESTRequestToHTTP ?
Thanks!!
The text was updated successfully, but these errors were encountered: