1

I'm using admin on rest with express/mongodb, every things works correctly (CRUD), but I have some errors that have appeared and I have no explanation for that, when I create an object a notification is displayed "element does not exist" but the object is created correctly and stored in mongodb. And when I try to update a object (Edit) a notification is displayed "Incorrect element" but the object is been updated correctly and stored in mongodb.

this is my server code:

// =================================================================
// configuration ===================================================
// =================================================================
var port = process.env.PORT || 8060; // used to create, sign, and verify tokens

mongoose.connect(config.database, {
    useNewUrlParser: true,
    user: config.database_user,
    pass: config.database_pass
});
// connect to database
app.set('superSecret', config.secret); // secret variable

// use body parser so we can get info from POST and/or URL parameters
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// use morgan to log requests to the console
app.use(morgan('dev'));
app.use(cors());

app.use(function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    res.header("Access-Control-Expose-Headers", "X-Total-Count, Content-Range");
    next();
});

app.set('etag', false);
// =================================================================
// Post module ================================================
// =================================================================

//-------------------------------
// list all post-----------------
//-------------------------------

app.get('/post', function (req, res) {
    Post.find({}, function (err, posts) {
        var postsMap = [];

        posts.forEach(function (post) {
            postsMap.push({ id: post._id, title: post.title, content: post.content})
        });
        res.setHeader('Content-Range', posts.length);
        res.send(postsMap);
    });
});

//-------------------------------
// find a postt-----------------
//-------------------------------

app.get('/post/:id', function (req, res) {
    Post.findById({_id: req.params.id }, function (err, post) {
        res.send(post);
    });
});


//-------------------------------
// create new post-----------------
//-------------------------------

app.post('/post', apiRoutes, function (req, res) {


    var post = new Post({
        title: req.body.content,
        content: req.body.title
    });

    post.save(function(err) {
        if (err) throw err;

        res.json({ success: true });
    });
});

//-------------------------------
// update a post-----------------
//-------------------------------

app.put('/post/:id', apiRoutes, function (req, res) {
    if (typeof req.body.content === 'undefined' || typeof req.body.title === 'undefined') {
        res.send(400, { message: 'no content provided' })
    } else {
        Post.update({ '_id': req.params.id }, { title: req.body.title, content: req.body.content }, function (err, post) {
            if (err) return res.send(500, { error: err });
            return res.send({ message: 'success update', post: post });
        });
    }
});
//-------------------------------
// delete a post-----------------
//-------------------------------

app.delete('/post/:id', apiRoutes, function (req, res) {
    if (typeof req.body.content === 'undefined' || typeof req.body.title === 'undefined') {
        res.send(400, { message: 'no content provided' })
    } else {
        Post.delete({ '_id': req.params.id }, { title: req.body.title, content: req.body.content }, function (err, post) {
            if (err) return res.send(500, { error: err });
            return res.send({ message: 'success update', post: post });
        });
    }
});

this is some of my rest client request apicalls :

OPTIONS /post 204 0.096 ms - 0
POST /post 200 2.179 ms - 16
OPTIONS /post/undefined 204 0.098 ms - 0
GET /post/undefined 200 0.288 ms - -
OPTIONS /post?filter=%7B%7D&range=%5B0%2C9%5D&sort=%5B%22id%22%2C%22DESC%22%5D 204 0.065 ms - 0
GET /post?filter=%7B%7D&range=%5B0%2C9%5D&sort=%5B%22id%22%2C%22DESC%22%5D 200 2.977 ms - 589
OPTIONS /post/5d4819ed1458a84b14295626 204 0.061 ms - 0
GET /post/5d4819ed1458a84b14295626 200 1.411 ms - 76
PUT /post/5d4819ed1458a84b14295626 200 1.422 ms - 64
OPTIONS /post?filter=%7B%7D&range=%5B0%2C9%5D&sort=%5B%22id%22%2C%22DESC%22%5D 204 0.071 ms - 0
GET /post?filter=%7B%7D&range=%5B0%2C9%5D&sort=%5B%22id%22%2C%22DESC%22%5D 200 1.947 ms - 643[![enter image description here][1]][1]

These two requests are ambiguous for some reason

OPTIONS /post/undefined 204 0.088 ms - 0
GET /post/undefined 200 0.536 ms - -

I'm using simpleRestClient , my App.js :

const httpClient = (url, options = {}) => {
    if (!options.headers) {
        options.headers = new Headers({ Accept: 'application/json' });
    }
    options.headers.set('x-access-token', localStorage.getItem('token'));
    return fetchUtils.fetchJson(url, options);
};

const restClient = simpleRestClient(API_URL, httpClient);

const App = () => (
    <Admin
        title="أرشيفارا"
        customRoutes={customRoutes}
        customReducers={{ theme: themeReducer }}
        menu={Menu}
        authClient={authClient}
        restClient={restClient}
        appLayout={Layout}
        messages={translations}
    >   
        <Resource name="post" list={PostList} edit={PostEdit} create={PostCreate} />

    </Admin>
);

