How is the correct way to have multiple dataProviders in react-admin?

前端 未结 3 1754
忘掉有多难
忘掉有多难 2021-01-02 17:25

I\'m trying to use multiple dataproviders in a react-admin project but I have an error:

Warning: Missing translation for key: \"dataProvider is not a functio         


        
相关标签:
3条回答
  • 2021-01-02 17:56

    If you're wondering how to have multiple data sources as well as multiple dataProvider types i.e. rest, GraphQL e.t.c. The v3 docs state:

    In react-admin v2, Data Providers used to be functions, not objects. React-admin v3 can detect a legacy Data Provider and wrap an object around it. So Data Providers developed for react-admin v2 still work with react-admin v3.

    This means that the dataProvider property on the <Admin> component can take:

    • An object of type DataProvider
    • A function that provides (type, resource, params) returning a promise containing the data.

    This means that if you provide your DataProvider is a function, you will have to create an adapter and call the functions on your provider manually. Here is an example.

    ./dataProviders.js

    import { gqlDataProvider, restGQLDataProvider, restProvider } from './buildDataProviders';
    
    // The first assumption is that all resources are unique, thus we can use an object or a map instead of an array. Key = resource, value = DataProvider.
    const dataProviders = new Map([
      ['users', restGQLDataProvider],
      ['users-local', restProvider],
      ['Invoice', gqlDataProvider],
      ['Debtor',  gqlDataProvider],
      ['Status',  gqlDataProvider]
    ]);
    
    export default async (type, resource, params) => {
      // Get the DataProvider from the map.
      const dataProvider = await dataProviders.get(resource);
    
      // The DataProvider object is wrapped in a function so we can pass in the parameters to get the result.
      if (dataProvider instanceof Function) {
        return dataProvider(type, resource, params);
      }
    };
    

    ./buildDataProvider.js

    import { gql } from 'apollo-boost';
    import buildGraphQLProvider from 'ra-data-graphql-simple';
    import { gqlClient, restClient } from './apolloClients';
    import simpleRestProvider from 'ra-data-simple-rest';
    
    const LOCAL_REST_ENDPOINT = 'http://localhost:3001/api';
    
    export RestProvider = simpleRestProvider(LOCAL_REST_ENDPOINT);
    
    export const gqlDataProvider = buildGraphQLProvider({
      client: gqlClient
    });
    
    // This is just an example of the getList provider, you'll have to write the rest.
    export const restProvider = (type, resource, params) => {
      const providerMap = new Map([
        ['GET_LIST', async () => { 
          return await RestProvider.getList(resource, params);
        }]
       ])
    
    export const restGQLDataProvider = (type, resource, params) => {
      const providerMap = new Map([
        ['GET_LIST', async () => {
          const query = gql`
            query ${resource} {
              ${resource} @rest(type: "${resource}", path: "/${resource}") {
                  id
                  name
                  username
                  email
                  address
                  phone
                  website
                  company
                }
              }
            `;
          
          const result = await restClient.query({ query });
              
          return { data: result.data[resource], total: result.data[resource].length };
        }]
      ])
    }
    

    ./apolloClients.js

    import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost';
    import { RestLink } from 'apollo-link-rest';
    import auth from '../../auth';
    
    const GRAPHQL_ENDPOINT = 'http://localhost:4000';
    const REST_ENDPOINT = 'http://jsonplaceholder.typicode.com';
    const httpLink = new HttpLink({ uri: GRAPHQL_ENDPOINT });
    const authLink = new ApolloLink((operation, forward) => {
      operation.setContext({
        headers: {
          'x-api-key': auth.aws_appsync_apiKey
        }
      });
    
      return forward(operation);
    });
    
    export const restClient = new ApolloClient({
      link: new RestLink({ uri: REST_ENDPOINT }),
      cache: new InMemoryCache()
    });
    
    export const gqlClient = new ApolloClient({
      link: authLink.concat(httpLink),
      cache: new InMemoryCache()
    });
    
    0 讨论(0)
  • 2021-01-02 18:03

    You're not calling the dataProvider you found, you're calling the mapping object from your array. You can fix it like this:

    import simpleRestProvider from 'ra-data-simple-rest';
    
    const dataProviders = [
      { dataProvider: simpleRestProvider('http://path.to.foo.api1'), resources: ['users1'] },
      { dataProvider: simpleRestProvider('http://path.to.foo.api2'), resources: ['users2'] },
    ];
    
    export default (type, resource, params) => {
      const dataProviderMapping = dataProviders.find(dp => dp.resources.includes(resource));
      return dataProviderMapping.dataProvider(type, resource, params);
    };
    
    0 讨论(0)
  • 2021-01-02 18:14

    I was faced with the same problem in the new version 3.1

    I decided to make a universal dataProvider

    dataProvider.js

    import {fetchUtils} from 'ra-core';
    import ApiEndpoints from '../config/ApiEndpoints';
    import {
    	restGetList,
    	restGetOne,
    	restGetMany,
    	restGetManyReference,
    	restUpdate,
    	restUpdateMany,
    	restCreate,
    	restDelete,
    	restDeleteMany,
    } from './rest/index';
    import {CONSTANTS} from '../constants';
    
    const httpClientTui = (url, options = {}) => {
    	if (!options.headers) {
    		// eslint-disable-next-line no-param-reassign
    		options.headers = new Headers({Accept: 'application/json'});
    	}
    	const token = localStorage.getItem(CONSTANTS.CMS_KEY.TOKEN);
    	options.headers.set('Authorization', `Bearer ${token}`);
    	return fetchUtils.fetchJson(url, options);
    };
    
    function selectApiEndpoint(resource = '') {
    	const result = {};
    	result.apiEndpoint = 'https://jsonplaceholder.typicode.com';	// default parameters
    	result.apiType = 'rest';
    	Object.keys(ApiEndpoints).forEach((key) => {
    		if (ApiEndpoints[key].url.includes(resource)) {
    			result.apiEndpoint = key.toString();
    			result.apiType = ApiEndpoints[key].type.toString();
    		}
    	});
    	return {
    		...result,
    	};
    }
    
    export default (apiUrl, httpClient = httpClientTui) => ({
    	getList: (resource, params) => {
    		const {apiEndpoint, apiType} = selectApiEndpoint(resource);
    		switch (apiType) {
    			case 'rest':
    				return restGetList(apiEndpoint, resource, params, httpClient);
    			default:
    				return restGetList(apiEndpoint, resource, params, httpClient);
    		}
    	},
    
    	getOne: (resource, params) => {
    		const {apiEndpoint, apiType} = selectApiEndpoint(resource);
    		switch (apiType) {
    			case 'rest':
    				return restGetOne(apiEndpoint, resource, params, httpClient);
    			default:
    				return restGetOne(apiEndpoint, resource, params, httpClient);
    		}
    	},
    
    	...
    });

    ApiEndpoints.js

    module.exports = {
    	'https://jsonplaceholder.typicode.com': {
    		type: 'rest',
    		url: [
    			'posts',
    			'comments',
    			'albums',
    			'photos',
    			'todos',
    			'users',
    		],
    	},
    };

    ./rest/index.js

    import {fetchUtils} from 'ra-core';
    import {stringify} from 'query-string';
    
    /**
     * From ra-data-json-server
     * https://github.com/marmelab/react-admin/blob/master/packages/ra-data-json-server/src/index.ts
     */
    
    /**
     * Get list
     * @param apiEndpoint {string} - domain
     * @param resource {string} - URI request
     * @param params {object} - params request
     * @param httpClient {function}
     * @returns {{data: object, total: number}}
     */
    export function restGetList(apiEndpoint, resource, params, httpClient) {
    	const {page, perPage} = params.pagination;
    	const {field, order} = params.sort;
    	const query = {
    		...fetchUtils.flattenObject(params.filter),
    		_sort: field,
    		_order: order,
    		_start: (page - 1) * perPage,
    		_end: page * perPage,
    	};
    	const url = `${apiEndpoint}/${resource}?${stringify(query)}`;
    
    	return httpClient(url).then(({headers, json}) => {
    		if (!headers.has('x-total-count')) {
    			throw new Error(
    				'not found x-total-count',
    			);
    		}
    		return {
    			data: json,
    			total: parseInt(
    				headers.get('x-total-count').split('/').pop()
    			),
    		};
    	});
    }
    
    /**
     * Get one record
     * @param apiEndpoint {string} - domain
     * @param resource {string} - URI request
     * @param params {object} - params request
     * @param httpClient {function}
     * @returns {{data: object}}
     */
    export function restGetOne(apiEndpoint, resource, params, httpClient) {
    	return httpClient(`${apiEndpoint}/${resource}/${params.id}`).then(({json}) => ({
    		data: json,
    	}));
    }
    
    ...

    0 讨论(0)
提交回复
热议问题