events api and computing events diff from client

This commit is contained in:
arpitnath 2023-08-16 02:01:53 +05:30
parent 49681f05bd
commit 5e4fa096ad
12 changed files with 218 additions and 53 deletions

View file

@ -687,20 +687,13 @@ const EditorComponent = (props) => {
await fetchDataQueries(data.editing_version?.id, true, true);
const appDefData = buildAppDefinition(data);
// let dataDefinition = data.definition ?? defaults(data.definition, defaultDefinition(props.darkMode));
// const pages = Object.entries(dataDefinition.pages).map(([pageId, page]) => ({ id: pageId, ...page }));
// const startingPageId = pages.filter((page) => page.handle === startingPageHandle)[0]?.id;
// const homePageId = !startingPageHandle || startingPageId === 'null' ? dataDefinition.homePageId : startingPageId;
// !------
const appJson = appDefData;
const pages = data.pages;
const events = data.events;
const startingPageId = pages.filter((page) => page.handle === startingPageHandle)[0]?.id;
const homePageId = !startingPageId || startingPageId === 'null' ? appJson.homePageId : startingPageId;
const currentComponents = appJson.pages[homePageId]?.components ?? {};
console.log('---arpit [fetching app] [pages] ==> ', { currentComponents });
const currentpageData = {
handle: appJson.pages[homePageId]?.handle,
name: appJson.pages[homePageId]?.name,
@ -708,8 +701,6 @@ const EditorComponent = (props) => {
variables: {},
};
// !------
setCurrentPageId(homePageId);
updateState({
@ -721,6 +712,7 @@ const EditorComponent = (props) => {
appName: data?.name,
userId: data?.user_id,
appId: data?.id,
events: events,
});
useCurrentStateStore.getState().actions.setCurrentState({
@ -751,6 +743,7 @@ const EditorComponent = (props) => {
// !--------
const setAppDefinitionFromVersion = (version, shouldWeEditVersion = true) => {
if (version?.id !== props.editingVersion?.id) {
// !Need to fix this
// appDefinitionChanged(defaults(version.definition, defaultDefinition(props.darkMode)), {
// skipAutoSave: true,
// skipYmapUpdate: true,
@ -992,7 +985,7 @@ const EditorComponent = (props) => {
const diffPatches = diff(appDefinition, updatedAppDefinition);
if (!isEmpty(diffPatches)) {
appDefinitionChanged(updatedAppDefinition, { skipAutoSave: true, componentDefinitionChanged: true });
appDefinitionChanged(updatedAppDefinition, { skipAutoSave: true, componentDefinitionChanged: true, ...props });
}
}
@ -1464,6 +1457,7 @@ const EditorComponent = (props) => {
appDefinitionChanged(newAppDefinition, {
pageDefinitionChanged: true,
pageEventsChanged: true,
});
};

View file

@ -217,7 +217,7 @@ export const EventManager = ({
alertType: 'info',
});
setEvents(newEvents);
eventsChanged(newEvents);
eventsChanged(newEvents, false, true);
}
//following two are functions responsible for on change and value for the control specific actions

View file

@ -207,7 +207,7 @@ export const Inspector = ({
componentDefinitionChanged(newComponent, { eventUpdated: true });
}
function eventsChanged(newEvents, isReordered = false) {
function eventsChanged(newEvents, isReordered = false, isNew = false) {
let newComponent = JSON.parse(JSON.stringify(component));
let newDefinition = JSON.parse(JSON.stringify(newComponent.component.definition));
@ -215,7 +215,14 @@ export const Inspector = ({
newComponent.component.definition = newDefinition;
componentDefinitionChanged(newComponent, { eventsChanged: true });
const opts = {
componentsEventsChanged: true,
};
if (isReordered) opts.eventsReOrdered = true;
if (isNew) opts.newEvent = true;
componentDefinitionChanged(newComponent, opts);
}
function eventOptionUpdated(event, option, value) {

View file

@ -74,6 +74,10 @@ function autoSaveApp(appId, versionId, diff, type, pageId, operation, isUserSwit
global_settings: {
update: { ...diff },
},
events: {
update: diff,
create: diff,
},
};
const body = !type

View file

@ -17,6 +17,7 @@ const initialState = {
components: [],
pages: [],
layouts: [],
events: [],
eventHandlers: [],
appDefinitionDiff: null,
appDiffOptions: {},

View file

@ -41,43 +41,73 @@ const updateType = Object.freeze({
componentAdded: 'components',
componentDefinitionChanged: 'components',
componentDeleted: 'components',
componentsEventsChanged: 'events',
pageEventsChanged: 'events',
});
const eventHandlerType = Object.freeze({
componentsEventsChanged: 'components',
pageEventsChanged: 'pages',
});
export const computeAppDiff = (appDiff, currentPageId, opts) => {
const { updateDiff, type, operation } = updateFor(appDiff, currentPageId, opts);
console.log('----arpit [updateFor]', { updateDiff, type, operation });
return { updateDiff, type, operation };
};
// const updateFor = (appDiff, currentPageId, opts) => {
// const componentUpdates = ['componentAdded', 'componentDefinitionChanged', 'componentDeleted', 'containerChanges'];
// const pageUpdates = ['pageDefinitionChanged', 'pageSortingChanged', 'deletePageRequest', 'addNewPage'];
// const appUpdates = ['homePageChanged'];
// const globalSettings = ['globalSettings'];
function verifyIsEventUpdates(data, eventsObj) {
if (!data.pages || Object.keys(data.pages).length === 0) {
return false;
}
// const options = _.keys(opts);
for (const pageId in data.pages) {
const components = data.pages[pageId].components;
for (const componentId in components) {
if (components[componentId].component.definition.events) {
eventsObj.components = Object.values(components[componentId].component.definition.events).map((e) => ({
event: e,
eventType: 'component',
attachedTo: componentId,
}));
// if (_.intersection(options, componentUpdates).length > 0) {
// return computeComponentDiff(appDiff, currentPageId, opts);
// } else if (_.intersection(options, pageUpdates).length > 0) {
// return computePageUpdate(appDiff, currentPageId, opts);
// } else if (_.intersection(options, appUpdates).length > 0) {
// return {
// updateDiff: appDiff,
// type: null,
// operation: 'update',
// };
// } else if (_.intersection(options, globalSettings).length > 0) {
// return {
// updateDiff: appDiff,
// type: 'global_settings',
// operation: 'update',
// };
// }
// };
return true;
}
}
}
return false;
}
function computeEventDiff(appDiff, currentPageId, opts = []) {
let type = 'events';
let updateDiff;
let operation = 'update';
const events = {
components: [],
pages: [],
};
verifyIsEventUpdates(appDiff, events);
updateDiff = events[eventHandlerType[opts[0]]];
console.log('----arpit [computeEventDiff]', { events, opts });
if (opts.includes('newEvent')) {
operation = 'create';
updateDiff = updateDiff[0];
}
return { updateDiff, type, operation };
}
const updateFor = (appDiff, currentPageId, opts) => {
const updateTypeMappings = [
{
updateTypes: ['componentsEventsChanged', 'pageEventsChanged', 'eventsReOrdered', 'newEvent'],
processingFunction: computeEventDiff,
},
{
updateTypes: ['componentAdded', 'componentDefinitionChanged', 'componentDeleted', 'containerChanges'],
processingFunction: computeComponentDiff,
@ -107,8 +137,10 @@ const updateFor = (appDiff, currentPageId, opts) => {
const options = _.keys(opts);
for (const { updateTypes, processingFunction } of updateTypeMappings) {
if (_.intersection(options, updateTypes).length > 0) {
return processingFunction(appDiff, currentPageId, opts);
const optionsTypes = _.intersection(options, updateTypes);
if (optionsTypes.length > 0) {
return processingFunction(appDiff, currentPageId, optionsTypes);
}
}
@ -122,7 +154,7 @@ const computePageUpdate = (appDiff, currentPageId, opts) => {
let updateDiff;
let operation = 'update';
if (opts?.deletePageRequest) {
if (opts.includes('deletePageRequest')) {
const deletePageId = _.keys(appDiff?.pages).map((pageId) => {
if (appDiff?.pages[pageId]?.pageId === undefined) {
return pageId;
@ -135,16 +167,16 @@ const computePageUpdate = (appDiff, currentPageId, opts) => {
type = updateType.pageDefinitionChanged;
operation = 'delete';
} else if (opts?.pageSortingChanged) {
} else if (opts.includes('pageSortingChanged')) {
updateDiff = appDiff?.pages;
type = updateType.pageDefinitionChanged;
} else if (opts?.pageDefinitionChanged) {
} else if (opts.includes('pageDefinitionChanged')) {
updateDiff = appDiff?.pages[currentPageId];
type = updateType.pageDefinitionChanged;
if (opts?.addNewPage) {
if (opts.includes('addNewPage')) {
operation = 'create';
}
}
@ -157,7 +189,7 @@ const computeComponentDiff = (appDiff, currentPageId, opts) => {
let updateDiff;
let operation = 'update';
if (opts?.componentDeleted) {
if (opts.includes('componentDeleted')) {
const currentPageComponents = appDiff?.pages[currentPageId]?.components;
updateDiff = _.keys(currentPageComponents);
@ -165,12 +197,15 @@ const computeComponentDiff = (appDiff, currentPageId, opts) => {
type = updateType.componentDeleted;
operation = 'delete';
} else if ((opts?.containerChanges || opts?.componentDefinitionChanged) && !opts?.componentAdded) {
} else if (
(opts.includes('containerChanges') || opts.includes('componentDefinitionChanged')) &&
!opts.includes('componentAdded')
) {
const currentPageComponents = appDiff?.pages[currentPageId]?.components;
updateDiff = currentPageComponents;
type = opts?.componentDefinitionChanged ? updateType.componentDefinitionChanged : updateType.containerChanges;
} else if (opts?.componentAdded) {
type = opts.includes('containerChanges') ? updateType.containerChanges : updateType.componentDefinitionChanged;
} else if (opts.includes('componentAdded')) {
const currentPageComponents = appDiff?.pages[currentPageId]?.components;
updateDiff = _.toPairs(currentPageComponents ?? []).reduce((result, [id, component]) => {

View file

@ -17,6 +17,11 @@ export class CreateEventHandlerTable1691004706564 implements MigrationInterface
type: 'varchar',
isNullable: false,
},
{
name: 'event',
type: 'jsonb',
isNullable: false,
},
{
name: 'app_version_id',
type: 'uuid',

View file

@ -29,6 +29,7 @@ import { ValidAppInterceptor } from 'src/interceptors/valid.app.interceptor';
import { AppDecorator } from 'src/decorators/app.decorator';
import { PageService } from '@services/page.service';
import { EventsService } from '@services/events_handler.service';
@Controller('apps')
export class AppsController {
@ -36,6 +37,7 @@ export class AppsController {
private appsService: AppsService,
private foldersService: FoldersService,
private pageService: PageService,
private eventsService: EventsService,
private appsAbilityFactory: AppsAbilityFactory
) {}
@ -89,6 +91,9 @@ export class AppsController {
: [];
const pagesForVersion = app.editingVersion ? await this.pageService.findPagesForVersion(app.editingVersion.id) : [];
const eventsForVersion = app.editingVersion
? await this.eventsService.findEventsForVersion(app.editingVersion.id)
: [];
// serialize queries
for (const query of dataQueriesForVersion) {
@ -100,6 +105,7 @@ export class AppsController {
response['data_queries'] = seralizedQueries;
response['definition'] = app.editingVersion?.definition;
response['pages'] = pagesForVersion;
response['events'] = eventsForVersion;
//! if editing version exists, camelize the definition
if (app.editingVersion && app.editingVersion.definition) {

View file

@ -28,6 +28,7 @@ import { AppDecorator } from 'src/decorators/app.decorator';
import { ComponentsService } from '@services/components.service';
import { PageService } from '@services/page.service';
import { EventsService } from '@services/events_handler.service';
import { AppVersionUpdateDto } from '@dto/app-version-update.dto';
@Controller({
@ -39,6 +40,7 @@ export class AppsControllerV2 {
private appsService: AppsService,
private componentsService: ComponentsService,
private pageService: PageService,
private eventService: EventsService,
private appsAbilityFactory: AppsAbilityFactory
) {}
@ -307,4 +309,42 @@ export class AppsControllerV2 {
await this.pageService.deletePage(pageId, versionId);
}
}
// events api
@UseGuards(JwtAuthGuard)
@UseInterceptors(ValidAppInterceptor)
@Post(':id/versions/:versionId/events')
async createEvent(@User() user, @Param('id') id, @Param('versionId') versionId, @Body() body) {
const version = await this.appsService.findVersion(versionId);
const app = version.app;
if (app.id !== id) {
throw new BadRequestException();
}
const ability = await this.appsAbilityFactory.appsActions(user, id);
if (!ability.can('updateVersions', app)) {
throw new ForbiddenException('You do not have permissions to perform this action');
}
return this.eventService.createEvent(body, versionId);
}
@UseGuards(JwtAuthGuard)
@UseInterceptors(ValidAppInterceptor)
@Put(':id/versions/:versionId/events')
async updateEvents(@User() user, @Param('id') id, @Param('versionId') versionId, @Body() body) {
const version = await this.appsService.findVersion(versionId);
const app = version.app;
if (app.id !== id) {
throw new BadRequestException();
}
const ability = await this.appsAbilityFactory.appsActions(user, id);
if (!ability.can('updateVersions', app)) {
throw new ForbiddenException('You do not have permissions to perform this action');
}
return await this.eventService.updateEvent(body, versionId);
}
}

View file

@ -1,4 +1,4 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { AppVersion } from './app_version.entity';
enum Target {
@ -15,8 +15,8 @@ export class EventHandler {
@Column({ name: 'name' })
name: string;
@Column({ name: 'app_version_id' })
AppVersionId: string;
@Column('simple-json')
event: any;
@Column({ name: 'source_id' })
sourceId: string;
@ -24,6 +24,10 @@ export class EventHandler {
@Column({ name: 'target' })
target: Target;
@ManyToOne(() => AppVersion, (appVersion) => appVersion.eventHandlers)
@Column({ name: 'app_version_id' })
appVersionId: string;
@ManyToOne(() => AppVersion, (appVersion) => appVersion.pages)
@JoinColumn({ name: 'app_version_id' })
appVersion: AppVersion;
}

View file

@ -41,6 +41,7 @@ import { Layout } from 'src/entities/layout.entity';
import { ComponentsService } from '@services/components.service';
import { PageService } from '@services/page.service';
import { EventsService } from '@services/events_handler.service';
@Module({
imports: [
@ -83,6 +84,7 @@ import { PageService } from '@services/page.service';
AppEnvironmentService,
ComponentsService,
PageService,
EventsService,
],
controllers: [AppsController, AppsControllerV2, AppUsersController, AppsImportExportController],
})

View file

@ -0,0 +1,67 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityManager, Repository } from 'typeorm';
import { Component } from 'src/entities/component.entity';
// import { Page } from 'src/entities/page.entity';
import { EventHandler } from 'src/entities/event_handler.entity';
import { dbTransactionWrap } from 'src/helpers/utils.helper';
@Injectable()
export class EventsService {
constructor(
@InjectRepository(Component)
private eventsRepository: Repository<EventHandler>
) {}
async findEventsForVersion(appVersionId: string): Promise<EventHandler[]> {
return dbTransactionWrap(async (manager: EntityManager) => {
const allEvents = await manager.find(EventHandler, {
where: { appVersionId },
});
return allEvents;
});
}
async createEvent(options, versionId) {
if (Object.keys(options).length === 0) {
return new BadRequestException('No event found');
}
const newEvent = {
name: options.event.eventId,
sourceId: options.attachedTo,
target: options.eventType,
event: options.event,
appVersionId: versionId,
};
console.log('---arpit || create events', { newEvent });
return await dbTransactionWrap(async (manager: EntityManager) => {
const event = await manager.save(EventHandler, newEvent);
return event;
});
}
async updateEvent(options = [], versionId: string) {
const eventHandlers = [];
options.forEach((option) => {
eventHandlers.push({
event: option.event,
name: option.event.eventId,
sourceId: option.attachedTo,
target: option.eventType,
});
});
console.log('---arpit || createOrUpdateEvent', { eventHandlers });
return {
status: 'success',
data: eventHandlers,
};
}
// utitlity functions
}