Correctly handle event update
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
6845825db2
commit
f5c3dbf128
|
@ -81,11 +81,12 @@ import { Modal } from 'buefy/dist/components/dialog';
|
||||||
export default class AddressAutoComplete extends Vue {
|
export default class AddressAutoComplete extends Vue {
|
||||||
|
|
||||||
@Prop({ required: false, default: () => [] }) initialData!: IAddress[];
|
@Prop({ required: false, default: () => [] }) initialData!: IAddress[];
|
||||||
|
@Prop({ required: false }) value!: IAddress;
|
||||||
|
|
||||||
data: IAddress[] = this.initialData;
|
data: IAddress[] = this.initialData;
|
||||||
selected: IAddress|null = new Address();
|
selected: IAddress|null = new Address();
|
||||||
isFetching: boolean = false;
|
isFetching: boolean = false;
|
||||||
queryText: string = '';
|
queryText: string = this.value && this.value.description || '';
|
||||||
addressModalActive: boolean = false;
|
addressModalActive: boolean = false;
|
||||||
|
|
||||||
async getAsyncData(query) {
|
async getAsyncData(query) {
|
||||||
|
|
|
@ -1,33 +1,51 @@
|
||||||
<template>
|
<template>
|
||||||
<b-field label="Enter some tags">
|
<b-field label="Enter some tags">
|
||||||
<b-taginput
|
<b-taginput
|
||||||
v-model="tags"
|
v-model="tagsStrings"
|
||||||
:data="filteredTags"
|
:data="filteredTags"
|
||||||
autocomplete
|
autocomplete
|
||||||
:allow-new="true"
|
:allow-new="true"
|
||||||
:field="path"
|
:field="path"
|
||||||
icon="label"
|
icon="label"
|
||||||
placeholder="Add a tag"
|
placeholder="Add a tag"
|
||||||
@typing="getFilteredTags">
|
@typing="getFilteredTags"
|
||||||
|
>
|
||||||
</b-taginput>
|
</b-taginput>
|
||||||
</b-field>
|
</b-field>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
import { get } from 'lodash';
|
import { get, differenceBy } from 'lodash';
|
||||||
import { ITag } from '@/types/tag.model';
|
import { ITag } from '@/types/tag.model';
|
||||||
@Component
|
|
||||||
|
@Component({
|
||||||
|
computed: {
|
||||||
|
tagsStrings: {
|
||||||
|
get() {
|
||||||
|
return this.$props.data.map((tag: ITag) => tag.title);
|
||||||
|
},
|
||||||
|
set(tagStrings) {
|
||||||
|
const tagEntities = tagStrings.map((tag) => {
|
||||||
|
if (TagInput.isTag(tag)) {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
return { title: tag, slug: tag } as ITag;
|
||||||
|
});
|
||||||
|
this.$emit('input', tagEntities);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
export default class TagInput extends Vue {
|
export default class TagInput extends Vue {
|
||||||
|
|
||||||
@Prop({ required: false, default: () => [] }) data!: object[];
|
@Prop({ required: false, default: () => [] }) data!: ITag[];
|
||||||
@Prop({ required: true, default: 'value' }) path!: string;
|
@Prop({ required: true, default: 'value' }) path!: string;
|
||||||
@Prop({ required: true }) value!: string;
|
@Prop({ required: true }) value!: ITag[];
|
||||||
|
|
||||||
filteredTags: object[] = [];
|
filteredTags: ITag[] = [];
|
||||||
tags: object[] = [];
|
|
||||||
|
|
||||||
getFilteredTags(text) {
|
getFilteredTags(text) {
|
||||||
this.filteredTags = this.data.filter((option) => {
|
this.filteredTags = differenceBy(this.data, this.value, 'id').filter((option) => {
|
||||||
return get(option, this.path)
|
return get(option, this.path)
|
||||||
.toString()
|
.toString()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
|
@ -35,18 +53,6 @@ export default class TagInput extends Vue {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Watch('tags')
|
|
||||||
onTagsChanged (tags) {
|
|
||||||
const tagEntities = tags.map((tag) => {
|
|
||||||
if (TagInput.isTag(tag)) {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return { title: tag, slug: tag } as ITag;
|
|
||||||
});
|
|
||||||
console.log('tags changed', tagEntities);
|
|
||||||
this.$emit('input', tagEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
static isTag(x: any): x is ITag {
|
static isTag(x: any): x is ITag {
|
||||||
return x.slug !== undefined;
|
return x.slug !== undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ export const FETCH_EVENT = gql`
|
||||||
domain,
|
domain,
|
||||||
name,
|
name,
|
||||||
url,
|
url,
|
||||||
|
id,
|
||||||
},
|
},
|
||||||
# attributedTo {
|
# attributedTo {
|
||||||
# avatar {
|
# avatar {
|
||||||
|
@ -64,6 +65,7 @@ export const FETCH_EVENT = gql`
|
||||||
${participantQuery}
|
${participantQuery}
|
||||||
},
|
},
|
||||||
tags {
|
tags {
|
||||||
|
id,
|
||||||
slug,
|
slug,
|
||||||
title
|
title
|
||||||
},
|
},
|
||||||
|
@ -82,6 +84,25 @@ export const FETCH_EVENT = gql`
|
||||||
domain,
|
domain,
|
||||||
name,
|
name,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
options {
|
||||||
|
maximumAttendeeCapacity,
|
||||||
|
remainingAttendeeCapacity,
|
||||||
|
showRemainingAttendeeCapacity,
|
||||||
|
offers {
|
||||||
|
price,
|
||||||
|
priceCurrency,
|
||||||
|
url
|
||||||
|
},
|
||||||
|
participationConditions {
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
url
|
||||||
|
},
|
||||||
|
attendees,
|
||||||
|
program,
|
||||||
|
commentModeration,
|
||||||
|
showParticipationPrice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +165,7 @@ export const CREATE_EVENT = gql`
|
||||||
$organizerActorId: ID!,
|
$organizerActorId: ID!,
|
||||||
$category: String,
|
$category: String,
|
||||||
$beginsOn: DateTime!,
|
$beginsOn: DateTime!,
|
||||||
|
$endsOn: DateTime,
|
||||||
$picture: PictureInput,
|
$picture: PictureInput,
|
||||||
$tags: [String],
|
$tags: [String],
|
||||||
$options: EventOptionsInput,
|
$options: EventOptionsInput,
|
||||||
|
@ -154,6 +176,7 @@ export const CREATE_EVENT = gql`
|
||||||
title: $title,
|
title: $title,
|
||||||
description: $description,
|
description: $description,
|
||||||
beginsOn: $beginsOn,
|
beginsOn: $beginsOn,
|
||||||
|
endsOn: $endsOn,
|
||||||
organizerActorId: $organizerActorId,
|
organizerActorId: $organizerActorId,
|
||||||
category: $category,
|
category: $category,
|
||||||
options: $options,
|
options: $options,
|
||||||
|
@ -173,13 +196,32 @@ export const CREATE_EVENT = gql`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const EDIT_EVENT = gql`
|
export const EDIT_EVENT = gql`
|
||||||
mutation EditEvent(
|
mutation updateEvent(
|
||||||
|
$id: ID!,
|
||||||
$title: String!,
|
$title: String!,
|
||||||
$description: String!,
|
$description: String!,
|
||||||
$organizerActorId: Int!,
|
$organizerActorId: ID!,
|
||||||
$category: String
|
$category: String,
|
||||||
|
$beginsOn: DateTime!,
|
||||||
|
$endsOn: DateTime,
|
||||||
|
$picture: PictureInput,
|
||||||
|
$tags: [String],
|
||||||
|
$options: EventOptionsInput,
|
||||||
|
$physicalAddress: AddressInput,
|
||||||
|
$visibility: EventVisibility
|
||||||
) {
|
) {
|
||||||
EditEvent(title: $title, description: $description, organizerActorId: $organizerActorId, category: $category) {
|
updateEvent(eventId: $id,
|
||||||
|
title: $title,
|
||||||
|
description: $description,
|
||||||
|
beginsOn: $beginsOn,
|
||||||
|
endsOn: $endsOn,
|
||||||
|
organizerActorId: $organizerActorId,
|
||||||
|
category: $category,
|
||||||
|
options: $options,
|
||||||
|
picture: $picture,
|
||||||
|
tags: $tags,
|
||||||
|
physicalAddress: $physicalAddress,
|
||||||
|
visibility: $visibility) {
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { ITag } from '@/types/tag.model';
|
||||||
import { IPicture } from '@/types/picture.model';
|
import { IPicture } from '@/types/picture.model';
|
||||||
|
|
||||||
export enum EventStatus {
|
export enum EventStatus {
|
||||||
TENTATIVE,
|
TENTATIVE = 'TENTATIVE',
|
||||||
CONFIRMED,
|
CONFIRMED = 'CONFIRMED',
|
||||||
CANCELLED,
|
CANCELLED = 'CANCELLED',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventVisibility {
|
export enum EventVisibility {
|
||||||
|
@ -17,9 +17,9 @@ export enum EventVisibility {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventJoinOptions {
|
export enum EventJoinOptions {
|
||||||
FREE,
|
FREE = 'FREE',
|
||||||
RESTRICTED,
|
RESTRICTED = 'RESTRICTED',
|
||||||
INVITE,
|
INVITE = 'INVITE',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventVisibilityJoinOptions {
|
export enum EventVisibilityJoinOptions {
|
||||||
|
|
|
@ -177,7 +177,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { CREATE_EVENT, EDIT_EVENT, FETCH_EVENT } from '@/graphql/event';
|
import { CREATE_EVENT, EDIT_EVENT, FETCH_EVENT } from '@/graphql/event';
|
||||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||||
import { EventModel, EventStatus, EventVisibility, EventVisibilityJoinOptions, IEvent, CommentModeration } from '@/types/event.model';
|
import { EventModel, EventStatus, EventVisibility, EventVisibilityJoinOptions, CommentModeration } from '@/types/event.model';
|
||||||
import { LOGGED_PERSON } from '@/graphql/actor';
|
import { LOGGED_PERSON } from '@/graphql/actor';
|
||||||
import { IPerson, Person } from '@/types/actor';
|
import { IPerson, Person } from '@/types/actor';
|
||||||
import PictureUpload from '@/components/PictureUpload.vue';
|
import PictureUpload from '@/components/PictureUpload.vue';
|
||||||
|
@ -207,6 +207,7 @@ export default class EditEvent extends Vue {
|
||||||
eventId!: string | undefined;
|
eventId!: string | undefined;
|
||||||
|
|
||||||
loggedPerson = new Person();
|
loggedPerson = new Person();
|
||||||
|
tags: ITag[] = [];
|
||||||
event = new EventModel();
|
event = new EventModel();
|
||||||
pictureFile: File | null = null;
|
pictureFile: File | null = null;
|
||||||
|
|
||||||
|
@ -223,7 +224,7 @@ export default class EditEvent extends Vue {
|
||||||
|
|
||||||
@Watch('$route.params.eventId', { immediate: true })
|
@Watch('$route.params.eventId', { immediate: true })
|
||||||
async onEventIdParamChanged (val: string) {
|
async onEventIdParamChanged (val: string) {
|
||||||
if (this.isUpdate !== true) return;
|
if (!this.isUpdate) return;
|
||||||
|
|
||||||
this.eventId = val;
|
this.eventId = val;
|
||||||
|
|
||||||
|
@ -231,6 +232,7 @@ export default class EditEvent extends Vue {
|
||||||
this.event = await this.getEvent();
|
this.event = await this.getEvent();
|
||||||
|
|
||||||
this.pictureFile = await buildFileFromIPicture(this.event.picture);
|
this.pictureFile = await buildFileFromIPicture(this.event.picture);
|
||||||
|
this.limitedPlaces = this.event.options.maximumAttendeeCapacity != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +243,6 @@ export default class EditEvent extends Vue {
|
||||||
|
|
||||||
this.event.beginsOn = now;
|
this.event.beginsOn = now;
|
||||||
this.event.endsOn = end;
|
this.event.endsOn = end;
|
||||||
console.log('eventvisibilityjoinoptions', this.eventVisibilityJoinOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createOrUpdate(e: Event) {
|
createOrUpdate(e: Event) {
|
||||||
|
@ -261,7 +262,7 @@ export default class EditEvent extends Vue {
|
||||||
|
|
||||||
console.log('Event created', data);
|
console.log('Event created', data);
|
||||||
|
|
||||||
this.$router.push({
|
await this.$router.push({
|
||||||
name: 'Event',
|
name: 'Event',
|
||||||
params: { uuid: data.createEvent.uuid },
|
params: { uuid: data.createEvent.uuid },
|
||||||
});
|
});
|
||||||
|
@ -277,7 +278,7 @@ export default class EditEvent extends Vue {
|
||||||
variables: this.buildVariables(),
|
variables: this.buildVariables(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$router.push({
|
await this.$router.push({
|
||||||
name: 'Event',
|
name: 'Event',
|
||||||
params: { uuid: this.eventId as string },
|
params: { uuid: this.eventId as string },
|
||||||
});
|
});
|
||||||
|
@ -297,6 +298,8 @@ export default class EditEvent extends Vue {
|
||||||
};
|
};
|
||||||
const res = Object.assign({}, this.event, obj);
|
const res = Object.assign({}, this.event, obj);
|
||||||
|
|
||||||
|
delete this.event.options['__typename'];
|
||||||
|
|
||||||
if (this.event.physicalAddress) {
|
if (this.event.physicalAddress) {
|
||||||
delete this.event.physicalAddress['__typename'];
|
delete this.event.physicalAddress['__typename'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<router-link
|
<router-link
|
||||||
class="button"
|
class="button"
|
||||||
:to="{ name: 'EditEvent', params: {uuid: event.uuid}}"
|
:to="{ name: 'EditEvent', params: {eventId: event.uuid}}"
|
||||||
>
|
>
|
||||||
<translate>Edit</translate>
|
<translate>Edit</translate>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
@ -110,6 +110,24 @@ defmodule Mobilizon.Actors.Actor do
|
||||||
|> unique_constraint(:url, name: :actors_url_index)
|
|> unique_constraint(:url, name: :actors_url_index)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def update_changeset(%Actor{} = actor, attrs) do
|
||||||
|
actor
|
||||||
|
|> Ecto.Changeset.cast(attrs, [
|
||||||
|
:name,
|
||||||
|
:summary,
|
||||||
|
:keys,
|
||||||
|
:manually_approves_followers,
|
||||||
|
:suspended,
|
||||||
|
:user_id
|
||||||
|
])
|
||||||
|
|> cast_embed(:avatar)
|
||||||
|
|> cast_embed(:banner)
|
||||||
|
|> validate_required([:preferred_username, :keys, :suspended, :url])
|
||||||
|
|> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_type_index)
|
||||||
|
|> unique_constraint(:url, name: :actors_url_index)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Changeset for person registration
|
Changeset for person registration
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -124,7 +124,7 @@ defmodule Mobilizon.Actors do
|
||||||
"""
|
"""
|
||||||
def update_actor(%Actor{} = actor, attrs) do
|
def update_actor(%Actor{} = actor, attrs) do
|
||||||
actor
|
actor
|
||||||
|> Actor.changeset(attrs)
|
|> Actor.update_changeset(attrs)
|
||||||
|> delete_files_if_media_changed()
|
|> delete_files_if_media_changed()
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,10 +54,10 @@ defmodule Mobilizon.Events.Event do
|
||||||
field(:online_address, :string)
|
field(:online_address, :string)
|
||||||
field(:phone_address, :string)
|
field(:phone_address, :string)
|
||||||
field(:category, :string)
|
field(:category, :string)
|
||||||
embeds_one(:options, Mobilizon.Events.EventOptions)
|
embeds_one(:options, Mobilizon.Events.EventOptions, on_replace: :update)
|
||||||
belongs_to(:organizer_actor, Actor, foreign_key: :organizer_actor_id)
|
belongs_to(:organizer_actor, Actor, foreign_key: :organizer_actor_id)
|
||||||
belongs_to(:attributed_to, Actor, foreign_key: :attributed_to_id)
|
belongs_to(:attributed_to, Actor, foreign_key: :attributed_to_id)
|
||||||
many_to_many(:tags, Tag, join_through: "events_tags")
|
many_to_many(:tags, Tag, join_through: "events_tags", on_replace: :delete)
|
||||||
many_to_many(:participants, Actor, join_through: Participant)
|
many_to_many(:participants, Actor, join_through: Participant)
|
||||||
has_many(:tracks, Track)
|
has_many(:tracks, Track)
|
||||||
has_many(:sessions, Session)
|
has_many(:sessions, Session)
|
||||||
|
@ -98,6 +98,38 @@ defmodule Mobilizon.Events.Event do
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def update_changeset(%Event{} = event, attrs) do
|
||||||
|
event
|
||||||
|
|> Ecto.Changeset.cast(attrs, [
|
||||||
|
:title,
|
||||||
|
:slug,
|
||||||
|
:description,
|
||||||
|
:begins_on,
|
||||||
|
:ends_on,
|
||||||
|
:category,
|
||||||
|
:status,
|
||||||
|
:visibility,
|
||||||
|
:publish_at,
|
||||||
|
:online_address,
|
||||||
|
:phone_address,
|
||||||
|
:picture_id,
|
||||||
|
:physical_address_id
|
||||||
|
])
|
||||||
|
|> cast_embed(:options)
|
||||||
|
|> put_tags(attrs)
|
||||||
|
|> validate_required([
|
||||||
|
:title,
|
||||||
|
:begins_on,
|
||||||
|
:organizer_actor_id,
|
||||||
|
:url,
|
||||||
|
:uuid
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_tags(changeset, %{"tags" => tags}), do: put_assoc(changeset, :tags, tags)
|
||||||
|
defp put_tags(changeset, _), do: changeset
|
||||||
|
|
||||||
def can_event_be_managed_by(%Event{organizer_actor_id: organizer_actor_id}, actor_id)
|
def can_event_be_managed_by(%Event{organizer_actor_id: organizer_actor_id}, actor_id)
|
||||||
when organizer_actor_id == actor_id do
|
when organizer_actor_id == actor_id do
|
||||||
{:event_can_be_managed, true}
|
{:event_can_be_managed, true}
|
||||||
|
|
|
@ -227,11 +227,12 @@ defmodule Mobilizon.Events do
|
||||||
:tracks,
|
:tracks,
|
||||||
:tags,
|
:tags,
|
||||||
:participants,
|
:participants,
|
||||||
:physical_address
|
:physical_address,
|
||||||
|
:picture
|
||||||
])}
|
])}
|
||||||
|
|
||||||
err ->
|
_err ->
|
||||||
{:error, err}
|
{:error, :event_not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -435,7 +436,8 @@ defmodule Mobilizon.Events do
|
||||||
"""
|
"""
|
||||||
def update_event(%Event{} = event, attrs) do
|
def update_event(%Event{} = event, attrs) do
|
||||||
event
|
event
|
||||||
|> Event.changeset(attrs)
|
|> Repo.preload(:tags)
|
||||||
|
|> Event.update_changeset(attrs)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@ defmodule MobilizonWeb.API.Events do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
API for Events
|
API for Events
|
||||||
"""
|
"""
|
||||||
alias Mobilizon.Actors
|
alias Mobilizon.Events.Event
|
||||||
alias Mobilizon.Actors.Actor
|
|
||||||
alias Mobilizon.Service.ActivityPub
|
alias Mobilizon.Service.ActivityPub
|
||||||
alias Mobilizon.Service.ActivityPub.Utils, as: ActivityPubUtils
|
alias Mobilizon.Service.ActivityPub.Utils, as: ActivityPubUtils
|
||||||
alias MobilizonWeb.API.Utils
|
alias MobilizonWeb.API.Utils
|
||||||
|
@ -16,13 +15,13 @@ defmodule MobilizonWeb.API.Events do
|
||||||
with %{
|
with %{
|
||||||
title: title,
|
title: title,
|
||||||
physical_address: physical_address,
|
physical_address: physical_address,
|
||||||
visibility: visibility,
|
|
||||||
picture: picture,
|
picture: picture,
|
||||||
content_html: content_html,
|
content_html: content_html,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
to: to,
|
to: to,
|
||||||
cc: cc,
|
cc: cc,
|
||||||
begins_on: begins_on,
|
begins_on: begins_on,
|
||||||
|
ends_on: ends_on,
|
||||||
category: category,
|
category: category,
|
||||||
options: options
|
options: options
|
||||||
} <- prepare_args(args),
|
} <- prepare_args(args),
|
||||||
|
@ -34,7 +33,13 @@ defmodule MobilizonWeb.API.Events do
|
||||||
content_html,
|
content_html,
|
||||||
picture,
|
picture,
|
||||||
tags,
|
tags,
|
||||||
%{begins_on: begins_on, physical_address: physical_address, category: category, options: options}
|
%{
|
||||||
|
begins_on: begins_on,
|
||||||
|
ends_on: ends_on,
|
||||||
|
physical_address: physical_address,
|
||||||
|
category: category,
|
||||||
|
options: options
|
||||||
|
}
|
||||||
) do
|
) do
|
||||||
ActivityPub.create(%{
|
ActivityPub.create(%{
|
||||||
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
@ -48,30 +53,28 @@ defmodule MobilizonWeb.API.Events do
|
||||||
@doc """
|
@doc """
|
||||||
Update an event
|
Update an event
|
||||||
"""
|
"""
|
||||||
@spec update_event(map()) :: {:ok, Activity.t(), Event.t()} | any()
|
@spec update_event(map(), Event.t()) :: {:ok, Activity.t(), Event.t()} | any()
|
||||||
def update_event(
|
def update_event(
|
||||||
%{
|
%{
|
||||||
organizer_actor: organizer_actor,
|
organizer_actor: organizer_actor
|
||||||
event: event
|
} = args,
|
||||||
} = args
|
%Event{} = event
|
||||||
) do
|
) do
|
||||||
with %{
|
with args <- Map.put(args, :tags, Map.get(args, :tags, [])),
|
||||||
|
%{
|
||||||
title: title,
|
title: title,
|
||||||
physical_address: physical_address,
|
physical_address: physical_address,
|
||||||
visibility: visibility,
|
|
||||||
picture: picture,
|
picture: picture,
|
||||||
content_html: content_html,
|
content_html: content_html,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
to: to,
|
to: to,
|
||||||
cc: cc,
|
cc: cc,
|
||||||
begins_on: begins_on,
|
begins_on: begins_on,
|
||||||
|
ends_on: ends_on,
|
||||||
category: category,
|
category: category,
|
||||||
options: options
|
options: options
|
||||||
} <-
|
} <-
|
||||||
prepare_args(
|
prepare_args(Map.merge(event, args)),
|
||||||
args
|
|
||||||
|> update_args(event)
|
|
||||||
),
|
|
||||||
event <-
|
event <-
|
||||||
ActivityPubUtils.make_event_data(
|
ActivityPubUtils.make_event_data(
|
||||||
organizer_actor.url,
|
organizer_actor.url,
|
||||||
|
@ -82,34 +85,24 @@ defmodule MobilizonWeb.API.Events do
|
||||||
tags,
|
tags,
|
||||||
%{
|
%{
|
||||||
begins_on: begins_on,
|
begins_on: begins_on,
|
||||||
|
ends_on: ends_on,
|
||||||
physical_address: physical_address,
|
physical_address: physical_address,
|
||||||
category: Map.get(args, :category),
|
category: category,
|
||||||
options: options
|
options: options
|
||||||
}
|
},
|
||||||
|
event.uuid,
|
||||||
|
event.url
|
||||||
) do
|
) do
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
actor: organizer_actor,
|
actor: organizer_actor.url,
|
||||||
|
cc: [],
|
||||||
object: event,
|
object: event,
|
||||||
local: true
|
local: true
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_args(args, event) do
|
|
||||||
%{
|
|
||||||
title: Map.get(args, :title, event.title),
|
|
||||||
description: Map.get(args, :description, event.description),
|
|
||||||
tags: Map.get(args, :tags, event.tags),
|
|
||||||
physical_address: Map.get(args, :physical_address, event.physical_address),
|
|
||||||
visibility: Map.get(args, :visibility, event.visibility),
|
|
||||||
physical_address: Map.get(args, :physical_address, event.physical_address),
|
|
||||||
begins_on: Map.get(args, :begins_on, event.begins_on),
|
|
||||||
category: Map.get(args, :category, event.category),
|
|
||||||
options: Map.get(args, :options, event.options)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp prepare_args(
|
defp prepare_args(
|
||||||
%{
|
%{
|
||||||
organizer_actor: organizer_actor,
|
organizer_actor: organizer_actor,
|
||||||
|
@ -118,8 +111,7 @@ defmodule MobilizonWeb.API.Events do
|
||||||
options: options,
|
options: options,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
begins_on: begins_on,
|
begins_on: begins_on,
|
||||||
category: category,
|
category: category
|
||||||
options: options
|
|
||||||
} = args
|
} = args
|
||||||
) do
|
) do
|
||||||
with physical_address <- Map.get(args, :physical_address, nil),
|
with physical_address <- Map.get(args, :physical_address, nil),
|
||||||
|
@ -131,13 +123,13 @@ defmodule MobilizonWeb.API.Events do
|
||||||
%{
|
%{
|
||||||
title: title,
|
title: title,
|
||||||
physical_address: physical_address,
|
physical_address: physical_address,
|
||||||
visibility: visibility,
|
|
||||||
picture: picture,
|
picture: picture,
|
||||||
content_html: content_html,
|
content_html: content_html,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
to: to,
|
to: to,
|
||||||
cc: cc,
|
cc: cc,
|
||||||
begins_on: begins_on,
|
begins_on: begins_on,
|
||||||
|
ends_on: Map.get(args, :ends_on, nil),
|
||||||
category: category,
|
category: category,
|
||||||
options: options
|
options: options
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ defmodule MobilizonWeb.API.Groups do
|
||||||
preferred_username: title,
|
preferred_username: title,
|
||||||
summary: summary,
|
summary: summary,
|
||||||
creator_actor_id: creator_actor_id,
|
creator_actor_id: creator_actor_id,
|
||||||
avatar: avatar,
|
avatar: _avatar,
|
||||||
banner: banner
|
banner: _banner
|
||||||
} = args
|
} = args
|
||||||
) do
|
) do
|
||||||
with {:is_owned, true, actor} <- User.owns_actor(user, creator_actor_id),
|
with {:is_owned, true, actor} <- User.owns_actor(user, creator_actor_id),
|
||||||
{:existing_group, nil} <- {:existing_group, Actors.get_group_by_title(title)},
|
|
||||||
title <- String.trim(title),
|
title <- String.trim(title),
|
||||||
|
{:existing_group, nil} <- {:existing_group, Actors.get_group_by_title(title)},
|
||||||
visibility <- Map.get(args, :visibility, :public),
|
visibility <- Map.get(args, :visibility, :public),
|
||||||
{content_html, tags, to, cc} <-
|
{content_html, tags, to, cc} <-
|
||||||
Utils.prepare_content(actor, summary, visibility, [], nil),
|
Utils.prepare_content(actor, summary, visibility, [], nil),
|
||||||
|
|
|
@ -193,7 +193,9 @@ defmodule MobilizonWeb.Resolvers.Event do
|
||||||
}
|
}
|
||||||
} = _resolution
|
} = _resolution
|
||||||
) do
|
) do
|
||||||
with {:is_owned, true, organizer_actor} <- User.owns_actor(user, organizer_actor_id),
|
# See https://github.com/absinthe-graphql/absinthe/issues/490
|
||||||
|
with args <- Map.put(args, :options, args[:options] || %{}),
|
||||||
|
{:is_owned, true, organizer_actor} <- User.owns_actor(user, organizer_actor_id),
|
||||||
{:ok, args} <- save_attached_picture(args),
|
{:ok, args} <- save_attached_picture(args),
|
||||||
{:ok, args} <- save_physical_address(args),
|
{:ok, args} <- save_physical_address(args),
|
||||||
args_with_organizer <- Map.put(args, :organizer_actor, organizer_actor),
|
args_with_organizer <- Map.put(args, :organizer_actor, organizer_actor),
|
||||||
|
@ -230,10 +232,13 @@ defmodule MobilizonWeb.Resolvers.Event do
|
||||||
}
|
}
|
||||||
} = _resolution
|
} = _resolution
|
||||||
) do
|
) do
|
||||||
with {:ok, %Event{} = event} <- Mobilizon.Events.get_event(event_id),
|
# See https://github.com/absinthe-graphql/absinthe/issues/490
|
||||||
|
with args <- Map.put(args, :options, args[:options] || %{}),
|
||||||
|
{:ok, %Event{} = event} <- Mobilizon.Events.get_event_full(event_id),
|
||||||
{:is_owned, true, organizer_actor} <- User.owns_actor(user, event.organizer_actor_id),
|
{:is_owned, true, organizer_actor} <- User.owns_actor(user, event.organizer_actor_id),
|
||||||
{:ok, args} <- save_attached_picture(args),
|
{:ok, args} <- save_attached_picture(args),
|
||||||
{:ok, args} <- save_physical_address(args),
|
{:ok, args} <- save_physical_address(args),
|
||||||
|
args <- Map.put(args, :organizer_actor, organizer_actor),
|
||||||
{
|
{
|
||||||
:ok,
|
:ok,
|
||||||
%Activity{
|
%Activity{
|
||||||
|
@ -243,11 +248,14 @@ defmodule MobilizonWeb.Resolvers.Event do
|
||||||
},
|
},
|
||||||
%Event{} = event
|
%Event{} = event
|
||||||
} <-
|
} <-
|
||||||
MobilizonWeb.API.Events.update_event(args) do
|
MobilizonWeb.API.Events.update_event(args, event) do
|
||||||
{:ok, event}
|
{:ok, event}
|
||||||
else
|
else
|
||||||
{:error, :event_not_found} ->
|
{:error, :event_not_found} ->
|
||||||
{:error, "Event not found"}
|
{:error, "Event not found"}
|
||||||
|
|
||||||
|
{:is_owned, _} ->
|
||||||
|
{:error, "User doesn't own actor"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -229,14 +229,14 @@ defmodule MobilizonWeb.Schema.EventType do
|
||||||
arg(:organizer_actor_id, non_null(:id))
|
arg(:organizer_actor_id, non_null(:id))
|
||||||
arg(:category, :string, default_value: "meeting")
|
arg(:category, :string, default_value: "meeting")
|
||||||
arg(:physical_address, :address_input)
|
arg(:physical_address, :address_input)
|
||||||
arg(:options, :event_options_input, default_value: %{})
|
arg(:options, :event_options_input)
|
||||||
|
|
||||||
resolve(&Event.create_event/3)
|
resolve(&Event.create_event/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Update an event"
|
@desc "Update an event"
|
||||||
field :update_event, type: :event do
|
field :update_event, type: :event do
|
||||||
arg(:event_id, non_null(:integer))
|
arg(:event_id, non_null(:id))
|
||||||
|
|
||||||
arg(:title, :string)
|
arg(:title, :string)
|
||||||
arg(:description, :string)
|
arg(:description, :string)
|
||||||
|
@ -246,6 +246,7 @@ defmodule MobilizonWeb.Schema.EventType do
|
||||||
arg(:status, :integer)
|
arg(:status, :integer)
|
||||||
arg(:public, :boolean)
|
arg(:public, :boolean)
|
||||||
arg(:visibility, :event_visibility)
|
arg(:visibility, :event_visibility)
|
||||||
|
arg(:organizer_actor_id, :id)
|
||||||
|
|
||||||
arg(:tags, list_of(:string), description: "The list of tags associated to the event")
|
arg(:tags, list_of(:string), description: "The list of tags associated to the event")
|
||||||
|
|
||||||
|
@ -259,6 +260,7 @@ defmodule MobilizonWeb.Schema.EventType do
|
||||||
arg(:phone_address, :string)
|
arg(:phone_address, :string)
|
||||||
arg(:category, :string)
|
arg(:category, :string)
|
||||||
arg(:physical_address, :address_input)
|
arg(:physical_address, :address_input)
|
||||||
|
arg(:options, :event_options_input)
|
||||||
|
|
||||||
resolve(&Event.update_event/3)
|
resolve(&Event.update_event/3)
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,21 +39,16 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||||
@doc """
|
@doc """
|
||||||
Wraps an object into an activity
|
Wraps an object into an activity
|
||||||
"""
|
"""
|
||||||
@spec create_activity(map(), boolean()) :: {:ok, %Activity{}} | {:error, any()}
|
@spec create_activity(map(), boolean()) :: {:ok, %Activity{}}
|
||||||
def create_activity(map, local \\ true) when is_map(map) do
|
def create_activity(map, local \\ true) when is_map(map) do
|
||||||
with map <- lazy_put_activity_defaults(map) do
|
with map <- lazy_put_activity_defaults(map) do
|
||||||
activity = %Activity{
|
{:ok,
|
||||||
data: map,
|
%Activity{
|
||||||
local: local,
|
data: map,
|
||||||
actor: map["actor"],
|
local: local,
|
||||||
recipients: get_recipients(map)
|
actor: map["actor"],
|
||||||
}
|
recipients: get_recipients(map)
|
||||||
|
}}
|
||||||
# Notification.create_notifications(activity)
|
|
||||||
# stream_out(activity)
|
|
||||||
{:ok, activity}
|
|
||||||
else
|
|
||||||
error -> {:error, error}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -196,7 +191,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||||
"object" => object
|
"object" => object
|
||||||
},
|
},
|
||||||
{:ok, activity} <- create_activity(data, local),
|
{:ok, activity} <- create_activity(data, local),
|
||||||
{:ok, object} <- insert_full_object(data),
|
{:ok, object} <- update_object(object["id"], data),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity, object}
|
{:ok, activity, object}
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,12 +15,30 @@ defmodule Mobilizon.Service.ActivityPub.Converters.Actor do
|
||||||
@impl Converter
|
@impl Converter
|
||||||
@spec as_to_model_data(map()) :: map()
|
@spec as_to_model_data(map()) :: map()
|
||||||
def as_to_model_data(object) do
|
def as_to_model_data(object) do
|
||||||
|
avatar =
|
||||||
|
object["icon"]["url"] &&
|
||||||
|
%{
|
||||||
|
"name" => object["icon"]["name"] || "avatar",
|
||||||
|
"url" => object["icon"]["url"]
|
||||||
|
}
|
||||||
|
|
||||||
|
banner =
|
||||||
|
object["image"]["url"] &&
|
||||||
|
%{
|
||||||
|
"name" => object["image"]["name"] || "banner",
|
||||||
|
"url" => object["image"]["url"]
|
||||||
|
}
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"type" => String.to_existing_atom(object["type"]),
|
"type" => String.to_existing_atom(object["type"]),
|
||||||
"preferred_username" => object["preferred_username"],
|
"preferred_username" => object["preferredUsername"],
|
||||||
"summary" => object["summary"],
|
"summary" => object["summary"],
|
||||||
"url" => object["url"],
|
"url" => object["url"],
|
||||||
"name" => object["name"]
|
"name" => object["name"],
|
||||||
|
"avatar" => avatar,
|
||||||
|
"banner" => banner,
|
||||||
|
"keys" => object["publicKey"]["publicKeyPem"],
|
||||||
|
"manually_approves_followers" => object["manuallyApprovesFollowers"]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ defmodule Mobilizon.Service.ActivityPub.Converters.Event do
|
||||||
"organizer_actor_id" => actor_id,
|
"organizer_actor_id" => actor_id,
|
||||||
"picture_id" => picture_id,
|
"picture_id" => picture_id,
|
||||||
"begins_on" => object["startTime"],
|
"begins_on" => object["startTime"],
|
||||||
|
"ends_on" => object["endTime"],
|
||||||
"category" => object["category"],
|
"category" => object["category"],
|
||||||
"url" => object["id"],
|
"url" => object["id"],
|
||||||
"uuid" => object["uuid"],
|
"uuid" => object["uuid"],
|
||||||
|
@ -173,7 +174,8 @@ defmodule Mobilizon.Service.ActivityPub.Converters.Event do
|
||||||
"startTime" => event.begins_on |> date_to_string(),
|
"startTime" => event.begins_on |> date_to_string(),
|
||||||
"endTime" => event.ends_on |> date_to_string(),
|
"endTime" => event.ends_on |> date_to_string(),
|
||||||
"tag" => event.tags |> build_tags(),
|
"tag" => event.tags |> build_tags(),
|
||||||
"id" => event.url
|
"id" => event.url,
|
||||||
|
"url" => event.url
|
||||||
}
|
}
|
||||||
|
|
||||||
res =
|
res =
|
||||||
|
|
|
@ -295,19 +295,15 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
||||||
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => _actor_id} =
|
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => _actor_id} =
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
when object_type in ["Person", "Application", "Service", "Organization"] do
|
when object_type in ["Person", "Group", "Application", "Service", "Organization"] do
|
||||||
case Actors.get_actor_by_url(object["id"]) do
|
case Actors.get_actor_by_url(object["id"]) do
|
||||||
{:ok, %Actor{url: url}} ->
|
{:ok, %Actor{url: actor_url}} ->
|
||||||
{:ok, new_actor_data} = ActivityPub.actor_data_from_actor_object(object)
|
|
||||||
|
|
||||||
Actors.insert_or_update_actor(new_actor_data)
|
|
||||||
|
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
local: false,
|
local: false,
|
||||||
to: data["to"] || [],
|
to: data["to"] || [],
|
||||||
cc: data["cc"] || [],
|
cc: data["cc"] || [],
|
||||||
object: object,
|
object: object,
|
||||||
actor: url
|
actor: actor_url
|
||||||
})
|
})
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
|
@ -316,6 +312,28 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{"type" => "Update", "object" => %{"type" => "Event"} = object, "actor" => actor} =
|
||||||
|
_update
|
||||||
|
) do
|
||||||
|
with {:ok, %{"actor" => existing_organizer_actor_url} = _existing_event_data} <-
|
||||||
|
fetch_obj_helper_as_activity_streams(object),
|
||||||
|
{:ok, %Actor{url: actor_url}} <- actor |> Utils.get_url() |> Actors.get_actor_by_url(),
|
||||||
|
true <- Utils.get_url(existing_organizer_actor_url) == actor_url do
|
||||||
|
ActivityPub.update(%{
|
||||||
|
local: false,
|
||||||
|
to: object["to"] || [],
|
||||||
|
cc: object["cc"] || [],
|
||||||
|
object: object,
|
||||||
|
actor: actor_url
|
||||||
|
})
|
||||||
|
else
|
||||||
|
e ->
|
||||||
|
Logger.debug(inspect(e))
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
|
|
|
@ -29,6 +29,8 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
alias MobilizonWeb.Router.Helpers, as: Routes
|
alias MobilizonWeb.Router.Helpers, as: Routes
|
||||||
alias MobilizonWeb.Endpoint
|
alias MobilizonWeb.Endpoint
|
||||||
|
|
||||||
|
@actor_types ["Group", "Person", "Application"]
|
||||||
|
|
||||||
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
||||||
# so figure out what the actor's URI is based on what we have.
|
# so figure out what the actor's URI is based on what we have.
|
||||||
def get_url(%{"id" => id}), do: id
|
def get_url(%{"id" => id}), do: id
|
||||||
|
@ -119,7 +121,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
@doc """
|
@doc """
|
||||||
Inserts a full object if it is contained in an activity.
|
Inserts a full object if it is contained in an activity.
|
||||||
"""
|
"""
|
||||||
def insert_full_object(%{"object" => %{"type" => "Event"} = object_data})
|
def insert_full_object(%{"object" => %{"type" => "Event"} = object_data, "type" => "Create"})
|
||||||
when is_map(object_data) do
|
when is_map(object_data) do
|
||||||
with {:ok, object_data} <-
|
with {:ok, object_data} <-
|
||||||
Converters.Event.as_to_model_data(object_data),
|
Converters.Event.as_to_model_data(object_data),
|
||||||
|
@ -128,7 +130,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert_full_object(%{"object" => %{"type" => "Group"} = object_data})
|
def insert_full_object(%{"object" => %{"type" => "Group"} = object_data, "type" => "Create"})
|
||||||
when is_map(object_data) do
|
when is_map(object_data) do
|
||||||
with object_data <-
|
with object_data <-
|
||||||
Map.put(object_data, "preferred_username", object_data["preferredUsername"]),
|
Map.put(object_data, "preferred_username", object_data["preferredUsername"]),
|
||||||
|
@ -140,7 +142,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
@doc """
|
@doc """
|
||||||
Inserts a full object if it is contained in an activity.
|
Inserts a full object if it is contained in an activity.
|
||||||
"""
|
"""
|
||||||
def insert_full_object(%{"object" => %{"type" => "Note"} = object_data})
|
def insert_full_object(%{"object" => %{"type" => "Note"} = object_data, "type" => "Create"})
|
||||||
when is_map(object_data) do
|
when is_map(object_data) do
|
||||||
with data <- Converters.Comment.as_to_model_data(object_data),
|
with data <- Converters.Comment.as_to_model_data(object_data),
|
||||||
{:ok, %Comment{} = comment} <- Events.create_comment(data) do
|
{:ok, %Comment{} = comment} <- Events.create_comment(data) do
|
||||||
|
@ -177,6 +179,39 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
|
|
||||||
def insert_full_object(_), do: {:ok, nil}
|
def insert_full_object(_), do: {:ok, nil}
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Update an object
|
||||||
|
"""
|
||||||
|
@spec update_object(struct(), map()) :: {:ok, struct()} | any()
|
||||||
|
def update_object(object, object_data)
|
||||||
|
|
||||||
|
def update_object(event_url, %{
|
||||||
|
"object" => %{"type" => "Event"} = object_data,
|
||||||
|
"type" => "Update"
|
||||||
|
})
|
||||||
|
when is_map(object_data) do
|
||||||
|
with {:event_not_found, %Event{} = event} <-
|
||||||
|
{:event_not_found, Events.get_event_by_url(event_url)},
|
||||||
|
{:ok, object_data} <- Converters.Event.as_to_model_data(object_data),
|
||||||
|
{:ok, %Event{} = event} <- Events.update_event(event, object_data) do
|
||||||
|
{:ok, event}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_object(actor_url, %{
|
||||||
|
"object" => %{"type" => type_actor} = object_data,
|
||||||
|
"type" => "Update"
|
||||||
|
})
|
||||||
|
when is_map(object_data) and type_actor in @actor_types do
|
||||||
|
with {:ok, %Actor{} = actor} <- Actors.get_actor_by_url(actor_url),
|
||||||
|
object_data <- Converters.Actor.as_to_model_data(object_data),
|
||||||
|
{:ok, %Actor{} = actor} <- Actors.update_actor(actor, object_data) do
|
||||||
|
{:ok, actor}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_object(_, _), do: {:ok, nil}
|
||||||
|
|
||||||
#### Like-related helpers
|
#### Like-related helpers
|
||||||
|
|
||||||
# @doc """
|
# @doc """
|
||||||
|
@ -264,7 +299,8 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
String.t(),
|
String.t(),
|
||||||
map(),
|
map(),
|
||||||
list(),
|
list(),
|
||||||
map()
|
map(),
|
||||||
|
String.t()
|
||||||
) :: map()
|
) :: map()
|
||||||
def make_event_data(
|
def make_event_data(
|
||||||
actor,
|
actor,
|
||||||
|
@ -273,10 +309,12 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
content_html,
|
content_html,
|
||||||
picture \\ nil,
|
picture \\ nil,
|
||||||
tags \\ [],
|
tags \\ [],
|
||||||
metadata \\ %{}
|
metadata \\ %{},
|
||||||
|
uuid \\ nil,
|
||||||
|
url \\ nil
|
||||||
) do
|
) do
|
||||||
Logger.debug("Making event data")
|
Logger.debug("Making event data")
|
||||||
uuid = Ecto.UUID.generate()
|
uuid = uuid || Ecto.UUID.generate()
|
||||||
|
|
||||||
res = %{
|
res = %{
|
||||||
"type" => "Event",
|
"type" => "Event",
|
||||||
|
@ -285,9 +323,10 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
"content" => content_html,
|
"content" => content_html,
|
||||||
"name" => title,
|
"name" => title,
|
||||||
"startTime" => metadata.begins_on,
|
"startTime" => metadata.begins_on,
|
||||||
|
"endTime" => metadata.ends_on,
|
||||||
"category" => metadata.category,
|
"category" => metadata.category,
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"id" => Routes.page_url(Endpoint, :event, uuid),
|
"id" => url || Routes.page_url(Endpoint, :event, uuid),
|
||||||
"uuid" => uuid,
|
"uuid" => uuid,
|
||||||
"tag" =>
|
"tag" =>
|
||||||
tags |> Enum.uniq() |> Enum.map(fn tag -> %{"type" => "Hashtag", "name" => "##{tag}"} end)
|
tags |> Enum.uniq() |> Enum.map(fn tag -> %{"type" => "Hashtag", "name" => "##{tag}"} end)
|
||||||
|
@ -505,7 +544,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
||||||
activity_id,
|
activity_id,
|
||||||
public
|
public
|
||||||
)
|
)
|
||||||
when type in ["Group", "Person", "Application"] do
|
when type in @actor_types do
|
||||||
do_make_announce_data(actor_url, actor_followers_url, url, url, activity_id, public)
|
do_make_announce_data(actor_url, actor_followers_url, url, url, activity_id, public)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# source: http://localhost:4000/api
|
# source: http://localhost:4000/api
|
||||||
# timestamp: Mon Sep 02 2019 16:41:17 GMT+0200 (GMT+02:00)
|
# timestamp: Thu Sep 05 2019 13:00:10 GMT+0200 (GMT+02:00)
|
||||||
|
|
||||||
schema {
|
schema {
|
||||||
query: RootQueryType
|
query: RootQueryType
|
||||||
|
@ -440,7 +440,7 @@ enum EventVisibility {
|
||||||
"""Visible only to people members of the group or followers of the person"""
|
"""Visible only to people members of the group or followers of the person"""
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
|
||||||
"""Publically listed and federated. Can be shared."""
|
"""Publicly listed and federated. Can be shared."""
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
|
||||||
"""Visible only to people with the link - or invited"""
|
"""Visible only to people with the link - or invited"""
|
||||||
|
@ -823,7 +823,7 @@ type RootMutationType {
|
||||||
"""Create an event"""
|
"""Create an event"""
|
||||||
createEvent(
|
createEvent(
|
||||||
beginsOn: DateTime!
|
beginsOn: DateTime!
|
||||||
category: String
|
category: String = "meeting"
|
||||||
description: String!
|
description: String!
|
||||||
endsOn: DateTime
|
endsOn: DateTime
|
||||||
onlineAddress: String
|
onlineAddress: String
|
||||||
|
@ -852,11 +852,6 @@ type RootMutationType {
|
||||||
|
|
||||||
"""Create a group"""
|
"""Create a group"""
|
||||||
createGroup(
|
createGroup(
|
||||||
"""
|
|
||||||
The actor's username which will be the admin (otherwise user's default one)
|
|
||||||
"""
|
|
||||||
adminActorUsername: String
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The avatar for the group, either as an object or directly the ID of an existing Picture
|
The avatar for the group, either as an object or directly the ID of an existing Picture
|
||||||
"""
|
"""
|
||||||
|
@ -867,14 +862,17 @@ type RootMutationType {
|
||||||
"""
|
"""
|
||||||
banner: PictureInput
|
banner: PictureInput
|
||||||
|
|
||||||
"""The summary for the group"""
|
"""The identity that creates the group"""
|
||||||
description: String = ""
|
creatorActorId: Int!
|
||||||
|
|
||||||
"""The displayed name for the group"""
|
"""The displayed name for the group"""
|
||||||
name: String
|
name: String
|
||||||
|
|
||||||
"""The name for the group"""
|
"""The name for the group"""
|
||||||
preferredUsername: String!
|
preferredUsername: String!
|
||||||
|
|
||||||
|
"""The summary for the group"""
|
||||||
|
summary: String = ""
|
||||||
): Group
|
): Group
|
||||||
|
|
||||||
"""Create a new person for user"""
|
"""Create a new person for user"""
|
||||||
|
@ -969,6 +967,34 @@ type RootMutationType {
|
||||||
"""Send a link through email to reset user password"""
|
"""Send a link through email to reset user password"""
|
||||||
sendResetPassword(email: String!, locale: String = "en"): String
|
sendResetPassword(email: String!, locale: String = "en"): String
|
||||||
|
|
||||||
|
"""Update an event"""
|
||||||
|
updateEvent(
|
||||||
|
beginsOn: DateTime
|
||||||
|
category: String
|
||||||
|
description: String
|
||||||
|
endsOn: DateTime
|
||||||
|
eventId: ID!
|
||||||
|
onlineAddress: String
|
||||||
|
options: EventOptionsInput
|
||||||
|
organizerActorId: ID
|
||||||
|
phoneAddress: String
|
||||||
|
physicalAddress: AddressInput
|
||||||
|
|
||||||
|
"""
|
||||||
|
The picture for the event, either as an object or directly the ID of an existing Picture
|
||||||
|
"""
|
||||||
|
picture: PictureInput
|
||||||
|
public: Boolean
|
||||||
|
publishAt: DateTime
|
||||||
|
state: Int
|
||||||
|
status: Int
|
||||||
|
|
||||||
|
"""The list of tags associated to the event"""
|
||||||
|
tags: [String]
|
||||||
|
title: String
|
||||||
|
visibility: EventVisibility
|
||||||
|
): Event
|
||||||
|
|
||||||
"""Update an identity"""
|
"""Update an identity"""
|
||||||
updatePerson(
|
updatePerson(
|
||||||
"""
|
"""
|
||||||
|
|
26
test/fixtures/mastodon-update.json
vendored
26
test/fixtures/mastodon-update.json
vendored
|
@ -1,24 +1,24 @@
|
||||||
{
|
{
|
||||||
"type": "Update",
|
"type": "Update",
|
||||||
"object": {
|
"object": {
|
||||||
"url": "http://mastodon.example.org/@gargron",
|
"url": "https://framapiaf.org/@framasoft",
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
"summary": "<p>Some bio</p>",
|
"summary": "<p>Some bio</p>",
|
||||||
"publicKey": {
|
"publicKey": {
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gs3VnQf6am3R+CeBV4H\nlfI1HZTNRIBHgvFszRZkCERbRgEWMu+P+I6/7GJC5H5jhVQ60z4MmXcyHOGmYMK/\n5XyuHQz7V2Ssu1AxLfRN5Biq1ayb0+DT/E7QxNXDJPqSTnstZ6C7zKH/uAETqg3l\nBonjCQWyds+IYbQYxf5Sp3yhvQ80lMwHML3DaNCMlXWLoOnrOX5/yK5+dedesg2\n/HIvGk+HEt36vm6hoH7bwPuEkgA++ACqwjXRe5Mta7i3eilHxFaF8XIrJFARV0t\nqOu4GID/jG6oA+swIWndGrtR2QRJIt9QIBFfK3HG5M0koZbY1eTqwNFRHFL3xaD\nUQIDAQAB\n-----END PUBLIC KEY-----\n",
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gs3VnQf6am3R+CeBV4H\nlfI1HZTNRIBHgvFszRZkCERbRgEWMu+P+I6/7GJC5H5jhVQ60z4MmXcyHOGmYMK/\n5XyuHQz7V2Ssu1AxLfRN5Biq1ayb0+DT/E7QxNXDJPqSTnstZ6C7zKH/uAETqg3l\nBonjCQWyds+IYbQYxf5Sp3yhvQ80lMwHML3DaNCMlXWLoOnrOX5/yK5+dedesg2\n/HIvGk+HEt36vm6hoH7bwPuEkgA++ACqwjXRe5Mta7i3eilHxFaF8XIrJFARV0t\nqOu4GID/jG6oA+swIWndGrtR2QRJIt9QIBFfK3HG5M0koZbY1eTqwNFRHFL3xaD\nUQIDAQAB\n-----END PUBLIC KEY-----\n",
|
||||||
"owner": "http://mastodon.example.org/users/gargron",
|
"owner": "https://framapiaf.org/users/framasoft",
|
||||||
"id": "http://mastodon.example.org/users/gargron#main-key"
|
"id": "https://framapiaf.org/users/framasoft#main-key"
|
||||||
},
|
},
|
||||||
"preferredUsername": "gargron",
|
"preferredUsername": "framasoft",
|
||||||
"outbox": "http://mastodon.example.org/users/gargron/outbox",
|
"outbox": "https://framapiaf.org/users/framasoft/outbox",
|
||||||
"name": "gargle",
|
"name": "nextsoft",
|
||||||
"manuallyApprovesFollowers": false,
|
"manuallyApprovesFollowers": false,
|
||||||
"inbox": "http://mastodon.example.org/users/gargron/inbox",
|
"inbox": "https://framapiaf.org/users/framasoft/inbox",
|
||||||
"id": "http://mastodon.example.org/users/gargron",
|
"id": "https://framapiaf.org/users/framasoft",
|
||||||
"following": "http://mastodon.example.org/users/gargron/following",
|
"following": "https://framapiaf.org/users/framasoft/following",
|
||||||
"followers": "http://mastodon.example.org/users/gargron/followers",
|
"followers": "https://framapiaf.org/users/framasoft/followers",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"sharedInbox": "http://mastodon.example.org/inbox"
|
"sharedInbox": "https://framapiaf.org/inbox"
|
||||||
},
|
},
|
||||||
"icon":{
|
"icon":{
|
||||||
"type":"Image",
|
"type":"Image",
|
||||||
|
@ -31,8 +31,8 @@
|
||||||
"url":"https://files.mastodon.social/accounts/headers/000/000/001/original/c91b871f294ea63e.png"
|
"url":"https://files.mastodon.social/accounts/headers/000/000/001/original/c91b871f294ea63e.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": "http://mastodon.example.org/users/gargron#updates/1519563538",
|
"id": "https://framapiaf.org/users/gargron#updates/1519563538",
|
||||||
"actor": "http://mastodon.example.org/users/gargron",
|
"actor": "https://framapiaf.org/users/framasoft",
|
||||||
"@context": [
|
"@context": [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"https://w3id.org/security/v1",
|
"https://w3id.org/security/v1",
|
||||||
|
|
|
@ -270,10 +270,8 @@ defmodule Mobilizon.ActorsTest do
|
||||||
assert %Actor{} = actor
|
assert %Actor{} = actor
|
||||||
assert actor.summary == "some updated description"
|
assert actor.summary == "some updated description"
|
||||||
assert actor.name == "some updated name"
|
assert actor.name == "some updated name"
|
||||||
assert actor.domain == "some updated domain"
|
|
||||||
assert actor.keys == "some updated keys"
|
assert actor.keys == "some updated keys"
|
||||||
refute actor.suspended
|
refute actor.suspended
|
||||||
assert actor.preferred_username == "some updated username"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_actor/2 with valid data updates the actor and it's media files", %{
|
test "update_actor/2 with valid data updates the actor and it's media files", %{
|
||||||
|
@ -310,10 +308,8 @@ defmodule Mobilizon.ActorsTest do
|
||||||
assert %Actor{} = actor
|
assert %Actor{} = actor
|
||||||
assert actor.summary == "some updated description"
|
assert actor.summary == "some updated description"
|
||||||
assert actor.name == "some updated name"
|
assert actor.name == "some updated name"
|
||||||
assert actor.domain == "some updated domain"
|
|
||||||
assert actor.keys == "some updated keys"
|
assert actor.keys == "some updated keys"
|
||||||
refute actor.suspended
|
refute actor.suspended
|
||||||
assert actor.preferred_username == "some updated username"
|
|
||||||
|
|
||||||
refute File.exists?(
|
refute File.exists?(
|
||||||
Mobilizon.CommonConfig.get!([MobilizonWeb.Uploaders.Local, :uploads]) <>
|
Mobilizon.CommonConfig.get!([MobilizonWeb.Uploaders.Local, :uploads]) <>
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
|
||||||
import Mobilizon.Factory
|
import Mobilizon.Factory
|
||||||
|
|
||||||
alias Mobilizon.Events
|
alias Mobilizon.Events
|
||||||
|
alias Mobilizon.Events.Event
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Actors
|
alias Mobilizon.Actors
|
||||||
alias Mobilizon.Service.HTTPSignatures.Signature
|
alias Mobilizon.Service.HTTPSignatures.Signature
|
||||||
|
@ -137,11 +138,14 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "update" do
|
describe "update" do
|
||||||
|
@updated_actor_summary "This is an updated actor"
|
||||||
|
|
||||||
test "it creates an update activity with the new actor data" do
|
test "it creates an update activity with the new actor data" do
|
||||||
actor = insert(:actor)
|
actor = insert(:actor)
|
||||||
actor_data = MobilizonWeb.ActivityPub.ActorView.render("actor.json", %{actor: actor})
|
actor_data = MobilizonWeb.ActivityPub.ActorView.render("actor.json", %{actor: actor})
|
||||||
|
actor_data = Map.put(actor_data, "summary", @updated_actor_summary)
|
||||||
|
|
||||||
{:ok, update, _} =
|
{:ok, update, updated_actor} =
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
actor: actor_data["url"],
|
actor: actor_data["url"],
|
||||||
to: [actor.url <> "/followers"],
|
to: [actor.url <> "/followers"],
|
||||||
|
@ -153,6 +157,42 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
|
||||||
assert update.data["to"] == [actor.url <> "/followers"]
|
assert update.data["to"] == [actor.url <> "/followers"]
|
||||||
assert update.data["object"]["id"] == actor_data["id"]
|
assert update.data["object"]["id"] == actor_data["id"]
|
||||||
assert update.data["object"]["type"] == actor_data["type"]
|
assert update.data["object"]["type"] == actor_data["type"]
|
||||||
|
assert update.data["object"]["summary"] == @updated_actor_summary
|
||||||
|
|
||||||
|
refute updated_actor.summary == actor.summary
|
||||||
|
|
||||||
|
{:ok, %Actor{} = database_actor} = Mobilizon.Actors.get_actor_by_url(actor.url)
|
||||||
|
assert database_actor.summary == @updated_actor_summary
|
||||||
|
assert database_actor.preferred_username == actor.preferred_username
|
||||||
|
end
|
||||||
|
|
||||||
|
@updated_start_time DateTime.utc_now() |> DateTime.truncate(:second)
|
||||||
|
|
||||||
|
test "it creates an update activity with the new event data" do
|
||||||
|
actor = insert(:actor)
|
||||||
|
event = insert(:event, organizer_actor: actor)
|
||||||
|
event_data = Mobilizon.Service.ActivityPub.Converters.Event.model_to_as(event)
|
||||||
|
event_data = Map.put(event_data, "startTime", @updated_start_time)
|
||||||
|
|
||||||
|
{:ok, update, updated_event} =
|
||||||
|
ActivityPub.update(%{
|
||||||
|
actor: actor.url,
|
||||||
|
to: [actor.url <> "/followers"],
|
||||||
|
cc: [],
|
||||||
|
object: event_data
|
||||||
|
})
|
||||||
|
|
||||||
|
assert update.data["actor"] == actor.url
|
||||||
|
assert update.data["to"] == [actor.url <> "/followers"]
|
||||||
|
assert update.data["object"]["id"] == event_data["id"]
|
||||||
|
assert update.data["object"]["type"] == event_data["type"]
|
||||||
|
assert update.data["object"]["startTime"] == @updated_start_time
|
||||||
|
|
||||||
|
refute updated_event.begins_on == event.begins_on
|
||||||
|
|
||||||
|
%Event{} = database_event = Mobilizon.Events.get_event_by_url(event.url)
|
||||||
|
assert database_event.begins_on == @updated_start_time
|
||||||
|
assert database_event.title == event.title
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ defmodule Mobilizon.Service.ActivityPub.Converters.ActorTest do
|
||||||
actor =
|
actor =
|
||||||
ActorConverter.as_to_model_data(%{
|
ActorConverter.as_to_model_data(%{
|
||||||
"type" => "Person",
|
"type" => "Person",
|
||||||
"preferred_username" => "test_account"
|
"preferredUsername" => "test_account"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert actor["type"] == :Person
|
assert actor["type"] == :Person
|
||||||
|
|
|
@ -330,7 +330,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
|
||||||
assert data["object"] == comment.url
|
assert data["object"] == comment.url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming update activities" do
|
test "it works for incoming update activities on actors" do
|
||||||
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
|
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
|
||||||
|
@ -349,11 +349,37 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
|
||||||
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(update_data)
|
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(update_data)
|
||||||
|
|
||||||
{:ok, %Actor{} = actor} = Actors.get_actor_by_url(data["actor"])
|
{:ok, %Actor{} = actor} = Actors.get_actor_by_url(data["actor"])
|
||||||
assert actor.name == "gargle"
|
assert actor.name == "nextsoft"
|
||||||
|
|
||||||
assert actor.summary == "<p>Some bio</p>"
|
assert actor.summary == "<p>Some bio</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works for incoming update activities on events" do
|
||||||
|
data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
|
||||||
|
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
object =
|
||||||
|
data["object"]
|
||||||
|
|> Map.put("actor", data["actor"])
|
||||||
|
|> Map.put("name", "My updated event")
|
||||||
|
|> Map.put("id", data["object"]["id"])
|
||||||
|
|> Map.put("type", "Event")
|
||||||
|
|
||||||
|
update_data =
|
||||||
|
update_data
|
||||||
|
|> Map.put("actor", data["actor"])
|
||||||
|
|> Map.put("object", object)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(update_data)
|
||||||
|
|
||||||
|
%Event{} = event = Events.get_event_by_url(data["object"]["id"])
|
||||||
|
assert event.title == "My updated event"
|
||||||
|
|
||||||
|
assert event.description == data["object"]["content"]
|
||||||
|
end
|
||||||
|
|
||||||
# test "it works for incoming update activities which lock the account" do
|
# test "it works for incoming update activities which lock the account" do
|
||||||
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
|
|
@ -418,12 +418,12 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
||||||
assert json_response(res, 200)["data"]["createEvent"]["picture"]["url"]
|
assert json_response(res, 200)["data"]["createEvent"]["picture"]["url"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_event/3 should check the event exists", %{conn: conn, actor: actor, user: user} do
|
test "update_event/3 should check the event exists", %{conn: conn, actor: _actor, user: user} do
|
||||||
mutation = """
|
mutation = """
|
||||||
mutation {
|
mutation {
|
||||||
updateEvent(
|
updateEvent(
|
||||||
event_id: 45,
|
event_id: 45,
|
||||||
title: "my event updated",
|
title: "my event updated"
|
||||||
) {
|
) {
|
||||||
title,
|
title,
|
||||||
uuid,
|
uuid,
|
||||||
|
@ -445,7 +445,7 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
||||||
|
|
||||||
test "update_event/3 should check the user is an administrator", %{
|
test "update_event/3 should check the user is an administrator", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
actor: actor,
|
actor: _actor,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
event = insert(:event)
|
event = insert(:event)
|
||||||
|
@ -454,6 +454,7 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
||||||
mutation {
|
mutation {
|
||||||
updateEvent(
|
updateEvent(
|
||||||
title: "my event updated",
|
title: "my event updated",
|
||||||
|
event_id: #{event.id}
|
||||||
) {
|
) {
|
||||||
title,
|
title,
|
||||||
uuid,
|
uuid,
|
||||||
|
@ -470,11 +471,11 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
assert hd(json_response(res, 200)["errors"])["message"] == "User doesn't own actor"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_event/3 updates an event", %{conn: conn, actor: actor, user: user} do
|
test "update_event/3 updates an event", %{conn: conn, actor: actor, user: user} do
|
||||||
event = insert(:event)
|
event = insert(:event, organizer_actor: actor)
|
||||||
|
|
||||||
begins_on = DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
|
begins_on = DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
|
||||||
|
|
||||||
|
@ -484,12 +485,14 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
||||||
title: "my event updated",
|
title: "my event updated",
|
||||||
description: "description updated",
|
description: "description updated",
|
||||||
begins_on: "#{begins_on}",
|
begins_on: "#{begins_on}",
|
||||||
|
event_id: #{event.id},
|
||||||
organizer_actor_id: "#{actor.id}",
|
organizer_actor_id: "#{actor.id}",
|
||||||
category: "birthday",
|
category: "birthday",
|
||||||
tags: ["tag1_updated", "tag2_updated"]
|
tags: ["tag1_updated", "tag2_updated"]
|
||||||
) {
|
) {
|
||||||
title,
|
title,
|
||||||
uuid,
|
uuid,
|
||||||
|
url,
|
||||||
tags {
|
tags {
|
||||||
title,
|
title,
|
||||||
slug
|
slug
|
||||||
|
@ -505,13 +508,76 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
assert json_response(res, 200)["errors"] == nil
|
||||||
assert json_response(res, 200)["data"]["updateEvent"]["title"] == "my event updated"
|
assert json_response(res, 200)["data"]["updateEvent"]["title"] == "my event updated"
|
||||||
|
assert json_response(res, 200)["data"]["updateEvent"]["uuid"] == event.uuid
|
||||||
|
assert json_response(res, 200)["data"]["updateEvent"]["url"] == event.url
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["createEvent"]["tags"] == [
|
assert json_response(res, 200)["data"]["updateEvent"]["tags"] == [
|
||||||
%{"slug" => "tag1_updated", "title" => "tag1_updated"},
|
%{"slug" => "tag1-updated", "title" => "tag1_updated"},
|
||||||
%{"slug" => "tag2_updated", "title" => "tag2_updated"}
|
%{"slug" => "tag2-updated", "title" => "tag2_updated"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "update_event/3 updates an event with a new picture", %{
|
||||||
|
conn: conn,
|
||||||
|
actor: actor,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
event = insert(:event, organizer_actor: actor)
|
||||||
|
|
||||||
|
begins_on = DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
|
||||||
|
|
||||||
|
mutation = """
|
||||||
|
mutation {
|
||||||
|
updateEvent(
|
||||||
|
title: "my event updated",
|
||||||
|
description: "description updated",
|
||||||
|
begins_on: "#{begins_on}",
|
||||||
|
event_id: #{event.id},
|
||||||
|
organizer_actor_id: "#{actor.id}",
|
||||||
|
category: "birthday",
|
||||||
|
picture: {
|
||||||
|
picture: {
|
||||||
|
name: "picture for my event",
|
||||||
|
alt: "A very sunny landscape",
|
||||||
|
file: "event.jpg",
|
||||||
|
actor_id: "#{actor.id}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
title,
|
||||||
|
uuid,
|
||||||
|
url,
|
||||||
|
picture {
|
||||||
|
name,
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
map = %{
|
||||||
|
"query" => mutation,
|
||||||
|
"event.jpg" => %Plug.Upload{
|
||||||
|
path: "test/fixtures/picture.png",
|
||||||
|
filename: "event.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api", map)
|
||||||
|
|
||||||
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["updateEvent"]["title"] == "my event updated"
|
||||||
|
assert json_response(res, 200)["data"]["updateEvent"]["uuid"] == event.uuid
|
||||||
|
assert json_response(res, 200)["data"]["updateEvent"]["url"] == event.url
|
||||||
|
|
||||||
|
assert json_response(res, 200)["data"]["updateEvent"]["picture"]["name"] ==
|
||||||
|
"picture for my event"
|
||||||
|
end
|
||||||
|
|
||||||
test "list_events/3 returns events", context do
|
test "list_events/3 returns events", context do
|
||||||
event = insert(:event)
|
event = insert(:event)
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ defmodule Mobilizon.Factory do
|
||||||
|
|
||||||
def tag_factory do
|
def tag_factory do
|
||||||
%Mobilizon.Events.Tag{
|
%Mobilizon.Events.Tag{
|
||||||
title: "MyTag",
|
title: sequence("MyTag"),
|
||||||
slug: sequence("MyTag")
|
slug: sequence("my-tag")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue