Merge branch 'feature/config-endpoint' into 'master'

Feature/config endpoint

Closes #81

See merge request framasoft/mobilizon!104
This commit is contained in:
Thomas Citharel 2019-03-22 15:48:21 +01:00
commit 5d8f1297b1
14 changed files with 161 additions and 15 deletions

View File

@ -12,7 +12,7 @@ config :mobilizon,
config :mobilizon, :instance, config :mobilizon, :instance,
name: System.get_env("MOBILIZON_INSTANCE_NAME") || "Localhost", name: System.get_env("MOBILIZON_INSTANCE_NAME") || "Localhost",
version: "1.0.0-dev", version: "1.0.0-dev",
registrations_open: true registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN") || false
config :mime, :types, %{ config :mime, :types, %{
"application/activity+json" => ["activity-json"], "application/activity+json" => ["activity-json"],

View File

@ -1,5 +1,9 @@
use Mix.Config use Mix.Config
config :mobilizon, :instance,
name: "Test instance",
registrations_open: true
# We don't run a server during test. If one is required, # We don't run a server during test. If one is required,
# you can enable the server option below. # you can enable the server option below.
config :mobilizon, MobilizonWeb.Endpoint, config :mobilizon, MobilizonWeb.Endpoint,

View File

@ -18,7 +18,7 @@
<div class="navbar-end"> <div class="navbar-end">
<div class="navbar-item"> <div class="navbar-item">
<div class="buttons"> <div class="buttons">
<router-link class="button is-primary" v-if="!currentUser.id" :to="{ name: 'Register' }"> <router-link class="button is-primary" v-if="!currentUser.id && config && config.registrationsOpen" :to="{ name: 'Register' }">
<strong> <strong>
<translate>Sign up</translate> <translate>Sign up</translate>
</strong> </strong>
@ -51,6 +51,8 @@ import { deleteUserData } from '@/utils/auth';
import { LOGGED_PERSON } from '@/graphql/actor'; import { LOGGED_PERSON } from '@/graphql/actor';
import { IActor, IPerson } from '@/types/actor.model'; import { IActor, IPerson } from '@/types/actor.model';
import { RouteName } from '@/router'; import { RouteName } from '@/router';
import { CONFIG } from '@/graphql/config';
import { IConfig } from '@/types/config.model';
@Component({ @Component({
apollo: { apollo: {
@ -71,7 +73,10 @@ import { RouteName } from '@/router';
loggedPerson: { loggedPerson: {
query: LOGGED_PERSON, query: LOGGED_PERSON,
}, },
}, config: {
query: CONFIG,
}
}
}) })
export default class NavBar extends Vue { export default class NavBar extends Vue {
notifications = [ notifications = [
@ -83,6 +88,7 @@ export default class NavBar extends Vue {
searchText: string | null = null; searchText: string | null = null;
searchSelect = null; searchSelect = null;
loggedPerson!: IPerson; loggedPerson!: IPerson;
config!: IConfig;
get items() { get items() {
return this.search.map(searchEntry => { return this.search.map(searchEntry => {

10
js/src/graphql/config.ts Normal file
View File

@ -0,0 +1,10 @@
import gql from 'graphql-tag';
export const CONFIG = gql`
query {
config {
name,
registrationsOpen
}
}
`;

View File

@ -0,0 +1,5 @@
export interface IConfig {
name: string,
registrationsOpen: boolean,
}

View File

@ -37,7 +37,7 @@
<translate>Forgot your password ?</translate> <translate>Forgot your password ?</translate>
</router-link> </router-link>
</div> </div>
<div class="control"> <div class="control" v-if="config && config.registrationsOpen">
<router-link <router-link
class="button is-text" class="button is-text"
:to="{ name: 'Register', params: { default_email: credentials.email, default_password: credentials.password }}" :to="{ name: 'Register', params: { default_email: credentials.email, default_password: credentials.password }}"
@ -61,12 +61,21 @@ import { ILogin } from '@/types/login.model';
import { UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user'; import { UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user';
import { onLogin } from '@/vue-apollo'; import { onLogin } from '@/vue-apollo';
import { RouteName } from '@/router'; import { RouteName } from '@/router';
import { IConfig } from '@/types/config.model';
import { CONFIG } from '@/graphql/config';
@Component @Component({
apollo: {
config: {
query: CONFIG
}
}
})
export default class Login extends Vue { export default class Login extends Vue {
@Prop({ type: String, required: false, default: '' }) email!: string; @Prop({ type: String, required: false, default: '' }) email!: string;
@Prop({ type: String, required: false, default: '' }) password!: string; @Prop({ type: String, required: false, default: '' }) password!: string;
config!: IConfig;
credentials = { credentials = {
email: '', email: '',
password: '', password: '',

View File

@ -0,0 +1,20 @@
defmodule Mobilizon.CommonConfig do
@moduledoc """
Instance configuration wrapper
"""
def registrations_open?() do
instance_config()
|> get_in([:registrations_open])
|> to_bool
end
def instance_name() do
instance_config()
|> get_in([:name])
end
defp instance_config(), do: Application.get_env(:mobilizon, :instance)
defp to_bool(v), do: v == true or v == "true" or v == "True"
end

View File

@ -146,13 +146,8 @@ defmodule Mobilizon.Users.User do
end end
end end
def is_confirmed(%User{confirmed_at: nil} = _user) do def is_confirmed(%User{confirmed_at: nil} = _user), do: {:error, :unconfirmed}
{:error, :unconfirmed} def is_confirmed(%User{} = user), do: {:ok, user}
end
def is_confirmed(%User{} = user) do
{:ok, user}
end
def owns_actor(%User{actors: actors}, actor_id) do def owns_actor(%User{actors: actors}, actor_id) do
case Enum.find(actors, fn a -> a.id == actor_id end) do case Enum.find(actors, fn a -> a.id == actor_id end) do

View File

@ -0,0 +1,14 @@
defmodule MobilizonWeb.Resolvers.Config do
@moduledoc """
Handles the config-related GraphQL calls
"""
import Mobilizon.CommonConfig
@doc """
Get config
"""
def get_config(_parent, _params, _context) do
{:ok, %{name: instance_name(), registrations_open: registrations_open?()}}
end
end

View File

@ -3,6 +3,7 @@ defmodule MobilizonWeb.Resolvers.User do
Handles the user-related GraphQL calls Handles the user-related GraphQL calls
""" """
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.CommonConfig
alias Mobilizon.Users.User alias Mobilizon.Users.User
alias Mobilizon.{Actors, Users} alias Mobilizon.{Actors, Users}
alias Mobilizon.Service.Users.{ResetPassword, Activation} alias Mobilizon.Service.Users.{ResetPassword, Activation}
@ -64,15 +65,23 @@ defmodule MobilizonWeb.Resolvers.User do
end end
@doc """ @doc """
Register an user : Register an user:
- check registrations are enabled
- create the user - create the user
- send a validation email to the user - send a validation email to the user
""" """
@spec create_user(any(), map(), any()) :: tuple() @spec create_user(any(), map(), any()) :: tuple()
def create_user(_parent, args, _resolution) do def create_user(_parent, args, _resolution) do
with {:ok, %User{} = user} <- Users.register(args) do with {:registrations_open, true} <- {:registrations_open, CommonConfig.registrations_open?()},
{:ok, %User{} = user} <- Users.register(args) do
Activation.send_confirmation_email(user) Activation.send_confirmation_email(user)
{:ok, user} {:ok, user}
else
{:registrations_open, false} ->
{:error, "Registrations are not enabled"}
err ->
err
end end
end end

View File

@ -18,6 +18,7 @@ defmodule MobilizonWeb.Schema do
import_types(MobilizonWeb.Schema.Actors.PersonType) import_types(MobilizonWeb.Schema.Actors.PersonType)
import_types(MobilizonWeb.Schema.Actors.GroupType) import_types(MobilizonWeb.Schema.Actors.GroupType)
import_types(MobilizonWeb.Schema.CommentType) import_types(MobilizonWeb.Schema.CommentType)
import_types(MobilizonWeb.Schema.ConfigType)
alias MobilizonWeb.Resolvers alias MobilizonWeb.Resolvers
@ -133,6 +134,7 @@ defmodule MobilizonWeb.Schema do
import_fields(:participant_queries) import_fields(:participant_queries)
import_fields(:tag_queries) import_fields(:tag_queries)
import_fields(:address_queries) import_fields(:address_queries)
import_fields(:config_queries)
end end
@desc """ @desc """

View File

@ -0,0 +1,23 @@
defmodule MobilizonWeb.Schema.ConfigType do
@moduledoc """
Schema representation for User
"""
use Absinthe.Schema.Notation
alias MobilizonWeb.Resolvers.Config
@desc "A config object"
object :config do
# Instance name
field(:name, :string)
field(:registrations_open, :boolean)
end
object :config_queries do
@desc "Get the instance config"
field :config, :config do
resolve(&Config.get_config/3)
end
end
end

View File

@ -0,0 +1,25 @@
defmodule MobilizonWeb.Resolvers.ConfigResolverTest do
alias MobilizonWeb.AbsintheHelpers
use MobilizonWeb.ConnCase
use Bamboo.Test
describe "Resolver: Get config" do
test "get_config/3 returns the instance config", context do
query = """
{
config {
name,
registrationsOpen
}
}
"""
res =
context.conn
|> get("/api", AbsintheHelpers.query_skeleton(query, "config"))
assert json_response(res, 200)["data"]["config"]["name"] == "Test instance"
assert json_response(res, 200)["data"]["config"]["registrationsOpen"] == true
end
end
end

View File

@ -1,11 +1,12 @@
defmodule MobilizonWeb.Resolvers.UserResolverTest do defmodule MobilizonWeb.Resolvers.UserResolverTest do
use MobilizonWeb.ConnCase use MobilizonWeb.ConnCase
alias Mobilizon.{Actors, Users} alias Mobilizon.{Actors, Users, CommonConfig}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Users.User alias Mobilizon.Users.User
alias MobilizonWeb.AbsintheHelpers alias MobilizonWeb.AbsintheHelpers
alias Mobilizon.Service.Users.ResetPassword alias Mobilizon.Service.Users.ResetPassword
import Mobilizon.Factory import Mobilizon.Factory
import Mock
use Bamboo.Test use Bamboo.Test
@valid_actor_params %{email: "test@test.tld", password: "testest", username: "test"} @valid_actor_params %{email: "test@test.tld", password: "testest", username: "test"}
@ -389,6 +390,29 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do
assert hd(json_response(res, 200)["errors"])["message"] == assert hd(json_response(res, 200)["errors"])["message"] ==
"Email doesn't fit required format" "Email doesn't fit required format"
end end
test "test create_user/3 doesn't create a user when registration is disabled", context do
with_mock CommonConfig, registrations_open?: fn -> false end do
mutation = """
mutation {
createUser(
email: "#{@user_creation.email}",
password: "#{@user_creation.password}",
) {
id,
email
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"Registrations are not enabled"
end
end
end end
describe "Resolver: Validate an user" do describe "Resolver: Validate an user" do