export default App;

enter image description here enter image description here

mariem
  • 56
  • 1
  • 8

2 Answers2

1

This is most probably because mongo react-admin expects all resources to have an id property, and not _id like mongo set it by default.

You'll have to decorate the dataProvider (or modify your API) so that it transform _id into id.

If you're not sure about how to decorate the dataProvider, ping me here and I'll update the answser with an example.

PS: migrate from admin-on-rest to react-admin (the new shiny version) :)

// In src/myRestClient.js

// Convert a MongoDB entity which has an _id property
// to an entity with an id property as react-admin expect
const convertToReactAdmin = ({ _id, ...item }) => ({
    id: _id,
    ...item,
});

// Decorate the simple rest client so that it convert the data from the API
// in the format expected by react-admin
const mongoDBClient = dataProvider => async (type, resource, params) => {
    // Execute the API call and wait for it to respond
    // It will always return an object with a data, and sometime a total (GET_LIST and GET_MANY)
    const { data, total } = await dataProvider(type, resource, params);

    switch (type) {
        case 'GET_LIST':
        case 'GET_MANY':
        case 'GET_MANY_REFERENCE':
            return {
                data: data.map(convertToReactAdmin),
                total, // For GET_MANY, total will be undefined
            };
        case 'GET_ONE':
        case 'CREATE':
        case 'UPDATE':
        case 'DELETE':
            return { data: convertToReactAdmin(data) };
        default:
            return { data };
    }
};

const httpClient = (url, options = {}) => {
    if (!options.headers) {
        options.headers = new Headers({ Accept: 'application/json' });
    }
    options.headers.set('x-access-token', localStorage.getItem('token'));
    return fetchUtils.fetchJson(url, options);
};

const restClient = simpleRestClient(API_URL, httpClient);

export default MongoDBClient(restClient);

// In src/App.js
import restClient from './myRestClient';

const App = () => (
    <Admin
        title="أرشيفارا"
        customRoutes={customRoutes}
        customReducers={{ theme: themeReducer }}
        menu={Menu}
        authClient={authClient}
        restClient={restClient}
        appLayout={Layout}
        messages={translations}
    >   
        <Resource name="post" list={PostList} edit={PostEdit} create={PostCreate} />

    </Admin>
);

export default App;
Gildas Garcia
  • 6,966
  • 3
  • 15
  • 29
  • Thank you very much @GildasGarcia, I'll try with this solution and I'll let you know, and i would be very grateful if you add an example about how to decorate the dataProvider (or RestClients) (simpleRestClient)[https://marmelab.com/admin-on-rest/RestClients.html#simple-rest]. – mariem Aug 05 '19 at 22:11
  • PS: I recently discovered the new shiny version :) – mariem Aug 05 '19 at 22:15
  • Do yourself a favor: if you just started your project, upgrade to it immediately, if not, upgrade to it asap – Gildas Garcia Aug 06 '19 at 07:31
  • hello @GildasGarcia, unfortuntly the changes that I have made in the file node-modules.admin-on-rest.src.rest.simple.js doesn't make a difference , is that the right module that I have to change ? I even try to change of the "showNotification" in the file node-modules.admin-on-rest.src.sideeffects.saga.crudResponse.js but nothing has changed, – mariem Aug 07 '19 at 09:33
  • this is my first project with reactJS and npm package, I am new in this environment ! – mariem Aug 07 '19 at 09:44
  • Updated! Let me know how it goes – Gildas Garcia Aug 08 '19 at 18:52
  • Hello, thank you very much @GildacGarcia, do you mean that you have updated the module admin-on-rest in node-modules ? I was just asking how I can do it myself if I'll face a similar problem in the future how can I fix it. Anyway, I have modified my API to resolve this issue by adding a new json object . – mariem Aug 09 '19 at 10:05
  • I have not myRestClient file in my project . in src/app.js I have :`import { simpleRestClient } from 'admin-on-rest' const restClient = simpleRestClient(API_URL, httpClient); const App = () => ( .....` – mariem Aug 09 '19 at 10:25
  • 1
    You'll have to add this file (`myRestClient.js`) yourself in your project. By the way, you should never modify the files in `node_modules` as they'll be overridden next time you run an npm/yarn install, and won't be available for anyone cloning the project (as this directory should not be committed on your source control) – Gildas Garcia Aug 09 '19 at 14:04
0

That's how I fixed this problem, like @GildasGarcia said the problem was in mongodb's id property set by default : _id .

So I created a new json object data that accepts the properties of the mongodb object

 //-------------------------------
    // find a postt-----------------
    //-------------------------------

    app.get('/post/:id', function (req, res) {

        Post.findById({_id: req.params.id}, function (err, post) {
            var data = {
                id: req.params.id,
                title: post.title,
                content:post.content
            };
            res.status(200).json(data);
        });
    });
mariem
  • 56
  • 1
  • 8