Merge branch 'fixes' into 'master'

Bunch of little fixes

Closes #274, #230, #232, #311, #316 et #315

See merge request framasoft/mobilizon!322
This commit is contained in:
Thomas Citharel 2019-11-18 19:13:51 +01:00
commit 792a2deddb
19 changed files with 1629 additions and 1266 deletions

View File

@ -55,7 +55,7 @@
"@vue/cli-plugin-typescript": "^4.0.3",
"@vue/cli-plugin-unit-mocha": "^4.0.3",
"@vue/cli-service": "^4.0.3",
"@vue/eslint-config-typescript": "^4.0.0",
"@vue/eslint-config-typescript": "^5.0.0",
"@vue/test-utils": "^1.0.0-beta.29",
"apollo-link-error": "^1.1.12",
"chai": "^4.2.0",
@ -67,8 +67,8 @@
"tslint": "^5.20.0",
"tslint-config-airbnb": "^5.11.2",
"typescript": "^3.6.3",
"vue-cli-plugin-styleguidist": "^3.25.0",
"vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
"vue-cli-plugin-styleguidist": "^4.0.1",
"vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0",
"vue-i18n-extract": "^1.0.2",
"vue-svg-inline-loader": "^1.3.0",
"vue-template-compiler": "^2.6.10",

View File

