mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 16:42:22 +01:00
feat: refactor location representation in AP
This commit is contained in:
commit
e0325d5145
25 changed files with 387 additions and 57 deletions
|
@ -20,7 +20,7 @@
|
|||
|
||||
<div class='p-location' itemprop="location" itemscope itemtype="https://schema.org/Place">
|
||||
<nuxt-link class='place d-block pl-0' text
|
||||
:to='`/place/${encodeURIComponent(event.place.name)}`'>
|
||||
:to='`/place/${event.place.id}/${encodeURIComponent(event.place.name)}`'>
|
||||
<v-icon v-text='mdiMapMarker'></v-icon>
|
||||
<span itemprop='name'>{{ event.place.name }}</span>
|
||||
</nuxt-link>
|
||||
|
|
|
@ -7,7 +7,7 @@ v-card
|
|||
v-row.my-4.d-flex.flex-column.align-center.text-center
|
||||
.text-h6
|
||||
v-icon(v-text='mdiMapMarker' )
|
||||
nuxt-link.ml-2.text-decoration-none(v-text="place.name" :to='`/place/${place.name}`')
|
||||
nuxt-link.ml-2.text-decoration-none(v-text="place.name" :to='`/place/${place.id}/${encodeURIComponent(place.name)}`')
|
||||
.mx-2(v-text="`${place.address}`")
|
||||
v-card-actions.py-4
|
||||
HowToArriveNav.pl-1(:place='place')
|
||||
|
|
|
@ -84,7 +84,7 @@ v-container
|
|||
template(v-slot:item.actions='{ item }')
|
||||
v-btn(@click='editPlace(item)' color='primary' icon)
|
||||
v-icon(v-text='mdiPencil')
|
||||
nuxt-link(:to='`/place/${item.name}`')
|
||||
nuxt-link(:to='`/place/${item.id}/${encodeURIComponent(item.name)}`')
|
||||
v-icon(v-text='mdiEye')
|
||||
|
||||
</template>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
.p-location.h-adr(itemprop="location" itemscope itemtype="https://schema.org/Place")
|
||||
v-icon(v-text='mdiMapMarker' small)
|
||||
nuxt-link.vcard.ml-2.p-name.text-decoration-none.text-uppercase(:to='`/place/${encodeURIComponent(event?.place?.name)}`')
|
||||
nuxt-link.vcard.ml-2.p-name.text-decoration-none.text-uppercase(:to='`/place/${event?.place?.id}/${encodeURIComponent(event?.place?.name)}`')
|
||||
span(itemprop='name') {{event?.place?.name}}
|
||||
.font-weight-light.p-street-address(v-if='event?.place?.name !=="online"' itemprop='address') {{event?.place?.address}}
|
||||
|
||||
|
|
65
pages/place/_id/_place.vue
Normal file
65
pages/place/_id/_place.vue
Normal file
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<v-container id='home' class='px-2 px-sm-6 pt-0'>
|
||||
<h1 class='d-block text-h4 font-weight-black text-center text-uppercase mt-10 mx-auto w-100 text-underline'>
|
||||
<u>{{ place.name }}</u>
|
||||
</h1>
|
||||
<span v-if='place.name!=="online"' class="d-block text-subtitle text-center w-100">{{ place.address }}</span>
|
||||
|
||||
<!-- Map -->
|
||||
<div v-if='settings.allow_geolocation && place.latitude && place.longitude' >
|
||||
<div class="mt-4 mx-auto px-4" >
|
||||
<Map :place='place' :height='mapHeight' />
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<HowToArriveNav :place='place' class="justify-center" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Events -->
|
||||
<div id="events" class='mt-14'>
|
||||
<v-lazy class='event v-card' :value='idx<9' v-for='(event, idx) in events' :key='event.id' :min-height='hide_thumbs ? 105 : undefined' :options="{ threshold: .5, rootMargin: '500px' }" :class="{ 'theme--dark': is_dark }">
|
||||
<Event :event='event' :lazy='idx > 9' />
|
||||
</v-lazy>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Event from '@/components/Event'
|
||||
import HowToArriveNav from '@/components/HowToArriveNav.vue'
|
||||
|
||||
export default {
|
||||
name: 'Place',
|
||||
components: {
|
||||
Event,
|
||||
HowToArriveNav,
|
||||
[process.client && 'Map']: () => import('@/components/Map.vue')
|
||||
},
|
||||
data() {
|
||||
return { mapHeight: "14rem" }
|
||||
},
|
||||
head() {
|
||||
const title = `${this.settings.title} - ${this.place.name}`
|
||||
return {
|
||||
title,
|
||||
link: [
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/place/${this.place.id}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/place/${this.place.id}` }
|
||||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
...mapGetters(['hide_thumbs', 'is_dark']),
|
||||
},
|
||||
async asyncData({ $axios, params, error }) {
|
||||
try {
|
||||
const { events, place } = await $axios.$get(`/place/${params.id}`)
|
||||
return { place, events }
|
||||
} catch (e) {
|
||||
error({ statusCode: 404, message: 'Place not found!' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -44,8 +44,8 @@ export default {
|
|||
return {
|
||||
title,
|
||||
link: [
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/place/${this.place.name}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/place/${this.place.name}` }
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/place/${this.place.id}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/place/${this.place.id}` }
|
||||
],
|
||||
}
|
||||
},
|
||||
|
|
|
@ -23,7 +23,7 @@ const collectionController = require('./collection')
|
|||
const eventController = {
|
||||
|
||||
async _findOrCreatePlace (body) {
|
||||
if (body.place_id) {
|
||||
if (body?.place_id) {
|
||||
const place = await Place.findByPk(body.place_id)
|
||||
if (!place) {
|
||||
throw new Error(`Place not found`)
|
||||
|
@ -31,18 +31,25 @@ const eventController = {
|
|||
return place
|
||||
}
|
||||
|
||||
if (body?.place_ap_id) {
|
||||
const place = await Place.findOne({ where: { ap_id: body.place_ap_id } })
|
||||
if (place) {
|
||||
return place
|
||||
}
|
||||
}
|
||||
|
||||
const place_name = body.place_name && body.place_name.trim()
|
||||
const place_address = body.place_address && body.place_address.trim()
|
||||
if (!place_name || !place_address && place_name?.toLocaleLowerCase() !== 'online') {
|
||||
throw new Error(`place_id or place_name and place_address are required`)
|
||||
throw new Error(`place_id or place_name and place_address are required: ${JSON.stringify(body)}`)
|
||||
}
|
||||
let place = await Place.findOne({ where: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), Sequelize.Op.eq, place_name.toLocaleLowerCase()) })
|
||||
if (!place) {
|
||||
place = await Place.create({
|
||||
name: place_name,
|
||||
address: place_address || '',
|
||||
latitude: Number(body.place_latitude) || null,
|
||||
longitude: Number(body.place_longitude) || null
|
||||
...( body.place_ap_id && { ap_id: body.place_ap_id }),
|
||||
...( body.place_latitude && body.place_longitude && ({ latitude: Number(body.place_latitude), longitude: Number(body.place_longitude) }))
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
console.error(e?.errors)
|
||||
|
|
|
@ -9,22 +9,22 @@ const { Op, where, col, fn, cast } = require('sequelize')
|
|||
module.exports = {
|
||||
|
||||
async getEvents (req, res) {
|
||||
const placeName = req.params.placeName
|
||||
const place = await Place.findOne({ where: { name: placeName }})
|
||||
const placeNameOrId = req.params.placeNameOrId
|
||||
const place = await Place.findOne({ where: { [Op.or]: { id: placeNameOrId, name: placeNameOrId }}})
|
||||
if (!place) {
|
||||
log.warn(`Place ${placeName} not found`)
|
||||
log.warn(`Place ${placeNameOrId} not found`)
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
|
||||
const format = req.params.format || 'json'
|
||||
log.debug(`Events for place: ${placeName}`)
|
||||
log.debug(`Events for place: ${place.name}`)
|
||||
const events = await eventController._select({ places: String(place.id), show_recurrent: true, show_multidate: true })
|
||||
|
||||
switch (format) {
|
||||
case 'rss':
|
||||
return exportController.feed(req, res, events,
|
||||
`${res.locals.settings.title} - Place @${place.name}`,
|
||||
`${res.locals.settings.baseurl}/feed/rss/place/${place.name}`)
|
||||
`${res.locals.settings.baseurl}/feed/rss/place/${place.id}`)
|
||||
case 'ics':
|
||||
return exportController.ics(req, res, events)
|
||||
default:
|
||||
|
|
|
@ -190,7 +190,7 @@ module.exports = () => {
|
|||
|
||||
// - PLACES
|
||||
api.get('/places', isAdmin, placeController.getAll)
|
||||
api.get('/place/:placeName', cors, placeController.getEvents)
|
||||
api.get('/place/:placeNameOrId', cors, placeController.getEvents)
|
||||
api.get('/place', cors, placeController.search)
|
||||
api.put('/place', isAdmin, placeController.updatePlace)
|
||||
|
||||
|
|
|
@ -52,8 +52,13 @@ module.exports = (sequelize, DataTypes) => {
|
|||
const summary = `${this.place && this.place.name}, ${datetime}`
|
||||
|
||||
let attachment = []
|
||||
let location = []
|
||||
|
||||
if (this?.online_locations?.length) {
|
||||
location = this.online_locations.map(url => ({
|
||||
type: 'VirtualLocation',
|
||||
url
|
||||
}))
|
||||
attachment = this.online_locations.map( href => ({
|
||||
type: 'Link',
|
||||
mediaType: 'text/html',
|
||||
|
@ -61,6 +66,15 @@ module.exports = (sequelize, DataTypes) => {
|
|||
href
|
||||
}))
|
||||
}
|
||||
|
||||
if (this.place.name !== 'online') {
|
||||
location.push(this.place.toAP())
|
||||
}
|
||||
|
||||
// in case we only have a single location (the common case) do not use an array to simplify federation parser
|
||||
if (location.length === 1) {
|
||||
location = location[0]
|
||||
}
|
||||
|
||||
if (this?.media?.length) {
|
||||
attachment.push({
|
||||
|
@ -92,13 +106,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
type: 'Event',
|
||||
startTime: DateTime.fromSeconds(this.start_datetime, opt).toISO(),
|
||||
...( this.end_datetime ? { endTime : DateTime.fromSeconds(this.end_datetime, opt).toISO() } : {} ),
|
||||
location: {
|
||||
type: 'Place',
|
||||
name: this.place.name,
|
||||
address: this.place.address,
|
||||
latitude: this.place.latitude,
|
||||
longitude: this.place.longitude
|
||||
},
|
||||
location,
|
||||
attachment,
|
||||
tag: tags,
|
||||
published: this.createdAt,
|
||||
|
|
|
@ -1,12 +1,36 @@
|
|||
module.exports = (sequelize, DataTypes) =>
|
||||
sequelize.define('place', {
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true,
|
||||
index: true,
|
||||
allowNull: false
|
||||
},
|
||||
address: DataTypes.STRING,
|
||||
latitude: DataTypes.FLOAT,
|
||||
longitude: DataTypes.FLOAT,
|
||||
})
|
||||
const config = require('../../config')
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
const Place = sequelize.define('place', {
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
index: true,
|
||||
allowNull: false
|
||||
},
|
||||
ap_id: {
|
||||
type: DataTypes.STRING,
|
||||
index: true
|
||||
},
|
||||
address: DataTypes.STRING,
|
||||
latitude: DataTypes.FLOAT,
|
||||
longitude: DataTypes.FLOAT,
|
||||
})
|
||||
|
||||
/**
|
||||
* @returns ActivityStream location representation
|
||||
* @link https://www.w3.org/TR/activitystreams-vocabulary/#places
|
||||
* @todo support PostalAddress type
|
||||
* @link WIP -> https://codeberg.org/fediverse/fep/src/commit/4a75a1bc50bc6d19fc1e6112f02c52621bc178fe/fep/8a8e/fep-8a8e.md#location
|
||||
*/
|
||||
Place.prototype.toAP = function () {
|
||||
return {
|
||||
id: this?.ap_id ?? `${config.baseurl}/federation/p/${this.id}`,
|
||||
type: 'Place',
|
||||
name: this.name,
|
||||
address: this.address,
|
||||
...( this.latitude && this.longitude && ({ latitude: this.latitude, longitude: this.longitude}))
|
||||
}
|
||||
}
|
||||
|
||||
return Place
|
||||
}
|
||||
|
|
|
@ -175,38 +175,72 @@ const Helpers = {
|
|||
}
|
||||
},
|
||||
|
||||
async parsePlace (APEvent) {
|
||||
|
||||
/**
|
||||
* Parses the location of an ActivityPub Event to extract physical and online locations.
|
||||
* @link https://www.w3.org/TR/activitystreams-vocabulary/#places
|
||||
* @link https://codeberg.org/fediverse/fep/src/commit/4a75a1bc50bc6d19fc1e6112f02c52621bc178fe/fep/8a8e/fep-8a8e.md#location
|
||||
* @param {Object} APEvent - The ActivityPub Event object
|
||||
* @returns {Array} An array containing the Place and a list of online locations
|
||||
*/
|
||||
async parsePlace(APEvent) {
|
||||
const eventController = require('../api/controller/event')
|
||||
let place
|
||||
if (APEvent?.location) {
|
||||
let place = null
|
||||
|
||||
if (!APEvent?.location) {
|
||||
log.warn(`[FEDI] Event "${APEvent?.name}" has no location field`)
|
||||
return [null, null]
|
||||
}
|
||||
|
||||
const locations = Array.isArray(APEvent.location) ? APEvent.location : [APEvent.location]
|
||||
|
||||
// find the first physical place from locations
|
||||
let APPlace = locations.find(location => location.address && !location?.address?.url) || locations.find(location => !location.address?.url)
|
||||
|
||||
// get the list of online locations
|
||||
let onlineLocations = locations.filter(location => location?.address?.url).map(location => location.address.url)
|
||||
|
||||
// we have a physical place
|
||||
if (APPlace) {
|
||||
place = {
|
||||
place_name: APEvent.location?.name,
|
||||
place_address: APEvent.location?.address?.streetAddress ?? APEvent.location?.address?.addressLocality ?? APEvent.location?.address?.addressCountry ?? APEvent.location?.address ?? '',
|
||||
place_latitude: APEvent.location?.latitude,
|
||||
place_longitude: APEvent.location?.longitude
|
||||
place_name: APPlace?.name,
|
||||
...(APPlace?.id && { place_ap_id: APPlace.id }),
|
||||
...(APPlace?.latitude && APPlace?.longitude && { place_latitude: APPlace.latitude, place_longitude: APPlace.longitude }),
|
||||
}
|
||||
// no physical but at least virtual location
|
||||
} else if (onlineLocations.length) {
|
||||
place = {
|
||||
place_name: 'online'
|
||||
}
|
||||
// nothing...
|
||||
} else {
|
||||
log.warn(`[FEDI] No Physical nor Virtual location: ${JSON.stringify(APEvent.location)}`)
|
||||
return [null, null]
|
||||
}
|
||||
|
||||
// could have online locations too
|
||||
let online_locations = []
|
||||
if (APEvent?.attachment?.length) {
|
||||
online_locations = APEvent.attachment.filter(a => a?.type === 'Link' && a?.href).map(a => a.href)
|
||||
}
|
||||
|
||||
if (!place) {
|
||||
if (online_locations) {
|
||||
place = { place_name: 'online' }
|
||||
// the `address` field could be Text, PostalAddress or VirtualLocation, we do support the name as a fallback
|
||||
const addr = APPlace?.address
|
||||
if (addr) {
|
||||
if (typeof addr === 'string') {
|
||||
place.place_address = addr
|
||||
} else if ( addr?.streetAddress || addr?.addressLocality || addr?.addressCountry || addr?.addressRegion ) {
|
||||
place.place_address = [ addr?.streetAddress, addr?.addressLocality, addr?.addressRegion, addr?.addressCountry].filter(part => part).join(', ')
|
||||
} else if (addr?.url) {
|
||||
place.place_name = 'online'
|
||||
} else {
|
||||
throw new Error ('No location nor online location')
|
||||
console.warn(`[FEDI] Event "${APEvent?.name}" has bad address location: ${JSON.stringify(APPlace?.address)}`)
|
||||
}
|
||||
} else {
|
||||
place.place_address = place.place_name
|
||||
}
|
||||
|
||||
place = await eventController._findOrCreatePlace(place)
|
||||
|
||||
if (!place) {
|
||||
throw new Error('Place not found nor created')
|
||||
throw new Error('Place not found nor created')
|
||||
}
|
||||
|
||||
return [place, online_locations]
|
||||
return [place, onlineLocations]
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -233,7 +267,7 @@ const Helpers = {
|
|||
|
||||
const APEvent = message.object
|
||||
|
||||
// validate coming events
|
||||
// validate incoming events
|
||||
const required_fields = ['name', 'startTime', 'id']
|
||||
let missing_field = required_fields.find(required_field => !APEvent[required_field])
|
||||
if (missing_field) {
|
||||
|
@ -287,7 +321,9 @@ const Helpers = {
|
|||
return false
|
||||
})
|
||||
|
||||
await event.setPlace(place)
|
||||
if (place) {
|
||||
await event.setPlace(place)
|
||||
}
|
||||
|
||||
// create/assign tags
|
||||
let tags = []
|
||||
|
|
|
@ -2,6 +2,7 @@ const express = require('express')
|
|||
const router = express.Router()
|
||||
// const cors = require('cors')
|
||||
const Users = require('./users')
|
||||
const Places = require('./places')
|
||||
const { Event, User, Tag, Place } = require('../api/models/models')
|
||||
|
||||
const settingsController = require('../api/controller/settings')
|
||||
|
@ -75,6 +76,8 @@ router.get('/u/:name/outbox', Users.outbox)
|
|||
|
||||
router.get('/u/:name', Users.get)
|
||||
|
||||
router.get('/p/:id', Places.get)
|
||||
|
||||
// Handle 404
|
||||
router.use((req, res) => {
|
||||
log.warn(`[FEDI] 404 Page not found: ${req.path}`)
|
||||
|
|
21
server/federation/places.js
Normal file
21
server/federation/places.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
const { Place } = require('../api/models/models')
|
||||
const log = require('../log')
|
||||
|
||||
module.exports = {
|
||||
async get (req, res) {
|
||||
log.debug('[FEDI] Get location')
|
||||
const id = req.params.id
|
||||
if (!id) { return res.status(400).send('Bad request.') }
|
||||
|
||||
const place = await Place.findByPk(id)
|
||||
if (!place) {
|
||||
log.warn(`[FEDI] Place ${id} not found`)
|
||||
return res.status(404).send('Not found.')
|
||||
}
|
||||
|
||||
const ret = place.toAP()
|
||||
ret['@context'] = ['https://www.w3.org/ns/activitystreams']
|
||||
res.type('application/activity+json; charset=utf-8')
|
||||
res.json(ret)
|
||||
},
|
||||
}
|
12
server/migrations/20250108163040-apIdPlace.js
Normal file
12
server/migrations/20250108163040-apIdPlace.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
return queryInterface.addColumn('places', 'ap_id', { type: Sequelize.STRING, index: true })
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
return queryInterface.removeColumn('places', 'ap_id')
|
||||
}
|
||||
};
|
23
server/migrations/20250109092712-removeNameUniqueness.js
Normal file
23
server/migrations/20250109092712-removeNameUniqueness.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
// needed as in sequelize there is no support for alter table and sequelize simulate this by creating a backup table and dropping the old one:
|
||||
// this will cause a foreign key error
|
||||
const dialect = queryInterface.sequelize.getDialect()
|
||||
if (dialect === 'sqlite') {
|
||||
await queryInterface.sequelize.query('PRAGMA foreign_keys = OFF')
|
||||
}
|
||||
|
||||
await queryInterface.changeColumn('places', 'name', { type: Sequelize.STRING, index: true, allowNull: false, unique: false })
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
const dialect = queryInterface.sequelize.getDialect()
|
||||
if (dialect === 'sqlite') {
|
||||
await queryInterface.sequelize.query('PRAGMA foreign_keys = OFF')
|
||||
}
|
||||
await queryInterface.changeColumn('places', 'name', { type: Sequelize.STRING, index: true, allowNull: false, unique: true })
|
||||
}
|
||||
};
|
|
@ -42,7 +42,7 @@ async function main () {
|
|||
// rss / ics feed
|
||||
app.use(helpers.feedRedirect)
|
||||
app.get('/feed/:format/tag/:tag', cors(), tagController.getEvents)
|
||||
app.get('/feed/:format/place/:placeName', cors(), placeController.getEvents)
|
||||
app.get('/feed/:format/place/:placeNameOrId', cors(), placeController.getEvents)
|
||||
app.get('/feed/:format/collection/:name', cors(), collectionController.getEvents)
|
||||
app.get('/feed/:format', cors(), exportController.export)
|
||||
|
||||
|
|
|
@ -102,6 +102,66 @@ describe('AP', () => {
|
|||
expect(response.body.type).toBe('Application')
|
||||
})
|
||||
|
||||
|
||||
describe('Location', () => {
|
||||
|
||||
test('should fail parsing a location without a name', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/bad-location-without-a-name.json')
|
||||
expect(parsePlace({ location })).rejects.toThrow()
|
||||
})
|
||||
|
||||
test ('should parse a location with only a name', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/physical-location-without-id.json')
|
||||
let [place] = await parsePlace({ location })
|
||||
expect(place.name).toBe('Location without id')
|
||||
expect(place.ap_id).toBeUndefined()
|
||||
expect(place.id).toBe(1)
|
||||
})
|
||||
|
||||
test ('should parse a location with id and name', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/physical-location-no-address.json')
|
||||
let [place] = await parsePlace({ location })
|
||||
expect(place.name).toBe('Location with a name')
|
||||
expect(place.address).toBe('Location with a name')
|
||||
expect(place.id).toBe(2)
|
||||
})
|
||||
|
||||
test ('should parse a location with a simple string address', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/physical-location-with-simple-string-address.json')
|
||||
let [place] = await parsePlace({ location })
|
||||
expect(place.name).toBe('Location with a simple string address')
|
||||
})
|
||||
|
||||
test ('should parse a location with a postal address', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/physical-location-with-postal-address.json')
|
||||
let [place] = await parsePlace({ location })
|
||||
expect(place.name).toBe('Location with a postal address')
|
||||
})
|
||||
|
||||
test ('should parse a virtual location', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/virtual-location.json')
|
||||
let [place, online_locations] = await parsePlace({ location })
|
||||
expect(place.name).toBe('online')
|
||||
expect(online_locations.length).toBe(1)
|
||||
expect(online_locations[0]).toBe("https://virtual.location.org")
|
||||
})
|
||||
|
||||
test ('should parse a mixed location', async () => {
|
||||
const { parsePlace } = require('../server/federation/helpers.js')
|
||||
const location = require('./fixtures/AP-location/multiple-mixed-locations.json')
|
||||
let [place, online_locations] = await parsePlace({ location })
|
||||
expect(place.name).toBe('Location with a name')
|
||||
expect(online_locations.length).toBe(2)
|
||||
expect(online_locations[0]).toBe('https://virtual.location.org')
|
||||
})
|
||||
})
|
||||
|
||||
// test('should not allow to create a new Event from a random instance', async () => {
|
||||
// const response = await request(app)
|
||||
// .post('/federation/u/relay/inbox')
|
||||
|
|
4
tests/fixtures/AP-location/bad-location-without-a-name.json
vendored
Normal file
4
tests/fixtures/AP-location/bad-location-without-a-name.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"id" : "http://localhost:13120/federation/p/1",
|
||||
"type" : "Place"
|
||||
}
|
30
tests/fixtures/AP-location/multiple-mixed-locations.json
vendored
Normal file
30
tests/fixtures/AP-location/multiple-mixed-locations.json
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"id" : "http://localhost:13120/federation/p/4",
|
||||
"name" : "Location with a name",
|
||||
"type" : "Place"
|
||||
},
|
||||
{
|
||||
"id" : "http://localhost:13120/federation/p/5",
|
||||
"name" : "A second location with a name",
|
||||
"type" : "Place"
|
||||
},
|
||||
{
|
||||
"id" : "https://virtual.location.org",
|
||||
"name" : "A Virtual location",
|
||||
"address": {
|
||||
"type": "VirtualLocation",
|
||||
"url": "https://virtual.location.org"
|
||||
},
|
||||
"type" : "Place"
|
||||
},
|
||||
{
|
||||
"id" : "https://a.second.fallback.virtual.location.org",
|
||||
"name" : "A Fallback Virtual location",
|
||||
"address": {
|
||||
"type": "VirtualLocation",
|
||||
"url": "https://a.second.fallback.virtual.location.org"
|
||||
},
|
||||
"type" : "Place"
|
||||
}
|
||||
]
|
5
tests/fixtures/AP-location/physical-location-no-address.json
vendored
Normal file
5
tests/fixtures/AP-location/physical-location-no-address.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id" : "http://localhost:13120/federation/p/1",
|
||||
"name" : "Location with a name",
|
||||
"type" : "Place"
|
||||
}
|
13
tests/fixtures/AP-location/physical-location-with-postal-address.json
vendored
Normal file
13
tests/fixtures/AP-location/physical-location-with-postal-address.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id" : "http://localhost:13120/federation/p/2",
|
||||
"name" : "Location with a postal address",
|
||||
"address": {
|
||||
"type": "PostalAddress",
|
||||
"addressCountry": "Austria",
|
||||
"addressLocality": "Fediverse Town",
|
||||
"addressRegion": "Steiermark",
|
||||
"postalCode": "8010",
|
||||
"streetAddress": "15 Fediverse Street"
|
||||
},
|
||||
"type" : "Place"
|
||||
}
|
6
tests/fixtures/AP-location/physical-location-with-simple-string-address.json
vendored
Normal file
6
tests/fixtures/AP-location/physical-location-with-simple-string-address.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id" : "http://localhost:13120/federation/p/3",
|
||||
"name" : "Location with a simple string address",
|
||||
"address": "Simple string address",
|
||||
"type" : "Place"
|
||||
}
|
4
tests/fixtures/AP-location/physical-location-without-id.json
vendored
Normal file
4
tests/fixtures/AP-location/physical-location-without-id.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name" : "Location without id",
|
||||
"type" : "Place"
|
||||
}
|
9
tests/fixtures/AP-location/virtual-location.json
vendored
Normal file
9
tests/fixtures/AP-location/virtual-location.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"id" : "https://virtual.location.org",
|
||||
"name" : "A Virtual location",
|
||||
"address": {
|
||||
"type": "VirtualLocation",
|
||||
"url": "https://virtual.location.org"
|
||||
},
|
||||
"type" : "Place"
|
||||
}
|
Loading…
Reference in a new issue