<commit_before>import isEmpty from 'lodash/isEmpty'

import Collection from '../collection/redis'
import Model from '../model'
import { forEach } from '../utils'

import { parseProp } from './utils'

// ===================================================================

export default class User extends Model {}

User.prototype.default = {
  permission: 'none',
}

// -------------------------------------------------------------------

export class Users extends Collection {
  get Model() {
    return User
  }

  async create(properties) {
    const { email } = properties

    // Avoid duplicates.
    if (await this.exists({ email })) {
      throw new Error(`the user ${email} already exists`)
    }

    // Create the user object.
    const user = new User(properties)

    // Adds the user to the collection.
    return /* await */ this.add(user)
  }

  async save(user) {
    // Serializes.
    let tmp
    user.groups = isEmpty((tmp = user.groups)) ? undefined : JSON.stringify(tmp)
    user.preferences = isEmpty((tmp = user.preferences))
      ? undefined
      : JSON.stringify(tmp)

    return /* await */ this.update(user)
  }

  async get(properties) {
    const users = await super.get(properties)

    // Deserializes
    forEach(users, user => {
      user.groups = parseProp('user', user, 'groups', [])
      user.preferences = parseProp('user', user, 'preferences', {})
    })

    return users
  }
}<commit_msg>fix(xo-server/users): serialize properties on user `create` as well (#5273)<commit_after>import isEmpty from 'lodash/isEmpty'

import Collection from '../collection/redis'
import Model from '../model'

import { parseProp } from './utils'

// ===================================================================

export default class User extends Model {}

User.prototype.default = {
  permission: 'none',
}

// -------------------------------------------------------------------

const serialize = user => {
  let tmp
  return {
    ...user,
    groups: isEmpty((tmp = user.groups)) ? undefined : JSON.stringify(tmp),
    preferences: isEmpty((tmp = user.preferences))
      ? undefined
      : JSON.stringify(tmp),
  }
}

const deserialize = user => ({
  ...user,
  groups: parseProp('user', user, 'groups', []),
  preferences: parseProp('user', user, 'preferences', {}),
})

export class Users extends Collection {
  get Model() {
    return User
  }

  async create(properties) {
    const { email } = properties

    // Avoid duplicates.
    if (await this.exists({ email })) {
      throw new Error(`the user ${email} already exists`)
    }

    // Create the user object.
    const user = new User(serialize(properties))

    // Adds the user to the collection.
    return /* await */ this.add(user)
  }

  async save(user) {
    return /* await */ this.update(serialize(user))
  }

  async get(properties) {
    return (await super.get(properties)).map(deserialize)
  }
}
{'fraction_non_alphanumeric': 0.22004806041881222, 'fraction_numerical': 0.0013731548232063166, 'mean_word_length': 2.557997557997558, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5379679', 'n_tokens_mistral': 817, 'n_tokens_neox': 788, 'n_words': 226}