@ -387,6 +387,7 @@ export default class EditorComponent extends Vue {
placement: 'top-start',
inertia: true,
duration: [400, 200],
// @ts-ignore for some reason
showOnInit: true,
arrow: true,
arrowType: 'round',

View File

@ -70,7 +70,7 @@ query {
}`;
export const CURRENT_ACTOR_CLIENT = gql`
query {
query currentActor {
currentActor @client {
id,
avatar {

View File

@ -289,6 +289,7 @@ export const EDIT_EVENT = gql`
$picture: PictureInput,
$onlineAddress: String,
$phoneAddress: String,
$organizerActorId: ID,
$category: String,
$physicalAddress: AddressInput,
$options: EventOptionsInput,
@ -307,6 +308,7 @@ export const EDIT_EVENT = gql`
picture: $picture,
onlineAddress: $onlineAddress,
phoneAddress: $phoneAddress,
organizerActorId: $organizerActorId,
category: $category,
physicalAddress: $physicalAddress
options: $options,

View File

@ -0,0 +1,35 @@
import { Component, Mixins, Vue } from 'vue-property-decorator';
import { Person } from '@/types/actor';
@Component
export default class IdentityEditionMixin extends Mixins(Vue) {
identity = new Person();
oldDisplayName: string | null = null;
autoUpdateUsername(newDisplayName: string | null) {
const oldUsername = IdentityEditionMixin.convertToUsername(this.oldDisplayName);
if (this.identity.preferredUsername === oldUsername) {
this.identity.preferredUsername = IdentityEditionMixin.convertToUsername(newDisplayName);
}
this.oldDisplayName = newDisplayName;
}
private static convertToUsername(value: string | null) {
if (!value) return '';
// https://stackoverflow.com/a/37511463
return value.toLocaleLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/ /g, '_')
.replace(/[^a-z0-9_]/g, '')
;
}
validateUsername() {
return this.identity.preferredUsername === IdentityEditionMixin.convertToUsername(this.identity.preferredUsername);
}
}

View File

@ -10,7 +10,7 @@
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { IActor } from '@/types/actor';
import IdentityPicker from './IdentityPicker.vue';
@ -22,6 +22,11 @@ export default class IdentityPickerWrapper extends Vue {
isComponentModalActive: boolean = false;
currentIdentity: IActor = this.value;
@Watch('value')
updateCurrentActor(value) {
this.currentIdentity = value;
}
relay(identity: IActor) {
this.currentIdentity = identity;
this.$emit('input', identity);
@ -36,4 +41,4 @@ export default class IdentityPickerWrapper extends Vue {
height: 1.5em;
vertical-align: text-bottom;
}
</style>
</style>

View File

@ -9,6 +9,10 @@
{{ $t('To achieve your registration, please create a first identity profile.')}}
</b-message>
<form v-if="!validationSent" @submit.prevent="submit">
<b-field :label="$t('Display name')">
<b-input aria-required="true" required v-model="identity.name" @input="autoUpdateUsername($event)"/>
</b-field>
<b-field
:label="$t('Username')"
:type="errors.preferred_username ? 'is-danger' : null"
@ -19,7 +23,7 @@
aria-required="true"
required
expanded
v-model="person.preferredUsername"
v-model="identity.preferredUsername"
/>
<p class="control">
<span class="button is-static">@{{ host }}</span>
@ -27,12 +31,8 @@
</b-field>
</b-field>
<b-field :label="$t('Displayed name')">
<b-input v-model="person.name"/>
</b-field>
<b-field :label="$t('Description')">
<b-input type="textarea" v-model="person.summary"/>
<b-input type="textarea" v-model="identity.summary"/>
</b-field>
<p class="control has-text-centered">
@ -45,7 +45,7 @@
<div v-if="validationSent && !userAlreadyActivated">
<b-message title="Success" type="is-success" closable="false">
<h2 class="title">
{{ $t('Your account is nearly ready, {username}', { username: person.preferredUsername }) }}
{{ $t('Your account is nearly ready, {username}', { username: identity.preferredUsername }) }}
</h2>
<p>
{{ $t('A validation email was sent to {email}', { email }) }}
@ -61,22 +61,22 @@
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { IPerson, Person } from '@/types/actor';
import { Component, Prop } from 'vue-property-decorator';
import { IPerson } from '@/types/actor';
import { IDENTITIES, REGISTER_PERSON } from '@/graphql/actor';
import { MOBILIZON_INSTANCE_HOST } from '@/api/_entrypoint';
import { RouteName } from '@/router';
import { changeIdentity } from '@/utils/auth';
import { ICurrentUser } from '@/types/current-user.model';
import { mixins } from 'vue-class-component';
import identityEditionMixin from '@/mixins/identityEdition';
@Component
export default class Register extends Vue {
export default class Register extends mixins(identityEditionMixin) {
@Prop({ type: String, required: true }) email!: string;
@Prop({ type: Boolean, required: false, default: false }) userAlreadyActivated!: boolean;
host?: string = MOBILIZON_INSTANCE_HOST;
person: IPerson = new Person();
errors: object = {};
validationSent: boolean = false;
sendingValidation: boolean = false;
@ -94,7 +94,7 @@ export default class Register extends Vue {
this.errors = {};
const { data } = await this.$apollo.mutate<{ registerPerson: IPerson }>({
mutation: REGISTER_PERSON,
variables: Object.assign({ email: this.email }, this.person),
variables: Object.assign({ email: this.email }, this.identity),
update: (store, { data }) => {
if (this.userAlreadyActivated) {
const identitiesData = store.readQuery<{ identities: IPerson[] }>({ query: IDENTITIES });

View File

@ -7,13 +7,13 @@
<picture-upload v-model="avatarFile" class="picture-upload"></picture-upload>
<b-field :label="$t('Display name')">
<b-field horizontal :label="$t('Display name')">
<b-input aria-required="true" required v-model="identity.name" @input="autoUpdateUsername($event)"/>
</b-field>
<b-field :label="$t('Username')">
<b-field>
<b-input aria-required="true" required v-model="identity.preferredUsername" :disabled="isUpdate"/>
<b-field horizontal custom-class="username-field" expanded :label="$t('Username')" :message="message">
<b-field expanded>
<b-input aria-required="true" required v-model="identity.preferredUsername" :disabled="isUpdate" :use-html5-validation="!isUpdate" pattern="[a-z0-9_]+"/>
<p class="control">
<span class="button is-static">@{{ getInstanceHost() }}</span>
@ -21,7 +21,7 @@
</b-field>
</b-field>
<b-field :label="$t('Description')">
<b-field horizontal :label="$t('Description')">
<b-input type="textarea" aria-required="false" v-model="identity.summary"/>
</b-field>
@ -77,10 +77,14 @@
cursor: pointer;
margin-top: 15px;
}
.username-field + .field {
margin-bottom: 0;
}
</style>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { Component, Prop, Watch } from 'vue-property-decorator';
import {
CREATE_PERSON,
CURRENT_ACTOR_CLIENT,
@ -96,6 +100,8 @@ import { Dialog } from 'buefy/dist/components/dialog';
import { RouteName } from '@/router';
import { buildFileFromIPicture, buildFileVariable, readFileAsync } from '@/utils/image';
import { changeIdentity } from '@/utils/auth';
import { mixins } from 'vue-class-component';
import identityEditionMixin from '@/mixins/identityEdition';
@Component({
components: {
@ -108,18 +114,21 @@ import { changeIdentity } from '@/utils/auth';
},
},
})
export default class EditIdentity extends Vue {
export default class EditIdentity extends mixins(identityEditionMixin) {
@Prop({ type: Boolean }) isUpdate!: boolean;
errors: string[] = [];
identityName!: string | undefined;
avatarFile: File | null = null;
identity = new Person();
private oldDisplayName: string | null = null;
private currentActor: IPerson | null = null;
get message() {
if (this.isUpdate) return null;
return this.$t('Only alphanumeric characters and underscores are supported.');
}
@Watch('isUpdate')
async isUpdateChanged () {
this.resetFields();
@ -153,16 +162,6 @@ export default class EditIdentity extends Vue {
return this.createIdentity();
}
autoUpdateUsername(newDisplayName: string | null) {
const oldUsername = this.convertToUsername(this.oldDisplayName);
if (this.identity.preferredUsername === oldUsername) {
this.identity.preferredUsername = this.convertToUsername(newDisplayName);
}
this.oldDisplayName = newDisplayName;
}
/**
* Delete an identity
*/
@ -309,18 +308,6 @@ export default class EditIdentity extends Vue {
}
}
private convertToUsername(value: string | null) {
if (!value) return '';
// https://stackoverflow.com/a/37511463
return value.toLocaleLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/ /g, '_')
.replace(/[^a-z0-9._]/g, '')
;
}
private async buildVariables() {
const avatarObj = buildFileVariable(this.avatarFile, 'avatar', `${this.identity.preferredUsername}'s avatar`);
const res = Object.assign({}, this.identity, avatarObj);

File diff suppressed because it is too large Load Diff

View File

@ -203,14 +203,9 @@ defmodule Mobilizon.Actors.Actor do
actor
|> cast(attrs, @attrs)
|> build_urls()
|> cast_embed(:avatar)
|> cast_embed(:banner)
|> common_changeset()
|> unique_username_validator()
|> validate_required(@required_attrs)
|> unique_constraint(:preferred_username,
name: :actors_preferred_username_domain_type_index
)
|> unique_constraint(:url, name: :actors_url_index)
end
@doc false
@ -218,13 +213,8 @@ defmodule Mobilizon.Actors.Actor do
def update_changeset(%__MODULE__{} = actor, attrs) do
actor
|> cast(attrs, @update_attrs)
|> cast_embed(:avatar)
|> cast_embed(:banner)
|> common_changeset()
|> validate_required(@update_required_attrs)
|> unique_constraint(:preferred_username,
name: :actors_preferred_username_domain_type_index
)
|> unique_constraint(:url, name: :actors_url_index)
end
@doc """
@ -235,13 +225,8 @@ defmodule Mobilizon.Actors.Actor do
actor
|> cast(attrs, @registration_attrs)
|> build_urls()
|> cast_embed(:avatar)
|> cast_embed(:banner)
|> common_changeset()
|> unique_username_validator()
|> unique_constraint(:preferred_username,
name: :actors_preferred_username_domain_type_index
)
|> unique_constraint(:url, name: :actors_url_index)
|> validate_required(@registration_required_attrs)
end
@ -254,13 +239,8 @@ defmodule Mobilizon.Actors.Actor do
%__MODULE__{}
|> cast(attrs, @remote_actor_creation_attrs)
|> validate_required(@remote_actor_creation_required_attrs)
|> cast_embed(:avatar)
|> cast_embed(:banner)
|> common_changeset()
|> unique_username_validator()
|> unique_constraint(:preferred_username,
name: :actors_preferred_username_domain_type_index
)
|> unique_constraint(:url, name: :actors_url_index)
|> validate_length(:summary, max: 5000)
|> validate_length(:preferred_username, max: 100)
@ -269,6 +249,16 @@ defmodule Mobilizon.Actors.Actor do
changeset
end
@spec common_changeset(Ecto.Changeset.t()) :: Ecto.Changeset.t()
defp common_changeset(%Ecto.Changeset{} = changeset) do
changeset
|> cast_embed(:avatar)
|> cast_embed(:banner)
|> unique_constraint(:url, name: :actors_url_index)
|> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_type_index)
|> validate_format(:preferred_username, ~r/[a-z0-9_]+/)
end
@doc """
Changeset for relay creation.
"""

View File

@ -128,7 +128,6 @@ defmodule Mobilizon.Events.Event do
|> common_changeset(attrs)
|> put_creator_if_published(:create)
|> validate_required(@required_attrs)
|> validate_lengths()
end
@doc false
@ -139,7 +138,6 @@ defmodule Mobilizon.Events.Event do
|> common_changeset(attrs)
|> put_creator_if_published(:update)
|> validate_required(@update_required_attrs)
|> validate_lengths()
end
@spec common_changeset(Changeset.t(), map) :: Changeset.t()
@ -149,6 +147,8 @@ defmodule Mobilizon.Events.Event do
|> put_tags(attrs)
|> put_address(attrs)
|> put_picture(attrs)
|> validate_lengths()
|> validate_end_time()
end
@spec validate_lengths(Changeset.t()) :: Changeset.t()
@ -162,6 +162,20 @@ defmodule Mobilizon.Events.Event do
|> validate_length(:slug, min: 3, max: 200)
end
defp validate_end_time(%Changeset{} = changeset) do
case fetch_field(changeset, :begins_on) do
{_, begins_on} ->
validate_change(changeset, :ends_on, fn :ends_on, ends_on ->
if begins_on > ends_on,
do: [ends_on: "ends_on cannot be set before begins_on"],
else: []
end)
:error ->
changeset
end
end
@doc """
Checks whether an event can be managed.
"""

View File

@ -275,6 +275,9 @@ defmodule MobilizonWeb.Resolvers.Event do
{:is_owned, nil} ->
{:error, "Organizer actor id is not owned by the user"}
{:error, _, %Ecto.Changeset{} = error, _} ->
{:error, error}
{:error, %Ecto.Changeset{} = error} ->
{:error, error}
end
@ -295,8 +298,9 @@ defmodule MobilizonWeb.Resolvers.Event do
# See https://github.com/absinthe-graphql/absinthe/issues/490
with args <- Map.put(args, :options, args[:options] || %{}),
{:ok, %Event{} = event} <- Events.get_event_with_preload(event_id),
organizer_actor_id <- args |> Map.get(:organizer_actor_id, event.organizer_actor_id),
{:is_owned, %Actor{} = organizer_actor} <-
User.owns_actor(user, event.organizer_actor_id),
User.owns_actor(user, organizer_actor_id),
args <- Map.put(args, :organizer_actor, organizer_actor),
{:ok, %Activity{data: %{"object" => %{"type" => "Event"}}}, %Event{} = event} <-
MobilizonWeb.API.Events.update_event(args, event) do
@ -307,6 +311,9 @@ defmodule MobilizonWeb.Resolvers.Event do
{:is_owned, nil} ->
{:error, "User doesn't own actor"}
{:error, _, %Ecto.Changeset{} = error, _} ->
{:error, error}
end
end

View File

@ -299,6 +299,7 @@ defmodule MobilizonWeb.Schema.EventType do
arg(:online_address, :string)
arg(:phone_address, :string)
arg(:organizer_actor_id, :id)
arg(:category, :string)
arg(:physical_address, :address_input)
arg(:options, :event_options_input)

View File

@ -93,8 +93,6 @@ defmodule Mobilizon.Service.ActivityPub do
{:ok, Actors.get_actor_by_url!(actor_url, true)}
e ->
require Logger
Logger.error(inspect(e))
{:error, e}
end
end
@ -135,14 +133,13 @@ defmodule Mobilizon.Service.ActivityPub do
Logger.debug("creating an activity")
Logger.debug(inspect(args))
{:ok, entity, create_data} =
case type do
:event -> create_event(args, additional)
:comment -> create_comment(args, additional)
:group -> create_group(args, additional)
end
with {:ok, activity} <- create_activity(create_data, local),
with {:ok, entity, create_data} <-
(case type do
:event -> create_event(args, additional)
:comment -> create_comment(args, additional)
:group -> create_group(args, additional)
end),
{:ok, activity} <- create_activity(create_data, local),
:ok <- maybe_federate(activity) do
{:ok, activity, entity}
else
@ -167,13 +164,12 @@ defmodule Mobilizon.Service.ActivityPub do
Logger.debug("updating an activity")
Logger.debug(inspect(args))
{:ok, entity, update_data} =
case type do
:event -> update_event(old_entity, args, additional)
:actor -> update_actor(old_entity, args, additional)
end
with {:ok, activity} <- create_activity(update_data, local),
with {:ok, entity, update_data} <-
(case type do
:event -> update_event(old_entity, args, additional)
:actor -> update_actor(old_entity, args, additional)
end),
{:ok, activity} <- create_activity(update_data, local),
:ok <- maybe_federate(activity) do
{:ok, activity, entity}
else

View File

@ -339,8 +339,7 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
ActivityPub.update(:event, old_event, object_data, false) do
{:ok, activity, new_event}
else
e ->
Logger.error(inspect(e))
_e ->
:error
end
end
@ -442,8 +441,7 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
:error
e ->
Logger.error(inspect(e))
_e ->
:error
end
end

View File

@ -100,7 +100,7 @@ defmodule Mobilizon.Mixfile do
{:ex_cldr_dates_times, "~> 2.0"},
{:ex_optimizer, "~> 0.1"},
{:progress_bar, "~> 2.0"},
{:oban, "~> 0.10"},
{:oban, "~> 0.11.1"},
# Dev and test dependencies
{:phoenix_live_reload, "~> 1.2", only: [:dev, :e2e]},
{:ex_machina, "~> 2.3", only: [:dev, :test]},

View File

@ -14,7 +14,7 @@
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"cldr_utils": {:hex, :cldr_utils, "2.5.0", "2a15b82b5b56bba99b897ff5801a5b1dcbce425b6430445e97d024a9999afb03", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
"comeonin": {:hex, :comeonin, "5.1.2", "fbbbbbfcf0f0e9900c0336d16c8d462edf838ba1759577e29cc5fbd7c28a4540", [:mix], [], "hexpm"},
"comeonin": {:hex, :comeonin, "5.1.3", "4c9880ed348cc0330c74086b4383ffb0b5a599aa603416497b7374c168cae340", [:mix], [], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"cors_plug": {:hex, :cors_plug, "2.0.0", "238ddb479f92b38f6dc1ae44b8d81f0387f9519101a6da442d543ab70ee0e482", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
@ -34,8 +34,8 @@
"erlex": {:hex, :erlex, "0.2.5", "e51132f2f472e13d606d808f0574508eeea2030d487fc002b46ad97e738b0510", [:mix], [], "hexpm"},
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
"ex_cldr": {:hex, :ex_cldr, "2.11.1", "8e500a88b68be01a97315bea394d593e74e1035c0a5dc6f0b8281423857ec9b3", [:mix], [{:cldr_utils, "~> 2.3", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.4.0", "c7adf1e752b0cbad6f565246a583f1d056ad05fdc0c7fb8b66d498d1b381225f", [:mix], [{:ex_cldr, "~> 2.8", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.4", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_units, "~> 2.0", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.3.0", "bffae489416b8b05d4683403263f5d62aae17de70c24ff915a533541fea514de", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.5.1", "136bc95c87791bfc558d19182e53790263981484085ffa23ceb9122ad52001c9", [:mix], [{:ex_cldr, "~> 2.8", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.4", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_units, "~> 2.0", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.4.1", "a8e8330a6d0712b8bb34c5e3759311da1d53fa8bebef163d72615f0ea60c0738", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ex_cldr_dates_times": {:hex, :ex_cldr_dates_times, "2.2.3", "4a82f6af48f55c92c0d28be066e5b2451e807e3f49246d08845af664fd7cb712", [:mix], [{:ex_cldr, "~> 2.8", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_calendars, "~> 1.2", [hex: :ex_cldr_calendars, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.6", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.9.0", "ffba3ba8cfa41194e57cc38f0cc78f83956ce5c1822b1e4d5f64394cb927ba2c", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.11", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.3", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ex_crypto": {:hex, :ex_crypto, "0.10.0", "af600a89b784b36613a989da6e998c1b200ff1214c3cfbaf8deca4aa2f0a1739", [:mix], [{:poison, ">= 2.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
@ -64,7 +64,7 @@
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"http_sign": {:hex, :http_sign, "0.1.1", "b16edb83aa282892f3271f9a048c155e772bf36e15700ab93901484c55f8dd10", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"http_signatures": {:git, "https://git.pleroma.social/pleroma/http_signatures.git", "293d77bb6f4a67ac8bde1428735c3b42f22cbb30", [ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"]},
"httpoison": {:hex, :httpoison, "1.6.1", "2ce5bf6e535cd0ab02e905ba8c276580bab80052c5c549f53ddea52d72e81f33", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"icalendar": {:git, "https://github.com/tcitworld/icalendar.git", "bd08e872c125f70a87c3ac7d87ea2f22a5577059", []},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
@ -79,16 +79,16 @@
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
"mimetype_parser": {:hex, :mimetype_parser, "0.1.3", "628ac9fe56aa7edcedb534d68397dd66674ab82493c8ebe39acb9a19b666099d", [:mix], [], "hexpm"},
"mix_test_watch": {:hex, :mix_test_watch, "1.0.1", "ae6fc45bbc80b826046fb84208df4b06035e10fae6d44d0cb48c5a2f92ee2e1d", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"},
"mix_test_watch": {:hex, :mix_test_watch, "1.0.2", "34900184cbbbc6b6ed616ed3a8ea9b791f9fd2088419352a6d3200525637f785", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"},
"mmdb2_decoder": {:hex, :mmdb2_decoder, "1.1.0", "2e2347521bb3bf6b81b9ee58d3be2199cb68ea42dcbafcd0d8eb40214d2844cf", [:mix], [], "hexpm"},
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
"mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"mogrify": {:hex, :mogrify, "0.7.3", "1494ee739f6e90de158dec4d4edee2d854d2f2d06a522e943f996ae176bca53d", [:mix], [], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
"oban": {:hex, :oban, "0.10.1", "c2ca0a413fb66b21dd58c7cb67fd80b01e9d79a0fbb28573f110057e7fdc3807", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.2", "1d71150d5293d703a9c38d4329da57d3935faed2031d64bc19e77b654ef2d177", [:mix], [], "hexpm"},
"oban": {:hex, :oban, "0.11.1", "e34964fad7f188c2c3d006485601a8897e537f7b88a31928be2833ae1cab59af", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.10", "619e4a545505f562cd294df52294372d012823f4fd9d34a6657a8b242898c255", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.11", "d112c862f6959f98e6e915c3b76c7a87ca3efd075850c8daa7c3c7a609014b0d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},

View File

@ -1,5 +1,5 @@
# source: http://localhost:4000/api
# timestamp: Fri Nov 08 2019 17:20:47 GMT+0100 (Central European Standard Time)
# timestamp: Mon Nov 18 2019 16:00:35 GMT+0100 (Central European Standard Time)
schema {
query: RootQueryType
@ -1081,6 +1081,7 @@ type RootMutationType {
joinOptions: EventJoinOptions = FREE
onlineAddress: String
options: EventOptionsInput
organizerActorId: ID
phoneAddress: String
physicalAddress: AddressInput
@ -1178,7 +1179,7 @@ type RootQueryType {
reports(limit: Int = 10, page: Int = 1, status: ReportStatus = OPEN): [Report]
"""Reverse geocode coordinates"""
reverseGeocode(latitude: Float!, longitude: Float!): [Address]
reverseGeocode(latitude: Float!, locale: String = "en", longitude: Float!, zoom: Int = 15): [Address]
"""Search for an address"""
searchAddress(limit: Int = 10, locale: String = "en", page: Int = 1, query: String!): [Address]

View File

@ -95,6 +95,40 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
"Organizer actor id is not owned by the user"
end
test "create_event/3 should check that end time is after start time", %{
conn: conn,
actor: actor,
user: user
} do
begins_on = DateTime.utc_now() |> DateTime.truncate(:second)
ends_on = Timex.shift(begins_on, hours: -2)
mutation = """
mutation {
createEvent(
title: "come to my event",
description: "it will be fine",
begins_on: "#{DateTime.to_iso8601(begins_on)}",
ends_on: "#{DateTime.to_iso8601(ends_on)}",
organizer_actor_id: "#{actor.id}",
category: "birthday"
) {
id,
title,
uuid
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"ends_on cannot be set before begins_on"
end
test "create_event/3 creates an event", %{conn: conn, actor: actor, user: user} do
mutation = """
mutation {
@ -661,6 +695,39 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
assert hd(json_response(res, 200)["errors"])["message"] == "User doesn't own actor"
end
test "update_event/3 should check end time is after the beginning time", %{
conn: conn,
actor: actor,
user: user
} do
event = insert(:event, organizer_actor: actor)
mutation = """
mutation {
updateEvent(
title: "my event updated",
ends_on: "#{Timex.shift(event.begins_on, hours: -2)}",
event_id: #{event.id}
) {
title,
uuid,
tags {
title,
slug
}
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"ends_on cannot be set before begins_on"
end
test "update_event/3 updates an event", %{conn: conn, actor: actor, user: user} do
event = insert(:event, organizer_actor: actor)
_creator = insert(:participant, event: event, actor: actor, role: :creator)