Skip to content

Commit

Permalink
Merge pull request #102 from dfpc-coe/group-select
Browse files Browse the repository at this point in the history
Mission CoTs
  • Loading branch information
ingalls authored Feb 16, 2024
2 parents 4ee92b1 + 2815de7 commit 6bdcf7c
Show file tree
Hide file tree
Showing 106 changed files with 10,132 additions and 1,550 deletions.
11 changes: 4 additions & 7 deletions api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import SwaggerUI from 'swagger-ui-express';
import history, {Context} from 'connect-history-api-fallback';
// @ts-ignore
import Schema from '@openaddresses/batch-schema';
import { ProfileConnConfig } from './lib/connection-config.js';
import Err from '@openaddresses/batch-error';
import Modeler from '@openaddresses/batch-generic';
import minimist from 'minimist';
import { ConnectionWebSocket, sleep } from './lib/connection-pool.js';
import { ConnectionWebSocket } from './lib/connection-web.js';
import sleep from './lib/sleep.js';
import EventsPool from './lib/events-pool.js';
import { WebSocket, WebSocketServer } from 'ws';
import BlueprintLogin, { tokenParser, AuthUser } from '@tak-ps/blueprint-login';
Expand Down Expand Up @@ -230,12 +232,7 @@ export default async function server(config: Config) {
const profile = await ProfileModel.from(parsedParams.connection);
if (!profile.auth.cert || !profile.auth.key) throw new Error('No Cert Found on profile');

client = await config.conns.add({
id: parsedParams.connection,
name: parsedParams.connection,
enabled: true,
auth: profile.auth
}, true);
client = await config.conns.add(new ProfileConnConfig(parsedParams.connection, profile.auth), true);
} else {
client = config.conns.get(parsedParams.connection);

Expand Down
142 changes: 138 additions & 4 deletions api/lib/api/mission.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import TAKAPI from '../tak-api.js';
import Err from '@openaddresses/batch-error';
import { Readable } from 'node:stream'
import { TAKList } from './types.js';

Expand Down Expand Up @@ -34,13 +35,31 @@ export type Mission = {
missionChanges?: Array<unknown>; // Only present on Mission.get()
}

export type MissionSubscriber = {
token?: string;
clientUid: string;
username: string;
createTime: string;
role: {
permissions: Array<string>;
hibernateLazyInitializer: object;
type: string;
}
}

/**
* @class
*/
export default class {
api: TAKAPI;

constructor(api: TAKAPI) {
this.api = api;
}

/**
* Return users associated with this mission
*/
async contacts(name: string) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/contacts`, this.api.url);

Expand All @@ -49,6 +68,9 @@ export default class {
});
}

/**
* Remove a file from the mission
*/
async detachContents(name: string, hash: string) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/contents`, this.api.url);
url.searchParams.append('hash', hash);
Expand All @@ -58,6 +80,9 @@ export default class {
});
}

/**
* Attach a file resource by hash from the TAK Server file manager
*/
async attachContents(name: string, hashes: string[]) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/contents`, this.api.url);

Expand All @@ -69,6 +94,9 @@ export default class {
});
}

/**
* Upload a Mission Package
*/
async upload(name: string, creatorUid: string, body: Readable) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/contents/missionpackage`, this.api.url);
url.searchParams.append('creatorUid', creatorUid);
Expand All @@ -79,6 +107,94 @@ export default class {
});
}

/**
* Return UIDs associated with any subscribed users
*/
async subscriptions(name: string): Promise<TAKList<MissionSubscriber>> {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/subscriptions`, this.api.url);
return await this.api.fetch(url, {
method: 'GET'
});
}

/**
* Return permissions associated with any subscribed users
*/
async subscriptionRoles(name: string): Promise<TAKList<any>> {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/subscriptions/roles`, this.api.url);
return await this.api.fetch(url, {
method: 'GET'
});
}

/**
* Return permissions associated with a given mission if subscribed
*/
async subscription(name: string): Promise<MissionSubscriber> {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/subscription`, this.api.url);
const res = await this.api.fetch(url, {
method: 'GET'
});

return res.data;
}

/**
* Subscribe to a mission
*/
async subscribe(name: string, query: {
uid: string;
password?: string;
secago?: number;
start?: string;
end?: string;

[key: string]: unknown;
}) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/subscription`, this.api.url);

