Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LiveStream logs #375

Merged
merged 15 commits into from
Oct 14, 2024
70 changes: 63 additions & 7 deletions api/lib/api/mission-log.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import TAKAPI from '../tak-api.js';
import { Type, Static } from '@sinclair/typebox';
import type { TAKItem } from './types.js';
import type { MissionOptions } from './mission.js';

export const MissionLog = Type.Object({
Expand All @@ -14,6 +15,16 @@ export const MissionLog = Type.Object({
keywords: Type.Array(Type.Unknown())
});

export const CreateMissionLog = Type.Object({
content: Type.String(),
creatorUid: Type.String(),
contentHashes: Type.Optional(Type.Array(Type.Unknown())),
keywords: Type.Optional(Type.Array(Type.Unknown()))
});

export const UpdateMissionLog = Type.Composite([ CreateMissionLog, Type.Object({
id: Type.String()
})]);

export default class {
api: TAKAPI;
Expand All @@ -32,26 +43,48 @@ export default class {
}
}

/**
* Delete a log entry on a Mission Sync
*
* {@link https://docs.tak.gov/api/takserver/redoc#tag/mission-api/operation/deleteLogEntry TAK Server Docs}.
*/
async delete(
log: string,
opts?: Static<typeof MissionOptions>
) {
): Promise<void> {
const url = new URL(`/Marti/api/missions/logs/entries/${log}`, this.api.url);

return await this.api.fetch(url, {
await this.api.fetch(url, {
method: 'DELETE',
headers: this.#headers(opts),
});

return;
}

/**
* Get a log entry on a Mission Sync
*
* {@link https://docs.tak.gov/api/takserver/redoc#tag/mission-api/operation/getOneLogEntry TAK Server Docs}.
*/
async get(
id: string,
): Promise<TAKItem<Static<typeof MissionLog>>> {
const url = new URL(`/Marti/api/missions/logs/entries/${encodeURIComponent(id)}`, this.api.url);

return await this.api.fetch(url);
}

/**
* Create a log entry on a Mission Sync
*
* {@link https://docs.tak.gov/api/takserver/redoc#tag/mission-api/operation/postLogEntry TAK Server Docs}.
*/
async create(
mission: string,
body: {
content: string;
creatorUid: string;
},
body: Static<typeof CreateMissionLog>,
opts?: Static<typeof MissionOptions>
) {
): Promise<TAKItem<Static<typeof MissionLog>>> {
const url = new URL(`/Marti/api/missions/logs/entries`, this.api.url);

return await this.api.fetch(url, {
Expand All @@ -64,4 +97,27 @@ export default class {
}
});
}

/**
* Update a log entry on a Mission Sync
*
* {@link https://docs.tak.gov/api/takserver/redoc#tag/mission-api/operation/updateLogEntry TAK Server Docs}.
*/
async update(
mission: string,
body: Static<typeof UpdateMissionLog>,
opts?: Static<typeof MissionOptions>
): Promise<TAKItem<Static<typeof MissionLog>>> {
const url = new URL(`/Marti/api/missions/logs/entries`, this.api.url);

return await this.api.fetch(url, {
method: 'PUT',
headers: this.#headers(opts),
body: {
content: body.content,
creatorUid: body.creatorUid,
missionNames: [ mission ],
}
});
}
}
1,649 changes: 363 additions & 1,286 deletions api/package-lock.json

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions api/routes/marti-mission-logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,53 @@ import { StandardResponse, GenericMartiResponse } from '../lib/types.js';
import Schema from '@openaddresses/batch-schema';
import Err from '@openaddresses/batch-error';
import { MissionOptions } from '../lib/api/mission.js';
import { MissionLog } from '../lib/api/mission-log.js';
import Auth from '../lib/auth.js';
import Config from '../lib/config.js';
import TAKAPI, {
APIAuthCertificate,
} from '../lib/tak-api.js';

export default async function router(schema: Schema, config: Config) {
await schema.get('/marti/missions/:name/log', {
name: 'List Logs',
group: 'MartiMissionLog',
params: Type.Object({
name: Type.String(),
}),
description: 'Helper API to add a log to a mission',
res: Type.Object({
total: Type.Integer(),
items: Type.Array(MissionLog)
})
}, async (req, res) => {
try {
const user = await Auth.as_user(config, req);

const auth = (await config.models.Profile.from(user.email)).auth;
const api = await TAKAPI.init(new URL(String(config.server.api)), new APIAuthCertificate(auth.cert, auth.key));

const opts: Static<typeof MissionOptions> = req.headers['missionauthorization']
? { token: String(req.headers['missionauthorization']) }
: await config.conns.subscription(user.email, req.params.name)

const mission = await api.Mission.get(
req.params.name,
{
logs: true
},
opts
);

return res.json({
total: (mission.logs || []).length,
items: mission.logs || []
});
} catch (err) {
return Err.respond(err, res);
}
});

await schema.post('/marti/missions/:name/log', {
name: 'Create Log',
group: 'MartiMissionLog',
Expand Down Expand Up @@ -48,6 +88,52 @@ export default async function router(schema: Schema, config: Config) {
}
});

await schema.patch('/marti/missions/:name/log/:logid', {
name: 'Update Log',
group: 'MartiMissionLog',
params: Type.Object({
name: Type.String(),
logid: Type.String()
}),
description: 'Helper API to update a log on a mission',
body: Type.Object({
content: Type.String()
}),
res: Type.Object({
version: Type.String(),
type: Type.String(),
data: MissionLog,
messages: Type.Optional(Type.Array(Type.String())),
nodeId: Type.Optional(Type.String())
})
}, async (req, res) => {
try {
const user = await Auth.as_user(config, req);

const auth = (await config.models.Profile.from(user.email)).auth;
const creatorUid = user.email;
const api = await TAKAPI.init(new URL(String(config.server.api)), new APIAuthCertificate(auth.cert, auth.key));

const opts: Static<typeof MissionOptions> = req.headers['missionauthorization']
? { token: String(req.headers['missionauthorization']) }
: await config.conns.subscription(user.email, req.params.name)

const mission = await api.MissionLog.update(
req.params.name,
{
id: req.params.logid,
creatorUid: creatorUid,
content: req.body.content
},
opts
);

return res.json(mission);
} catch (err) {
return Err.respond(err, res);
}
});

await schema.delete('/marti/missions/:name/log/:log', {
name: 'Delete Log',
group: 'MartiMissionLog',
Expand Down
10 changes: 8 additions & 2 deletions api/routes/marti-mission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ export default async function router(schema: Schema, config: Config) {
description: 'Helper API to get a single mission',
query: Type.Object({
password: Type.Optional(Type.String()),
changes: Type.Optional(Type.Boolean()),
logs: Type.Optional(Type.Boolean()),
changes: Type.Boolean({
default: false,
description: 'If true, include changes array in the resulting Mission'
}),
logs: Type.Boolean({
default: false,
description: 'If true, include logs array in the resulting Mission'
}),
secago: Type.Optional(Type.Integer()),
start: Type.Optional(Type.String()),
end: Type.Optional(Type.String())
Expand Down
Loading
Loading