The main purpose of Olobase Admin is to manage remote resources from a specific API. When it needs to communicate with your backend API for any standardized CRUD operation, it calls the adapted method in your provider that will be responsible for fetching or updating the resource data.
- packages
- admin
- providers
- data
jsonServer.js
// Fetching books
let { data, total } = await provider.getList("books", { page: 1, perPage: 10 });
console.log(data);
// Fetching one book
let { data } = await provider.getOne("books", { id: 1 });
console.log(book)
// Create new book
await provider.create("books", { data: { title: "My title" } })
// Update title book
await provider.update("books", { id: book.id, data: { title: "New title" } });
// Delete book
await provider.delete("books", { id: book.id });
All fetch methods of a data provider are standardized to ensure compatibility of Olobase Admin with any API server. This is the adapter model that allows for all kinds of different providers for each type of backend of any exchange protocol, whether it's REST, GraphQL, or even SOAP.
To give Olobase Admin the ability to retrieve remote source data, a specific data provider needs to be injected into the constructor method as explained in the next section. This data provider defaults to JsonServerProvider.
As always with any adapter model approach, all data providers must comply with a specific contract to allow communication with Olobase Admin. The next object represents the minimum contract that must be implemented:
const dataProvider = {
getList: (resource, params) => Promise,
getOne: (resource, params) => Promise,
getMany: (resource, params) => Promise,
create: (resource, params) => Promise,
update: (resource, params) => Promise,
updateMany: (resource, params) => Promise,
delete: (resource, params) => Promise,
deleteMany: (resource, params) => Promise,
copy: (resource, params) => Promise,
copyMany: (resource, params) => Promise,
}
getList | Data Iterator component for displaying a list of resources within a data table or any custom list layout components. It also supports searching, filtering, sorting, and optional relationship fetching. |
getOne | It is used to show details of the resource, especially the Show action or the data table show actions. |
getMany | It is used only for the AutoCompleter component to fetch all available selections by IDs on initial load, whether in editing page content or query context filtering. |
create | Used by VaForm to create new resources. |
update | Used by VaForm to update the existing resource. |
delete | Simple delete action called when interacting with Delete Button. |
Data providers are very simple to use. They can take a simple API URL or a custom HTTP client of your choice as the first constructor argument. If you need to share some headers throughout the admin application, use the full object; It's especially useful for authentication-related topics.
src/plugins/admin.js
import {
jsonServerDataProvider,
jwtAuthProvider,
} from "olobase-admin/src/providers";
import { en, tr } from "olobase-admin/src/locales";
import config from "@/_config";
let admin = new OlobaseAdmin(import.meta.env);
/**
* Install admin plugin
*/
export default {
install: (app, http, resources) => {
// console.error(app.config.globalProperties)
admin.setOptions({
app,
router,
resources,
store,
i18n,
dashboard: "dashboard",
downloadUrl: "/files/findOneById/",
readFileUrl: "/files/readOneById/",
title: "Demo",
routes,
locales: { en, tr },
dataProvider: jsonServerDataProvider(http),
authProvider: jwtAuthProvider(http),
http,
canAction: null,
// canAction: ({ resource, action, can }) => {
// if (can(["admin"])) {
// return true;
// }
// // any other custom actions on given resource and action...
// },
options: config,
});
admin.init();
OlobaseAdmin.install(app); // install layouts & components
app.provide("i18n", i18n);
app.provide("router", router);
app.provide("admin", admin); // make injectable
app.component("PageNotFound", PageNotFound);
},
};
Writing your own data provider is pretty simple. Note the method call signatures by copying the JsonServerProvider. As mentioned before each provider method takes 2 arguments:
The following table lists which parameters can be sent in the second parameter for each provider method.
Method | Description | Parameter Format | Response |
---|---|---|---|
getList | Lists data | { pagination: { page: Number , perPage: Number }, sort: [{ by: String, desc: Boolean }], filter: Object }, include: String[], fields: { [resource]: String[] } } | { data: Resource[], total: Number } |
getOne | Returns a resource (data) by id | { id: Any } | { data: Resource } |
getMany | Returns multiple data based on id | { ids: Array, include: String[], fields: { [resource]: String[] } } | { data: Resource[] } |
create | Creates new data | { data: Object } | { data: Resource } |
update | Updates a default resource (data) | { id: Any, data: Object } | { data: Resource } |
updateMany | Updates multiple data | { ids: Array, data: Object } | empty |
delete | Deletes current data | { id: Any } | empty |
deleteMany | Deletes multiple data | { ids: Array } | empty |
copy | Copies the selected data | { id: Any } | { data: Resource } |
copyMany | Copies multiple selected data | { ids: Array } | { data: Resource } |
Here are some valid examples of calls to Olobase Admin in each resource repository module:
dataProvider.getList("books", {
pagination: { page: 1, perPage: 15 },
sort: [{ by: "publication_date", desc: true }, { by: "title", desc: false }],
filter: { author: "Cassandra" },
include: ["media", "reviews"],
fields: { books: ["isbn", "title"], reviews: ["status", "author"] }
});
dataProvider.getOne("books", { id: 1 });
dataProvider.getMany("books", { ids: [1, 2, 3] });
dataProvider.create("books", { data: { title: "Lorem ipsum" } });
dataProvider.update("books", { id: 1, data: { title: "New title" } });
dataProvider.updateMany("books", { ids: [1, 2, 3], data: { commentable: true } });
dataProvider.delete("books", { id: 1 });
dataProvider.deleteMany("books", { ids: [1, 2, 3] });
In case of any server side errors, i.e. when the response status is outside the 2xx range, you simply return a rejection promise with a specific Object containing at least a descriptive error message as well as the HTTP status code. This status is passed to the authentication provider to allow you to perform a specific authentication action based on a specific status code.
In case of multiple errors, we return to error, and in case of an empty response or server error from the server, we return to the general error error.
try {
let response = await this.admin.http('post', url, data);
} catch (e) {
if (
&& e.response
&& e.response.status === 400
&& e.response["data"]
&& e.response["data"]["data"]
&& e.response["data"]["data"]["error"]
) {
this.admin.message("error", e.response.data.data.error);
}
}
Expected error object format:
Key | Type | Description |
---|---|---|
error | string | Error message to be displayed in Snackbar |
status | number | Response status code sent by the server |
errors | array | Error objects sent by the server for client-side validation support |
You can use all data provider methods for each resource in your custom CRUD pages or any authenticated custom page directly from the Vuex store. You have 2 different methods, one with the mapActions Vuex helper and the other with the global $store instance where you can use dispatch.
The following code shows both ways of getting data from your providers under one example:
<template>
<v-row>
<v-col v-for="item in data" :key="item.id">
{{ item.name }}
</v-col>
</v-row>
</template>
<script>
import { mapActions } from "vuex";
export default {
data() {
return {
data: [],
}
},
async mounted() {
/**
* Use the global vuex store instance.
* You need to provide the name of the resource followed by the provider method you want to call.
* Each provider methods needs a `params` argument which is the same object described above.
*/
this.data = await this.$store.dispatch("publishers/getList", {
pagination: {
page: 1,
perPage: 5,
},
});
/**
* Use the registered global method which use global `api` store module.
* Then you need to provide a object argument of this format : `{ resource, params }`
*/
this.data = await this.getList({
resource: "publishers",
params: {
pagination: {
page: 1,
perPage: 5,
},
},
});
},
methods: {
...mapActions({
getList: "api/getList",
}),
},
};
</script>