Skip to content

Commit

Permalink
Beacon event types from MSC3489 (#2230)
Browse files Browse the repository at this point in the history
* ASSET_NODE_TYPE -> M_ASSET

Signed-off-by: Kerry Archibald <kerrya@element.io>

* export const M_TIMESTAMP = new UnstableValue("m.ts", "org.matrix.msc3488.ts");

Signed-off-by: Kerry Archibald <kerrya@element.io>

* LOCATION_EVENT_TYPE -> M_LOCATION

Signed-off-by: Kerry Archibald <kerrya@element.io>

* extensible event types for location

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add locationevent parsing helpers

Signed-off-by: Kerry Archibald <kerrya@element.io>

* rename

Signed-off-by: Kerry Archibald <kerrya@element.io>

* comment

Signed-off-by: Kerry Archibald <kerrya@element.io>

* revert makelocationcontent signature

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add beacon event types

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add variable* to type and comment

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add content helper functions for beacon_info and beacon

Signed-off-by: Kerry Archibald <kerrya@element.io>

* copyright

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add m.beacon_info.live from msc3672

Signed-off-by: Kerry Archibald <kerrya@element.io>
  • Loading branch information
Kerry authored Mar 14, 2022
1 parent 17f3920 commit d064d82
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 0 deletions.
115 changes: 115 additions & 0 deletions spec/unit/content-helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { REFERENCE_RELATION } from "matrix-events-sdk";

import { M_BEACON_INFO } from "../../src/@types/beacon";
import { LocationAssetType, M_ASSET, M_LOCATION, M_TIMESTAMP } from "../../src/@types/location";
import { makeBeaconContent, makeBeaconInfoContent } from "../../src/content-helpers";

describe('Beacon content helpers', () => {
describe('makeBeaconInfoContent()', () => {
const mockDateNow = 123456789;
beforeEach(() => {
jest.spyOn(global.Date, 'now').mockReturnValue(mockDateNow);
});
afterAll(() => {
jest.spyOn(global.Date, 'now').mockRestore();
});
it('create fully defined event content', () => {
expect(makeBeaconInfoContent(
1234,
true,
'nice beacon_info',
LocationAssetType.Pin,
)).toEqual({
[M_BEACON_INFO.name]: {
description: 'nice beacon_info',
timeout: 1234,
live: true,
},
[M_TIMESTAMP.name]: mockDateNow,
[M_ASSET.name]: {
type: LocationAssetType.Pin,
},
});
});

it('defaults timestamp to current time', () => {
expect(makeBeaconInfoContent(
1234,
true,
'nice beacon_info',
LocationAssetType.Pin,
)).toEqual(expect.objectContaining({
[M_TIMESTAMP.name]: mockDateNow,
}));
});

it('defaults asset type to self when not set', () => {
expect(makeBeaconInfoContent(
1234,
true,
'nice beacon_info',
// no assetType passed
)).toEqual(expect.objectContaining({
[M_ASSET.name]: {
type: LocationAssetType.Self,
},
}));
});
});

describe('makeBeaconContent()', () => {
it('creates event content without description', () => {
expect(makeBeaconContent(
'geo:foo',
123,
'$1234',
// no description
)).toEqual({
[M_LOCATION.name]: {
description: undefined,
uri: 'geo:foo',
},
[M_TIMESTAMP.name]: 123,
"m.relates_to": {
rel_type: REFERENCE_RELATION.name,
event_id: '$1234',
},
});
});

it('creates event content with description', () => {
expect(makeBeaconContent(
'geo:foo',
123,
'$1234',
'test description',
)).toEqual({
[M_LOCATION.name]: {
description: 'test description',
uri: 'geo:foo',
},
[M_TIMESTAMP.name]: 123,
"m.relates_to": {
rel_type: REFERENCE_RELATION.name,
event_id: '$1234',
},
});
});
});
});
144 changes: 144 additions & 0 deletions src/@types/beacon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { EitherAnd, RELATES_TO_RELATIONSHIP, REFERENCE_RELATION } from "matrix-events-sdk";

import { UnstableValue } from "../NamespacedValue";
import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location";

/**
* Beacon info and beacon event types as described in MSC3489
* https://github.com/matrix-org/matrix-spec-proposals/pull/3489
*/

/**
* Beacon info events are state events.
* We have two requirements for these events:
* 1. they can only be written by their owner
* 2. a user can have an arbitrary number of beacon_info events
*
* 1. is achieved by setting the state_key to the owners mxid.
* Event keys in room state are a combination of `type` + `state_key`.
* To achieve an arbitrary number of only owner-writable state events
* we introduce a variable suffix to the event type
*
* Eg
* {
* "type": "m.beacon_info.@matthew:matrix.org.1",
* "state_key": "@matthew:matrix.org",
* "content": {
* "m.beacon_info": {
* "description": "The Matthew Tracker",
* "timeout": 86400000,
* },
* // more content as described below
* }
* },
* {
* "type": "m.beacon_info.@matthew:matrix.org.2",
* "state_key": "@matthew:matrix.org",
* "content": {
* "m.beacon_info": {
* "description": "Another different Matthew tracker",
* "timeout": 400000,
* },
* // more content as described below
* }
* }
*/

/**
* Variable event type for m.beacon_info
*/
export const M_BEACON_INFO_VARIABLE = new UnstableValue("m.beacon_info.*", "org.matrix.msc3489.beacon_info.*");

/**
* Non-variable type for m.beacon_info event content
*/
export const M_BEACON_INFO = new UnstableValue("m.beacon_info", "org.matrix.msc3489.beacon_info");
export const M_BEACON = new UnstableValue("m.beacon", "org.matrix.msc3489.beacon");

export type MBeaconInfoContent = {
description?: string;
// how long from the last event until we consider the beacon inactive in milliseconds
timeout: number;
// true when this is a live location beacon
// https://github.com/matrix-org/matrix-spec-proposals/pull/3672
live?: boolean;
};

export type MBeaconInfoEvent = EitherAnd<
{ [M_BEACON_INFO.name]: MBeaconInfoContent },
{ [M_BEACON_INFO.altName]: MBeaconInfoContent }
>;

/**
* m.beacon_info Event example from the spec
* https://github.com/matrix-org/matrix-spec-proposals/pull/3489
* {
"type": "m.beacon_info.@matthew:matrix.org.1",
"state_key": "@matthew:matrix.org",
"content": {
"m.beacon_info": {
"description": "The Matthew Tracker", // same as an `m.location` description
"timeout": 86400000, // how long from the last event until we consider the beacon inactive in milliseconds
},
"m.ts": 1436829458432, // creation timestamp of the beacon on the client
"m.asset": {
"type": "m.self" // the type of asset being tracked as per MSC3488
}
}
}
*/

/**
* m.beacon_info.* event content
*/
export type MBeaconInfoEventContent = &
MBeaconInfoEvent &
// creation timestamp of the beacon on the client
MTimestampEvent &
// the type of asset being tracked as per MSC3488
MAssetEvent;

/**
* m.beacon event example
* https://github.com/matrix-org/matrix-spec-proposals/pull/3489
*
* {
"type": "m.beacon",
"sender": "@matthew:matrix.org",
"content": {
"m.relates_to": { // from MSC2674: https://github.com/matrix-org/matrix-doc/pull/2674
"rel_type": "m.reference", // from MSC3267: https://github.com/matrix-org/matrix-doc/pull/3267
"event_id": "$beacon_info"
},
"m.location": {
"uri": "geo:51.5008,0.1247;u=35",
"description": "Arbitrary beacon information"
},
"m.ts": 1636829458432,
}
}
*/

export type MBeaconEventContent = &
MLocationEvent &
// timestamp when location was taken
MTimestampEvent &
// relates to a beacon_info event
RELATES_TO_RELATIONSHIP<typeof REFERENCE_RELATION>;

41 changes: 41 additions & 0 deletions src/content-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ limitations under the License.

/** @module ContentHelpers */

import { REFERENCE_RELATION } from "matrix-events-sdk";

import { MBeaconEventContent, MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon";
import { MsgType } from "./@types/event";
import { TEXT_NODE_TYPE } from "./@types/extensible_events";
import {
Expand Down Expand Up @@ -186,3 +189,41 @@ export const parseLocationEvent = (wireEventContent: LocationEventWireContent):

return makeLocationContent(fallbackText, geoUri, timestamp, description, assetType);
};

/**
* Beacon event helpers
*/

export const makeBeaconInfoContent = (
timeout: number,
isLive?: boolean,
description?: string,
assetType?: LocationAssetType,
): MBeaconInfoEventContent => ({
[M_BEACON_INFO.name]: {
description,
timeout,
live: isLive,
},
[M_TIMESTAMP.name]: Date.now(),
[M_ASSET.name]: {
type: assetType ?? LocationAssetType.Self,
},
});

export const makeBeaconContent = (
uri: string,
timestamp: number,
beaconInfoId: string,
description?: string,
): MBeaconEventContent => ({
[M_LOCATION.name]: {
description,
uri,
},
[M_TIMESTAMP.name]: timestamp,
"m.relates_to": {
rel_type: REFERENCE_RELATION.name,
event_id: beaconInfoId,
},
});

0 comments on commit d064d82

Please sign in to comment.