for (const q in query) url.searchParams.append(q, String(query[q]));
return await this.api.fetch(url, {
method: 'PUT'
});
}

/**
* Get current subscription status
*/
async subscribed(name: string, query: {
uid: string;

[key: string]: unknown;
}) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/subscription`, this.api.url);

for (const q in query) url.searchParams.append(q, String(query[q]));
return await this.api.fetch(url, {
method: 'GET'
});
}

/**
* Unsubscribe from a mission
*/
async unsubscribe(name: string, query: {
uid: string;
disconnectOnly?: string;

[key: string]: unknown;
}) {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}/subscription`, this.api.url);

for (const q in query) url.searchParams.append(q, String(query[q]));
return await this.api.fetch(url, {
method: 'DELETE'
});
}

/**
* List missions in currently active channels
*/
async list(query: {
passwordProtected?: string;
defaultRole?: string;
Expand All @@ -94,6 +210,9 @@ export default class {
});
}

/**
* Get mission by its GUID
*/
async getGuid(guid: string, query: {
password?: string;
changes?: string;
Expand All @@ -103,15 +222,21 @@ export default class {
end?: string;

[key: string]: unknown;
}) {
}): Promise<Mission> {
const url = new URL(`/Marti/api/missions/guid/${encodeURIComponent(guid)}`, this.api.url);

for (const q in query) url.searchParams.append(q, String(query[q]));
return await this.api.fetch(url, {
const missions: TAKList<Mission> = await this.api.fetch(url, {
method: 'GET'
});

if (!missions.data.length) throw new Err(404, null, `No Mission for GUID: ${guid}`);
return missions.data[0];
}

/**
* Get mission by its Name
*/
async get(name: string, query: {
password?: string;
changes?: string;
Expand All @@ -121,15 +246,21 @@ export default class {
end?: string;

[key: string]: unknown;
}): Promise<TAKList<Mission>> {
}): Promise<Mission> {
const url = new URL(`/Marti/api/missions/${encodeURIComponent(name)}`, this.api.url);

for (const q in query) url.searchParams.append(q, String(query[q]));
return await this.api.fetch(url, {
const missions: TAKList<Mission> = await this.api.fetch(url, {
method: 'GET'
});

if (!missions.data.length) throw new Err(404, null, `No Mission for Name: ${name}`);
return missions.data[0];
}

/**
* Create a new mission
*/
async create(name: string, query: {
group: Array<string> | string;
creatorUid: string;
Expand Down Expand Up @@ -158,6 +289,9 @@ export default class {
});
}

/**
* Delete a mission
*/
async delete(name: string, query: {
creatorUid?: string;
deepDelete?: string;
Expand Down
3 changes: 2 additions & 1 deletion api/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import SecretsManager from '@aws-sdk/client-secrets-manager';
import type EventsPool from './events-pool.js';
import { Pool } from '@openaddresses/batch-generic';
import ConnectionPool, { ConnectionWebSocket } from './connection-pool.js';
import ConnectionPool from './connection-pool.js';
import { ConnectionWebSocket } from './connection-web.js';
import Cacher from './cacher.js';
import { Server } from './schema.js';
import { type InferSelectModel } from 'drizzle-orm';
Expand Down
46 changes: 46 additions & 0 deletions api/lib/connection-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Connection } from './schema.js';
import { InferSelectModel } from 'drizzle-orm';

export type ConnectionAuth = {
cert: string;
key: string;
}

export default interface ConnectionConfig {
id: string | number;
name: string;
enabled: boolean;
auth: ConnectionAuth;
}

export class MachineConnConfig implements ConnectionConfig {
id: number;
name: string;
enabled: boolean;
auth: ConnectionAuth;

constructor(connection: InferSelectModel<typeof Connection>) {
this.id = connection.id;
this.name = connection.name;
this.enabled = connection.enabled;
this.auth = connection.auth;
}
}

export class ProfileConnConfig implements ConnectionConfig {
id: string;
name: string;
enabled: boolean;
auth: ConnectionAuth;

constructor(
email: string,
auth: ConnectionAuth
) {
this.id = email;
this.name = email;
this.enabled = true;
this.auth = auth;
}
}

Loading

0 comments on commit 6bdcf7c

Please sign in to comment.