352 lines
11 KiB
TypeScript
352 lines
11 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, it } from "@jest/globals";
|
|
import { init, TestPlatform } from "../setup";
|
|
import { TestDbService } from "../utils.prepare.db";
|
|
import { Api } from "../utils.api";
|
|
import Application, {
|
|
PublicApplicationObject,
|
|
} from "../../../src/services/applications/entities/application";
|
|
import { cloneDeep } from "lodash";
|
|
|
|
import { logger as log } from "../../../src/core/platform/framework";
|
|
import { v1 as uuidv1 } from "uuid";
|
|
|
|
describe("Applications", () => {
|
|
const url = "/internal/services/applications/v1";
|
|
|
|
let platform: TestPlatform;
|
|
let testDbService: TestDbService;
|
|
let api: Api;
|
|
let appRepo;
|
|
|
|
beforeAll(async ends => {
|
|
platform = await init();
|
|
await platform.database.getConnector().drop();
|
|
testDbService = await TestDbService.getInstance(platform, true);
|
|
await testDbService.createDefault();
|
|
postPayload.company_id = platform.workspace.company_id;
|
|
api = new Api(platform);
|
|
appRepo = await testDbService.getRepository("application", Application);
|
|
ends();
|
|
});
|
|
|
|
afterAll(done => {
|
|
platform.tearDown().then(() => done());
|
|
});
|
|
|
|
const publishApp = async id => {
|
|
const entity = await appRepo.findOne({ id });
|
|
if (!entity) throw new Error(`entity ${id} not found`);
|
|
entity.publication.published = true;
|
|
await appRepo.save(entity);
|
|
};
|
|
|
|
describe("Create application", function () {
|
|
it("should 403 if creator is not a company admin", async done => {
|
|
const payload = { resource: cloneDeep(postPayload) };
|
|
|
|
const user = await testDbService.createUser([testDbService.defaultWorkspace()], {
|
|
companyRole: "member",
|
|
});
|
|
|
|
const response = await api.post(`${url}/applications`, payload, user.id);
|
|
expect(response.statusCode).toBe(403);
|
|
done();
|
|
});
|
|
|
|
it("should 200 on application create", async done => {
|
|
const payload = { resource: cloneDeep(postPayload) };
|
|
const response = await api.post(`${url}/applications`, payload);
|
|
expect(response.statusCode).toBe(200);
|
|
|
|
const r = response.resource;
|
|
|
|
expect(r.company_id).toBe(payload.resource.company_id);
|
|
expect(!!r.is_default).toBe(false);
|
|
expect(r.identity).toMatchObject(payload.resource.identity);
|
|
expect(r.access).toMatchObject(payload.resource.access);
|
|
expect(r.display).toMatchObject(payload.resource.display);
|
|
expect(r.publication).toMatchObject(payload.resource.publication);
|
|
expect(r.stats).toMatchObject({
|
|
created_at: expect.any(Number),
|
|
updated_at: expect.any(Number),
|
|
version: 0,
|
|
});
|
|
|
|
expect(r.api).toMatchObject({
|
|
hooks_url: payload.resource.api.hooks_url,
|
|
allowed_ips: payload.resource.api.allowed_ips,
|
|
private_key: expect.any(String),
|
|
});
|
|
|
|
expect(r.api.private_key).not.toBe("");
|
|
|
|
const dbData = await appRepo.findOne({ id: response.resource.id });
|
|
|
|
expect(dbData.api).toMatchObject({
|
|
allowed_ips: payload.resource.api.allowed_ips,
|
|
hooks_url: payload.resource.api.hooks_url,
|
|
private_key: expect.any(String),
|
|
});
|
|
|
|
done();
|
|
});
|
|
});
|
|
describe("Update application", function () {
|
|
let createdApp: PublicApplicationObject;
|
|
|
|
beforeAll(async done => {
|
|
const payload = { resource: cloneDeep(postPayload) };
|
|
const response = await api.post(`${url}/applications`, payload);
|
|
createdApp = response.resource;
|
|
|
|
done();
|
|
});
|
|
|
|
it("should 403 if editor is not a company admin", async done => {
|
|
if (!createdApp) throw new Error("can't find created app");
|
|
log.debug(createdApp);
|
|
|
|
const user = await testDbService.createUser([testDbService.defaultWorkspace()], {
|
|
companyRole: "member",
|
|
});
|
|
|
|
const response = await api.post(`${url}/applications/${createdApp.id}`, postPayload, user.id);
|
|
expect(response.statusCode).toBe(403);
|
|
done();
|
|
});
|
|
|
|
it("should 404 if application not found", async done => {
|
|
const response = await api.post(`${url}/applications/${uuidv1()}`, { resource: postPayload });
|
|
expect(response.statusCode).toBe(404);
|
|
done();
|
|
});
|
|
|
|
describe("Unpublished application", () => {
|
|
it("should 200 on application update", async done => {
|
|
const payload = { resource: cloneDeep(postPayload) };
|
|
|
|
payload.resource.is_default = true;
|
|
payload.resource.identity.name = "test2";
|
|
payload.resource.api.hooks_url = "123123";
|
|
payload.resource.access.read = [];
|
|
payload.resource.publication.requested = true;
|
|
|
|
const response = await api.post(`${url}/applications/${createdApp.id}`, payload);
|
|
expect(response.statusCode).toBe(200);
|
|
|
|
const r = response.resource;
|
|
|
|
expect(r.company_id).toBe(payload.resource.company_id);
|
|
expect(!!r.is_default).toBe(false);
|
|
expect(r.identity).toMatchObject(payload.resource.identity);
|
|
|
|
expect(r.access).toMatchObject(payload.resource.access);
|
|
expect(r.display).toMatchObject(payload.resource.display);
|
|
expect(r.publication).toMatchObject(payload.resource.publication);
|
|
expect(r.stats).toMatchObject({
|
|
created_at: expect.any(Number),
|
|
updated_at: expect.any(Number),
|
|
version: 1,
|
|
});
|
|
|
|
expect(r.api).toBeTruthy();
|
|
|
|
const dbData = await appRepo.findOne({ id: response.resource.id });
|
|
|
|
expect(dbData.api).toMatchObject({
|
|
allowed_ips: payload.resource.api.allowed_ips,
|
|
hooks_url: payload.resource.api.hooks_url,
|
|
private_key: expect.any(String),
|
|
});
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe.skip("Published application", () => {
|
|
beforeAll(async done => {
|
|
const payload = { resource: cloneDeep(postPayload) };
|
|
const response = await api.post(`${url}/applications`, payload);
|
|
createdApp = response.resource;
|
|
await publishApp(createdApp.id);
|
|
done();
|
|
});
|
|
|
|
it("should 200 on update if allowed fields changed", async done => {
|
|
const payload = { resource: cloneDeep(createdApp) as Application };
|
|
const entity = await appRepo.findOne({ id: createdApp.id });
|
|
payload.resource.api = cloneDeep(entity.api);
|
|
payload.resource.publication.requested = true;
|
|
const response = await api.post(`${url}/applications/${createdApp.id}`, payload);
|
|
expect(response.statusCode).toBe(200);
|
|
|
|
expect(response.resource.publication).toMatchObject({
|
|
requested: true,
|
|
published: true,
|
|
});
|
|
done();
|
|
});
|
|
|
|
it("should 400 on update if not allowed fields changed", async done => {
|
|
const payload = { resource: cloneDeep(createdApp) as Application };
|
|
const entity = await appRepo.findOne({ id: createdApp.id });
|
|
payload.resource.api = cloneDeep(entity.api);
|
|
const response = await api.post(`${url}/applications/${createdApp.id}`, payload);
|
|
expect(response.statusCode).toBe(400);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
describe("Get applications", function () {
|
|
let firstApp: PublicApplicationObject;
|
|
let secondApp: PublicApplicationObject;
|
|
let thirdApp: PublicApplicationObject;
|
|
beforeAll(async done => {
|
|
const payload = { resource: cloneDeep(postPayload) };
|
|
firstApp = (await api.post(`${url}/applications`, payload)).resource;
|
|
secondApp = (await api.post(`${url}/applications`, payload)).resource;
|
|
thirdApp = (await api.post(`${url}/applications`, payload)).resource;
|
|
|
|
await publishApp(firstApp.id);
|
|
await publishApp(secondApp.id);
|
|
|
|
done();
|
|
});
|
|
|
|
it("should list published applications", async done => {
|
|
const response = await api.get(`${url}/applications`);
|
|
expect(response.statusCode).toBe(200);
|
|
|
|
const published = response.resources.filter(a => a.publication.published).length;
|
|
const unpublished = response.resources.filter(a => a.publication.unpublished).length;
|
|
|
|
expect(published).toBeGreaterThanOrEqual(2);
|
|
expect(unpublished).toEqual(0);
|
|
|
|
expect(response.resources.map(a => a.id)).toEqual(
|
|
expect.arrayContaining([firstApp.id, secondApp.id]),
|
|
);
|
|
|
|
done();
|
|
});
|
|
|
|
it("should return public object for published application to any user", async done => {
|
|
const response = await api.get(`${url}/applications/${firstApp.id}`, uuidv1());
|
|
expect(response.statusCode).toBe(200);
|
|
expect(response.resource.id).toEqual(firstApp.id);
|
|
expect(response.resource.api).toBeFalsy();
|
|
done();
|
|
});
|
|
|
|
it("should return public object for unpublished application to any user", async done => {
|
|
const response = await api.get(`${url}/applications/${thirdApp.id}`, uuidv1());
|
|
expect(response.statusCode).toBe(200);
|
|
expect(response.resource.id).toEqual(thirdApp.id);
|
|
expect(response.resource.api).toBeFalsy();
|
|
done();
|
|
});
|
|
|
|
it("should return whole object for published application to admin", async done => {
|
|
const response = await api.get(`${url}/applications/${firstApp.id}`);
|
|
expect(response.statusCode).toBe(200);
|
|
expect(response.resource.id).toEqual(firstApp.id);
|
|
expect(response.resource.api).toBeTruthy();
|
|
|
|
done();
|
|
});
|
|
|
|
it("should return whole object for unpublished application to admin", async done => {
|
|
const response = await api.get(`${url}/applications/${thirdApp.id}`);
|
|
expect(response.statusCode).toBe(200);
|
|
expect(response.resource.id).toEqual(thirdApp.id);
|
|
expect(response.resource.api).toBeTruthy();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
const postPayload = {
|
|
is_default: true,
|
|
company_id: null,
|
|
identity: {
|
|
code: "code",
|
|
name: "name",
|
|
icon: "icon",
|
|
description: "description",
|
|
website: "website",
|
|
categories: [],
|
|
compatibility: [],
|
|
},
|
|
api: {
|
|
hooks_url: "hooks_url",
|
|
allowed_ips: "allowed_ips",
|
|
},
|
|
access: {
|
|
read: ["messages"],
|
|
write: ["messages"],
|
|
delete: ["messages"],
|
|
hooks: ["messages"],
|
|
},
|
|
display: {
|
|
twake: {
|
|
version: 1,
|
|
|
|
files: {
|
|
editor: {
|
|
preview_url: "string", //Open a preview inline (iframe)
|
|
edition_url: "string", //Url to edit the file (full screen)
|
|
extensions: [], //Main extensions app can read
|
|
// if file was created by the app, then the app is able to edit with or without extension
|
|
empty_files: [
|
|
{
|
|
url: "string", // "https://[...]/empty.docx";
|
|
filename: "string", // "Untitled.docx";
|
|
name: "string", // "Word Document";
|
|
},
|
|
],
|
|
},
|
|
actions: [
|
|
//List of action that can apply on a file
|
|
{
|
|
name: "string",
|
|
id: "string",
|
|
},
|
|
],
|
|
},
|
|
|
|
//Chat plugin
|
|
chat: {
|
|
input: true,
|
|
commands: [
|
|
{
|
|
command: "string", // my_app mycommand
|
|
description: "string",
|
|
},
|
|
],
|
|
actions: [
|
|
//List of action that can apply on a message
|
|
{
|
|
name: "string",
|
|
id: "string",
|
|
},
|
|
],
|
|
},
|
|
|
|
//Allow app to appear as a bot user in direct chat
|
|
direct: false,
|
|
|
|
//Display app as a standalone application in a tab
|
|
tab: { url: "string" },
|
|
|
|
//Display app as a standalone application on the left bar
|
|
standalone: { url: "string" },
|
|
|
|
//Define where the app can be configured from
|
|
configuration: ["global", "channel"],
|
|
},
|
|
},
|
|
publication: {
|
|
requested: false, //Publication requested
|
|
},
|
|
};
|