<commit_before>import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { Pool } from 'pg';
import { AnalyticsUtil } from '../lib/analyticsEvents';
import { PublicInstanceSetting } from '../lib/instanceSettings';
import { DatabaseServerSetting } from './databaseSettings';
import { addGraphQLMutation, addGraphQLResolvers } from './vulcan-lib';

const connectionStringSetting = new DatabaseServerSetting<string | null>("analytics.connectionString", null)
// Since different environments are connected to the same DB, this setting cannot be moved to the database
const environmentDescriptionSetting = new PublicInstanceSetting<string>("analytics.environment", "misconfigured", "warning")

const serverId = Random.id();

const isValidEventAge = (age) => age>=0 && age<=60*60*1000;

addGraphQLResolvers({
  Mutation: {
    analyticsEvent(root, { events, now: clientTime }, context: ResolverContext) {
      // Adjust timestamps to account for server-client clock skew
      // The mutation comes with a timestamp on each event from the client
      // clock, and a timestamp representing when events were flushed, also
      // from the client clock. We use these to translate from absolute time to
      // relative time (ie, age), and apply that age as an offset relative to
      // the server clock.
      // If an event age is <0 or >1h, ignore its timestamp entirely, assume
      // that means that timestamp is broken (eg, the clock was reset while
      // events were being captured); in that case, use the time it reached the
      // server instead.
      const serverTime = new Date();
      
      for (let event of events) {
        const eventTime = new Date(event.timestamp);
        const age = clientTime.valueOf() - eventTime.valueOf();
        const adjustedTimestamp = isValidEventAge(age) ? new Date(serverTime.valueOf()-age.valueOf()) : serverTime;
        
        let eventCopy = {...event, timestamp: adjustedTimestamp};
        writeEventToAnalyticsDB(eventCopy);
      }
      return true;
    },
  }
});
addGraphQLMutation('analyticsEvent(events: [JSON!], now: Date): Boolean');

let analyticsConnectionPool:Pool|null = null;
// Return the Analytics database connection pool, if configured. If no
// analytics DB is specified in the server config, returns null instead. The
// first time this is called, it will block briefly.
const getAnalyticsConnection = (): Pool|null => {
  const connectionString = connectionStringSetting.get()
  // We make sure that the settingsCache is initialized before we access the connection strings
  if (!connectionString) {
    //eslint-disable-next-line no-console
    console.log("Analytics logging disabled: analytics.connectionString is not configured");
    return null;
  } <commit_msg>Only warn once, if analytics connection is not configured<commit_after>import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { Pool } from 'pg';
import { AnalyticsUtil } from '../lib/analyticsEvents';
import { PublicInstanceSetting } from '../lib/instanceSettings';
import { DatabaseServerSetting } from './databaseSettings';
import { addGraphQLMutation, addGraphQLResolvers } from './vulcan-lib';

const connectionStringSetting = new DatabaseServerSetting<string | null>("analytics.connectionString", null)
// Since different environments are connected to the same DB, this setting cannot be moved to the database
const environmentDescriptionSetting = new PublicInstanceSetting<string>("analytics.environment", "misconfigured", "warning")

const serverId = Random.id();

const isValidEventAge = (age) => age>=0 && age<=60*60*1000;

addGraphQLResolvers({
  Mutation: {
    analyticsEvent(root, { events, now: clientTime }, context: ResolverContext) {
      // Adjust timestamps to account for server-client clock skew
      // The mutation comes with a timestamp on each event from the client
      // clock, and a timestamp representing when events were flushed, also
      // from the client clock. We use these to translate from absolute time to
      // relative time (ie, age), and apply that age as an offset relative to
      // the server clock.
      // If an event age is <0 or >1h, ignore its timestamp entirely, assume
      // that means that timestamp is broken (eg, the clock was reset while
      // events were being captured); in that case, use the time it reached the
      // server instead.
      const serverTime = new Date();
      
      for (let event of events) {
        const eventTime = new Date(event.timestamp);
        const age = clientTime.valueOf() - eventTime.valueOf();
        const adjustedTimestamp = isValidEventAge(age) ? new Date(serverTime.valueOf()-age.valueOf()) : serverTime;
        
        let eventCopy = {...event, timestamp: adjustedTimestamp};
        writeEventToAnalyticsDB(eventCopy);
      }
      return true;
    },
  }
});
addGraphQLMutation('analyticsEvent(events: [JSON!], now: Date): Boolean');

let analyticsConnectionPool:Pool|null = null;

let missingConnectionStringWarned = false;

// Return the Analytics database connection pool, if configured. If no
// analytics DB is specified in the server config, returns null instead. The
// first time this is called, it will block briefly.
const getAnalyticsConnection = (): Pool|null => {
  const connectionString = connectionStringSetting.get()
  // We make sure that the settingsCache is initialized before we access the connection strings
  if (!connectionString) {
    if (!missingConnectionStringWarned) {
      missingConnectionStringWarned = true;
      //eslint-disable-next-line no-console
      console.log("Analytics logging disabled: analytics.connectionString is not configured");
    }
    return null;
  }
{'fraction_non_alphanumeric': 0.09672929714683368, 'fraction_numerical': 0.003827418232428671, 'mean_word_length': 3.731687242798354, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, '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': '3510760', 'n_tokens_mistral': 1472, 'n_tokens_neox': 1444, 'n_words': 569}