<commit_before>/*

Mutations have four steps:

1. Validation

If the mutation call is not trusted (i.e. it comes from a GraphQL mutation),
we'll run all validate steps:

- Check that the current user has permission to insert/edit each field.
- Add userId to document (insert only).
- Run validation callbacks.

2. Sync Callbacks

The second step is to run the mutation argument through all the sync callbacks.

3. Operation

We then perform the insert/update/remove operation.

4. Async Callbacks

Finally, *after* the operation is performed, we execute any async callbacks.
Being async, they won't hold up the mutation and slow down its response time
to the client.

*/

import { runCallbacks, runCallbacksAsync } from '../modules/index.js';
import { createError } from 'apollo-errors';
import { validateDocument, validateModifier } from '../modules/validation.js';
import { debug } from '../modules/debug.js';

export const newMutation = async ({ collection, document, currentUser, validate, context }) => {

  debug('//------------------------------------//');
  debug('// newMutation');
  debug(collection._name);
  debug(`validate: ${validate}`);
  debug(document);

  // we don't want to modify the original document
  let newDocument = Object.assign({}, document);

  const collectionName = collection._name;
  const schema = collection.simpleSchema()._schema;

  if (validate) {

    const validationErrors = validateDocument(newDocument, collection, context);

    // run validation callbacks
    newDocument = runCallbacks(`${collectionName}.new.validate`, newDocument, currentUser, validationErrors);
  
    if (validationErrors.length) {
      const NewDocumentValidationError = createError('app.validation_error', {message: 'app.new_document_validation_error'});
      throw new NewDocumentValidationError({data: {break: true, errors: validationErrors}});
    }

  }
  
  // check if userId field is in the schema and add it to document if needed
  const userIdInSchema = Object.keys(schema).find(key => key === 'userId');
  if (!!userIdInSchema && !newDocument.userId) newDocument.userId = currentUser._id;

  // run onInsert step
  // note: cannot use forEach with async/await. 
  // See https://stackoverflow.com/a/37576787/649299
  for(let fieldName of _.keys(schema)) {
    if (schema[fieldName].onInsert) {
      const autoValue = await schema[fieldName].onInsert(newDocument, currentUser);
      if (autoValue) {
        newDocument[fieldName] = autoValue;
      }
    }
  }

  // TODO: find that info in GraphQL mutations
  // if (Meteor.isServer && this.connection) {
  //   post.userIP = this.connection.clientAddress;
  //   post.userAgent = this.connection.httpHeaders['user-agent'];
  // }

  // run sync callbacks<commit_msg>test if autoValue is undefined<commit_after>/*

Mutations have four steps:

1. Validation

If the mutation call is not trusted (i.e. it comes from a GraphQL mutation),
we'll run all validate steps:

- Check that the current user has permission to insert/edit each field.
- Add userId to document (insert only).
- Run validation callbacks.

2. Sync Callbacks

The second step is to run the mutation argument through all the sync callbacks.

3. Operation

We then perform the insert/update/remove operation.

4. Async Callbacks

Finally, *after* the operation is performed, we execute any async callbacks.
Being async, they won't hold up the mutation and slow down its response time
to the client.

*/

import { runCallbacks, runCallbacksAsync } from '../modules/index.js';
import { createError } from 'apollo-errors';
import { validateDocument, validateModifier } from '../modules/validation.js';
import { debug } from '../modules/debug.js';

export const newMutation = async ({ collection, document, currentUser, validate, context }) => {

  debug('//------------------------------------//');
  debug('// newMutation');
  debug(collection._name);
  debug(`validate: ${validate}`);
  debug(document);

  // we don't want to modify the original document
  let newDocument = Object.assign({}, document);

  const collectionName = collection._name;
  const schema = collection.simpleSchema()._schema;

  if (validate) {

    const validationErrors = validateDocument(newDocument, collection, context);

    // run validation callbacks
    newDocument = runCallbacks(`${collectionName}.new.validate`, newDocument, currentUser, validationErrors);
  
    if (validationErrors.length) {
      const NewDocumentValidationError = createError('app.validation_error', {message: 'app.new_document_validation_error'});
      throw new NewDocumentValidationError({data: {break: true, errors: validationErrors}});
    }

  }
  
  // check if userId field is in the schema and add it to document if needed
  const userIdInSchema = Object.keys(schema).find(key => key === 'userId');
  if (!!userIdInSchema && !newDocument.userId) newDocument.userId = currentUser._id;

  // run onInsert step
  // note: cannot use forEach with async/await. 
  // See https://stackoverflow.com/a/37576787/649299
  for(let fieldName of _.keys(schema)) {
    if (schema[fieldName].onInsert) {
      const autoValue = await schema[fieldName].onInsert(newDocument, currentUser);
      if (typeof autoValue !== 'undefined') {
        newDocument[fieldName] = autoValue;
      }
    }
  }

  // TODO: find that info in GraphQL mutations
  // if (Meteor.isServer && this.connection) {
  //   post.userIP = this.connection.clientAddress;
  //   post.userAgent = this.connection.httpHeaders['user-agent'];
  // }

  // run sync callbacks
{'fraction_non_alphanumeric': 0.12536075036075037, 'fraction_numerical': 0.006493506493506494, 'mean_word_length': 3.6285475792988313, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 2, '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': '2001227', 'n_tokens_mistral': 1562, 'n_tokens_neox': 1521, 'n_words': 509}