Добавление пользовательской службы в веб-интерфейс акселератора решения для удаленного мониторинга
В этой статье показано, как добавить новую службу в веб-интерфейс акселератора решения для удаленного мониторинга. Содержание статьи:
- Подготовка локальной среды разработки.
- Добавление новой службы в веб-интерфейс.
Пример службы в этой статье предоставляет данные для сетки, добавление которой описано в статье Add a custom grid to the Remote Monitoring solution accelerator web UI (Добавление пользовательской сетки в веб-интерфейс акселератора решения для удаленного мониторинга).
В приложении реагирования служба обычно взаимодействует с внутренней службой. Примеры в акселераторе решения для удаленного мониторинга включают службы, взаимодействующие с диспетчером Центра Интернета вещей и микрослужбами конфигурации.
Предварительные требования
Чтобы выполнить действия, описанные в этом руководстве, необходимо установить следующее ПО на локальный компьютер разработки:
Перед началом работы
Прежде чем продолжить, следует выполнить действия, описанные в статье Добавление пользовательской страницы в веб-интерфейс акселератора решения для удаленного мониторинга.
Добавление службы
Чтобы добавить службу в веб-интерфейс, необходимо добавить исходные файлы, которые определяют эту службу. Необходимо также изменить некоторые имеющиеся файлы, чтобы веб-интерфейс учитывал новую службу.
Добавление новых файлов, которые определяют службу
Чтобы приступить к работе, перейдите к папке src/walkthrough/services. В ней содержатся четыре файла, которые определяют простую службу:
exampleService.js
import { Observable } from 'rxjs';
import { toExampleItemModel, toExampleItemsModel } from './models';
/** Normally, you'll need to define the endpoint URL.
* See app.config.js to add a new service URL.
*
* For this example, we'll just hardcode sample data to be returned instead
* of making an actual service call. See the other service files for examples.
*/
//const ENDPOINT = Config.serviceUrls.example;
/** Contains methods for calling the example service */
export class ExampleService {
/** Returns an example item */
static getExampleItem(id) {
return Observable.of(
{ ID: id, Description: "This is an example item." },
)
.map(toExampleItemModel);
}
/** Returns a list of example items */
static getExampleItems() {
return Observable.of(
{
items: [
{ ID: "123", Description: "This is item 123." },
{ ID: "188", Description: "This is item ONE-DOUBLE-EIGHT." },
{ ID: "210", Description: "This is item TWO-TEN." },
{ ID: "277", Description: "This is item 277." },
{ ID: "413", Description: "This is item FOUR-THIRTEEN." },
{ ID: "789", Description: "This is item 789." },
]
}
).map(toExampleItemsModel);
}
/** Mimics a server call by adding a delay */
static updateExampleItems() {
return this.getExampleItems().delay(2000);
}
}
Дополнительные сведения о реализации службы см. в статье о реактивном программировании.
model/exampleModels.js
import { camelCaseReshape, getItems } from 'utilities';
/**
* Reshape the server side model to match what the UI wants.
*
* Left side is the name on the client side.
* Right side is the name as it comes from the server (dot notation is supported).
*/
export const toExampleItemModel = (data = {}) => camelCaseReshape(data, {
'id': 'id',
'description': 'descr'
});
export const toExampleItemsModel = (response = {}) => getItems(response)
.map(toExampleItemModel);
Скопируйте файл exampleService.js в папку src/services и файл exampleModels.js в папку src/services/models.
Обновите файл index.js в папке src/services для экспорта новой службы:
export * from './exampleService';
Обновите файл index.js в папке src/services/models для экспорта новой модели:
export * from './exampleModels';
Настройка вызовов к службе из хранилища
Чтобы начать работу, просмотрите папку src/walkthrough/store/reducers, в которой содержится пример модуля сжатия:
exampleReducer.js
import 'rxjs';
import { Observable } from 'rxjs';
import moment from 'moment';
import { schema, normalize } from 'normalizr';
import update from 'immutability-helper';
import { createSelector } from 'reselect';
import { ExampleService } from 'walkthrough/services';
import {
createReducerScenario,
createEpicScenario,
errorPendingInitialState,
pendingReducer,
errorReducer,
setPending,
toActionCreator,
getPending,
getError
} from 'store/utilities';
// ========================= Epics - START
const handleError = fromAction => error =>
Observable.of(redux.actions.registerError(fromAction.type, { error, fromAction }));
export const epics = createEpicScenario({
/** Loads the example items */
fetchExamples: {
type: 'EXAMPLES_FETCH',
epic: fromAction =>
ExampleService.getExampleItems()
.map(toActionCreator(redux.actions.updateExamples, fromAction))
.catch(handleError(fromAction))
}
});
// ========================= Epics - END
// ========================= Schemas - START
const itemSchema = new schema.Entity('examples');
const itemListSchema = new schema.Array(itemSchema);
// ========================= Schemas - END
// ========================= Reducers - START
const initialState = { ...errorPendingInitialState, entities: {}, items: [], lastUpdated: '' };
const updateExamplesReducer = (state, { payload, fromAction }) => {
const { entities: { examples }, result } = normalize(payload, itemListSchema);
return update(state, {
entities: { $set: examples },
items: { $set: result },
lastUpdated: { $set: moment() },
...setPending(fromAction.type, false)
});
};
/* Action types that cause a pending flag */
const fetchableTypes = [
epics.actionTypes.fetchExamples
];
export const redux = createReducerScenario({
updateExamples: { type: 'EXAMPLES_UPDATE', reducer: updateExamplesReducer },
registerError: { type: 'EXAMPLE_REDUCER_ERROR', reducer: errorReducer },
isFetching: { multiType: fetchableTypes, reducer: pendingReducer }
});
export const reducer = { examples: redux.getReducer(initialState) };
// ========================= Reducers - END
// ========================= Selectors - START
export const getExamplesReducer = state => state.examples;
export const getEntities = state => getExamplesReducer(state).entities || {};
export const getItems = state => getExamplesReducer(state).items || [];
export const getExamplesLastUpdated = state => getExamplesReducer(state).lastUpdated;
export const getExamplesError = state =>
getError(getExamplesReducer(state), epics.actionTypes.fetchExamples);
export const getExamplesPendingStatus = state =>
getPending(getExamplesReducer(state), epics.actionTypes.fetchExamples);
export const getExamples = createSelector(
getEntities, getItems,
(entities, items) => items.map(id => entities[id])
);
// ========================= Selectors - END
Скопируйте exampleReducer.js в папку src/store/reducers.
Дополнительные сведения о модуле сжатия и Epics см. на странице redux-observable.
Настройка ПО промежуточного слоя
Чтобы настроить ПО промежуточного слоя, добавьте модуль сжатия в файл rootReducer.js в папке src/store:
import { reducer as exampleReducer } from './reducers/exampleReducer';
const rootReducer = combineReducers({
...appReducer,
...devicesReducer,
...rulesReducer,
...simulationReducer,
...exampleReducer
});
Добавьте epics в файл rootEpics.js в папке src/store:
import { epics as exampleEpics } from './reducers/exampleReducer';
// Extract the epic function from each property object
const epics = [
...appEpics.getEpics(),
...devicesEpics.getEpics(),
...rulesEpics.getEpics(),
...simulationEpics.getEpics(),
...exampleEpics.getEpics()
];
Дополнительная информация
Из статьи вы узнали о ресурсах, которые могут помочь вам при настройке или добавлении служб в пользовательском веб-интерфейсе в акселераторе решения для удаленного мониторинга.
Вы определили службу. Теперь необходимо добавить пользовательскую сетку в веб-интерфейс акселератора решения для удаленного мониторинга, который отображает данные, возвращаемые службой.
Дополнительные концептуальные сведения об акселераторе решения для удаленного мониторинга см. в статье об архитектуре удаленного мониторинга.