fix(analytics.gblib): Fixes in database storage.
This commit is contained in:
parent
5d6dacc910
commit
22f4250831
12 changed files with 177 additions and 128 deletions
11
.vscode/tasks.json
vendored
11
.vscode/tasks.json
vendored
|
@ -14,6 +14,17 @@
|
|||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "typescript",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
8
boot.js
8
boot.js
|
@ -6,15 +6,9 @@ const { exec } = require('child_process');
|
|||
|
||||
// Displays version of Node JS being used at runtime and others attributes.
|
||||
|
||||
console.log(`[GB Runtime] version = ${process.version}`);
|
||||
console.log(`[GB Runtime] env = ${process.env}`);
|
||||
console.log(`[GB Runtime] NodeJS = ${process.version}`);
|
||||
console.log(`[GB Runtime] platform = ${process.platform}`);
|
||||
console.log(`[GB Runtime] release = ${process.release}`);
|
||||
console.log(`[GB Runtime] argv = ${process.argv}`);
|
||||
console.log(`[GB Runtime] env.USER = ${process.env.USER}`);
|
||||
console.log(`[GB Runtime] env.PATH = ${process.env.PATH.split(':').join('\n')}`);
|
||||
console.log(`[GB Runtime] env.PWD = ${process.env.PWD}`);
|
||||
console.log(`[GB Runtime] env.HOME = ${process.env.HOME}`);
|
||||
console.log(`[GB Runtime] debugPort = ${process.debugPort}`);
|
||||
|
||||
var now = () => {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
import { GuaribasConversation, GuaribasConversationMessage } from './models';
|
||||
|
||||
/**
|
||||
* .gblib Package handler.
|
||||
|
@ -49,6 +50,8 @@ export class GBAnalyticsPackage implements IGBPackage {
|
|||
}
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
GBLog.verbose(`loadPackage called.`);
|
||||
core.sequelize.addModels([GuaribasConversation, GuaribasConversationMessage]);
|
||||
|
||||
}
|
||||
public unloadPackage(core: IGBCoreService): void {
|
||||
GBLog.verbose(`unloadPackage called.`);
|
||||
|
|
|
@ -58,6 +58,50 @@ import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBMod
|
|||
import { GuaribasSubject } from '../../kb.gbapp/models';
|
||||
import { GuaribasUser } from '../../security.gblib/models';
|
||||
|
||||
|
||||
/**
|
||||
* A conversation that groups many messages.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Column
|
||||
public conversationId: number;
|
||||
|
||||
@ForeignKey(() => GuaribasSubject)
|
||||
@Column
|
||||
public startSubjectId: number;
|
||||
|
||||
@BelongsTo(() => GuaribasSubject)
|
||||
public startSubject: GuaribasSubject;
|
||||
|
||||
@ForeignKey(() => GuaribasChannel)
|
||||
@Column
|
||||
public channelId: string;
|
||||
|
||||
@Column public rateDate: Date;
|
||||
|
||||
@Column(DataType.FLOAT)
|
||||
@Column
|
||||
public rate: number;
|
||||
|
||||
@Column
|
||||
@CreatedAt
|
||||
public createdAt: Date;
|
||||
|
||||
@Column public text: string;
|
||||
|
||||
@ForeignKey(() => GuaribasUser)
|
||||
@Column
|
||||
public startedByUserId: number;
|
||||
|
||||
@BelongsTo(() => GuaribasUser)
|
||||
public startedBy: GuaribasUser;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A single message in a conversation.
|
||||
*/
|
||||
|
@ -104,48 +148,3 @@ export class GuaribasConversationMessage extends Model<GuaribasConversationMessa
|
|||
@BelongsTo(() => GuaribasUser)
|
||||
public user: GuaribasUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* A conversation that groups many messages.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Column
|
||||
public conversationId: number;
|
||||
|
||||
@ForeignKey(() => GuaribasSubject)
|
||||
@Column
|
||||
public startSubjectId: number;
|
||||
|
||||
@BelongsTo(() => GuaribasSubject)
|
||||
public startSubject: GuaribasSubject;
|
||||
|
||||
@ForeignKey(() => GuaribasChannel)
|
||||
@Column
|
||||
public channelId: string;
|
||||
|
||||
@Column public rateDate: Date;
|
||||
|
||||
@Column(DataType.FLOAT)
|
||||
@Column
|
||||
public rate: number;
|
||||
|
||||
@Column
|
||||
@CreatedAt
|
||||
public createdAt: Date;
|
||||
|
||||
@Column public text: string;
|
||||
|
||||
@HasMany(() => GuaribasConversationMessage)
|
||||
public conversationMessage: GuaribasConversationMessage[];
|
||||
|
||||
@ForeignKey(() => GuaribasUser)
|
||||
@Column
|
||||
public startedByUserId: number;
|
||||
|
||||
@BelongsTo(() => GuaribasUser)
|
||||
public startedBy: GuaribasUser;
|
||||
}
|
||||
|
|
|
@ -52,15 +52,34 @@ export class AnalyticsService {
|
|||
return await conversation.save();
|
||||
}
|
||||
|
||||
public async updateConversationRate(
|
||||
instanceId: number,
|
||||
conversationId: number,
|
||||
rate: number
|
||||
): Promise<GuaribasConversation> {
|
||||
const options = { where: {} };
|
||||
// TODO: Filter by instanceId: instanceId
|
||||
options.where = { conversationId: conversationId };
|
||||
const item = await GuaribasConversation.findOne(options);
|
||||
item.rate = rate;
|
||||
item.rateDate = new Date();
|
||||
return item.save();
|
||||
}
|
||||
|
||||
|
||||
public async createMessage(
|
||||
instanceId: number,
|
||||
conversation: GuaribasConversation,
|
||||
user: GuaribasUser,
|
||||
content: string
|
||||
): Promise<GuaribasConversationMessage> {
|
||||
|
||||
const message = GuaribasConversationMessage.build();
|
||||
message.conversation = conversation;
|
||||
message.user = user;
|
||||
message.content = content;
|
||||
message.instanceId = instanceId;
|
||||
message.userId = user.userId;
|
||||
message.conversationId = conversation.conversationId;
|
||||
|
||||
return await message.save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -382,11 +382,11 @@ STORAGE_SYNC=true
|
|||
const sysPackages: IGBPackage[] = [];
|
||||
[
|
||||
GBAdminPackage,
|
||||
GBAnalyticsPackage,
|
||||
GBCorePackage,
|
||||
GBSecurityPackage,
|
||||
GBKBPackage,
|
||||
GBCustomerSatisfactionPackage,
|
||||
GBAnalyticsPackage,
|
||||
GBWhatsappPackage,
|
||||
GBAzureDeployerPackage,
|
||||
GBSharePointPackage,
|
||||
|
|
|
@ -58,19 +58,12 @@ import {
|
|||
|
||||
import { MicrosoftAppCredentials } from 'botframework-connector';
|
||||
import { GBServer } from '../../../src/app';
|
||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||
import { GBCorePackage } from '../../core.gbapp';
|
||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||
import { GBKBPackage } from '../../kb.gbapp';
|
||||
import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog';
|
||||
import { GBSecurityPackage } from '../../security.gblib';
|
||||
import { GBWhatsappPackage } from '../../whatsapp.gblib';
|
||||
import { Messages } from '../strings';
|
||||
import { GBAdminPackage } from './../../admin.gbapp/index';
|
||||
import { GBConfigService } from './GBConfigService';
|
||||
import { GBDeployer } from './GBDeployer';
|
||||
import { SecService } from '../../security.gblib/services/SecService';
|
||||
import { isBreakOrContinueStatement } from 'typescript';
|
||||
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService';
|
||||
|
||||
/**
|
||||
* Minimal service layer for a bot.
|
||||
|
@ -124,7 +117,7 @@ export class GBMinService {
|
|||
if (process.env.DISABLE_WEB !== 'true') {
|
||||
GBServer.globals.server.get('/instances/:botId', (req, res) => {
|
||||
(async () => {
|
||||
await this.handleGetInstanceFroClient(req, res);
|
||||
await this.handleGetInstanceForClient(req, res);
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
@ -189,7 +182,7 @@ export class GBMinService {
|
|||
public async mountBot(instance: IGBInstance) {
|
||||
|
||||
// Build bot adapter.
|
||||
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.publicAddress, GBServer.globals.sysPackages);
|
||||
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.sysPackages);
|
||||
|
||||
if (GBServer.globals.minInstances.length === 0) {
|
||||
GBServer.globals.minBoot = min;
|
||||
|
@ -201,7 +194,7 @@ export class GBMinService {
|
|||
// this.deployer.deployPackage(min, 'packages/default.gbdialog');
|
||||
|
||||
// Call the loadBot context.activity for all packages.
|
||||
this.invokeLoadBot(GBServer.globals.appPackages, GBServer.globals.sysPackages, min, GBServer.globals.server);
|
||||
this.invokeLoadBot(GBServer.globals.appPackages, GBServer.globals.sysPackages, min);
|
||||
|
||||
// Serves individual URL for each bot conversational interface...
|
||||
const url = `/api/messages/${instance.botId}`;
|
||||
|
@ -280,7 +273,7 @@ export class GBMinService {
|
|||
/**
|
||||
* Returns the instance object to clients requesting bot info.
|
||||
*/
|
||||
private async handleGetInstanceFroClient(req: any, res: any) {
|
||||
private async handleGetInstanceForClient(req: any, res: any) {
|
||||
let botId = req.params.botId;
|
||||
if (botId === '[default]' || botId === undefined) {
|
||||
botId = GBConfigService.get('BOT_ID');
|
||||
|
@ -365,7 +358,7 @@ export class GBMinService {
|
|||
}
|
||||
}
|
||||
|
||||
private async buildBotAdapter(instance: any, proxyAddress: string, sysPackages: IGBPackage[]) {
|
||||
private async buildBotAdapter(instance: any, sysPackages: IGBPackage[]) {
|
||||
const adapter = new BotFrameworkAdapter({
|
||||
appId: instance.marketplaceId,
|
||||
appPassword: instance.marketplacePassword
|
||||
|
@ -404,11 +397,9 @@ export class GBMinService {
|
|||
return { min, adapter, conversationState };
|
||||
}
|
||||
|
||||
private invokeLoadBot(appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance, server: any) {
|
||||
let index = 0;
|
||||
private invokeLoadBot(appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance) {
|
||||
sysPackages.forEach(e => {
|
||||
e.loadBot(min);
|
||||
index++;
|
||||
}, this);
|
||||
|
||||
appPackages.forEach(p => {
|
||||
|
@ -456,15 +447,24 @@ export class GBMinService {
|
|||
user.loaded = true;
|
||||
user.subjects = [];
|
||||
user.cb = undefined;
|
||||
await min.userProfile.set(step.context, user);
|
||||
|
||||
|
||||
if (context.activity.membersAdded !== undefined) {
|
||||
let sec = new SecService();
|
||||
const member = context.activity.membersAdded[0];
|
||||
|
||||
await sec.ensureUser(instance.instanceId, member.id,
|
||||
const persistedUser = await sec.ensureUser(instance.instanceId, member.id,
|
||||
min.botId, member.id, "", "web", member.name, member.id);
|
||||
|
||||
const analytics = new AnalyticsService();
|
||||
|
||||
user.systemUser = persistedUser;
|
||||
user.conversation = await analytics.createConversation(persistedUser);
|
||||
|
||||
}
|
||||
|
||||
await min.userProfile.set(step.context, user);
|
||||
|
||||
}
|
||||
|
||||
GBLog.info(
|
||||
|
@ -534,7 +534,17 @@ export class GBMinService {
|
|||
}
|
||||
|
||||
private async processMessageActivity(context, min: GBMinInstance, step: GBDialogStep) {
|
||||
// Direct script invoking by itent name.
|
||||
|
||||
// Adds message to the analytics layer.
|
||||
|
||||
const analytics = new AnalyticsService();
|
||||
const user = await min.userProfile.get(context, {});
|
||||
analytics.createMessage(min.instance.instanceId,
|
||||
user.conversation, user.systemUser,
|
||||
context.activity.text);
|
||||
|
||||
// Checks for global exit kewywords cancelling any active dialogs.
|
||||
|
||||
const globalQuit = (locale, utterance) => {
|
||||
return utterance.match(Messages[locale].global_quit);
|
||||
}
|
||||
|
@ -562,7 +572,6 @@ export class GBMinService {
|
|||
await step.beginDialog('/menu', JSON.parse(context.activity.text));
|
||||
// Otherwise, continue to the active dialog in the stack.
|
||||
} else {
|
||||
const user = await min.userProfile.get(context, {});
|
||||
if (step.activeDialog !== undefined) {
|
||||
await step.continueDialog();
|
||||
} else {
|
||||
|
|
|
@ -42,6 +42,7 @@ import { BotAdapter } from 'botbuilder';
|
|||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { CSService } from '../services/CSService';
|
||||
import { Messages } from '../strings';
|
||||
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService';
|
||||
|
||||
/**
|
||||
* Dialog for collecting quality of answer.
|
||||
|
@ -78,6 +79,14 @@ export class QualityDialog extends IGBDialog {
|
|||
user.lastQuestion,
|
||||
user.lastQuestionId
|
||||
);
|
||||
|
||||
// Updates values to perform Bot Analytics.
|
||||
|
||||
const analytics = new AnalyticsService();
|
||||
analytics.updateConversationRate(min.instance.instanceId, user.conversation, score);
|
||||
|
||||
// Goes to the ask loop.
|
||||
|
||||
await step.replaceDialog('/ask', { isReturning: true });
|
||||
}
|
||||
|
||||
|
|
52
packages/default.gbui/package-lock.json
generated
52
packages/default.gbui/package-lock.json
generated
|
@ -2818,6 +2818,15 @@
|
|||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
||||
},
|
||||
"browser-id": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-id/-/browser-id-1.1.0.tgz",
|
||||
"integrity": "sha512-MuvthhJ8pDjIYpJzHwYOrlFtTPhEOF3wk9AVvy19pLQwmS/OdGWptuhzjkgLnDJS3WgV2m7bLDvlCwufg1jWBA==",
|
||||
"requires": {
|
||||
"uuid": "^3.3.3",
|
||||
"versioned-storage": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"browser-process-hrtime": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
|
||||
|
@ -6058,6 +6067,11 @@
|
|||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"fingerprintjs2": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-2.1.0.tgz",
|
||||
"integrity": "sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ=="
|
||||
},
|
||||
"flat-cache": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
|
||||
|
@ -7639,8 +7653,7 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -7677,8 +7690,7 @@
|
|||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
|
@ -7687,8 +7699,7 @@
|
|||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -7791,8 +7802,7 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -7802,7 +7812,6 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -7822,13 +7831,11 @@
|
|||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -7845,7 +7852,6 @@
|
|||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -7926,8 +7932,7 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -7937,7 +7942,6 @@
|
|||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -8013,8 +8017,7 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -8044,7 +8047,6 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -8062,7 +8064,6 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -8101,13 +8102,11 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14283,6 +14282,11 @@
|
|||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"versioned-storage": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/versioned-storage/-/versioned-storage-1.1.0.tgz",
|
||||
"integrity": "sha512-oS+unMiWJjaVAFCkNJvmHiu6LDb8KrzT8YmfrQpAmn8/yKnW4Roq3lcVMxQofT9Oidqg8oWChbGYl4FGQJyPPg=="
|
||||
},
|
||||
"vfile": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "default.gbui",
|
||||
"version": "0.0.12",
|
||||
"version": "0.0.13",
|
||||
"private": false,
|
||||
"repository": "https://github.com/pragmatismo-io/BotServer",
|
||||
"description": "Default web interface for General Bots open-core",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"botframework-webchat": "^4.7.1",
|
||||
"deep-extend": "0.6.0",
|
||||
"fetch": "1.1.0",
|
||||
"fingerprintjs2": "^2.1.0",
|
||||
"msal": "^1.2.0",
|
||||
"powerbi-client": "2.10.2",
|
||||
"react": "^16.12.0",
|
||||
|
|
|
@ -41,8 +41,8 @@ import GBCss from './components/GBCss.js';
|
|||
import { DirectLine } from 'botframework-directlinejs';
|
||||
import { ConnectionStatus } from 'botframework-directlinejs';
|
||||
import ReactWebChat from 'botframework-webchat';
|
||||
// import GBPowerBIPlayer from './players/GBPowerBIPlayer.js';
|
||||
import { UserAgentApplication } from 'msal';
|
||||
import Fingerprint2 from 'fingerprintjs2';
|
||||
|
||||
class GBUIApp extends React.Component {
|
||||
constructor() {
|
||||
|
@ -90,7 +90,8 @@ class GBUIApp extends React.Component {
|
|||
}
|
||||
|
||||
getUser() {
|
||||
return { id: 'webUser@gb', name: 'You' };
|
||||
|
||||
return { id: 'web@gb', name: 'You' };
|
||||
}
|
||||
|
||||
postEvent(name, value) {
|
||||
|
|
|
@ -65,8 +65,7 @@ export class SecService extends GBService {
|
|||
user.email = userName;
|
||||
user.phone = phone;
|
||||
user.defaultChannel = channelName;
|
||||
user.save();
|
||||
return user;
|
||||
return await user.save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue