apollo-server#UserInputError TypeScript Examples

The following examples show how to use apollo-server#UserInputError. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: live.ts    From vt-api with GNU Affero General Public License v3.0 6 votes vote down vote up
export async function live(_, query: LiveQuery) {
  try {
    const { organizations = [], platforms = [], exclude_organizations = [] } = query;
    if (organizations.length && exclude_organizations.length) {
      return new UserInputError('Setting both organizations and exclude_organizations is redundant. Only choose one.');
    }
    const EXCLUDE_ORG = !organizations.length;
    const ORGANIZATIONS = parseOrganization(EXCLUDE_ORG ? exclude_organizations : organizations);
    const CACHE_KEY = getCacheKey(`LIVE:${+EXCLUDE_ORG}${cutGroupString(ORGANIZATIONS)}${platforms}`);

    const cachedVideos = await memcache.get(CACHE_KEY);
    if (cachedVideos) return cachedVideos;

    const uncachedVideos = await Videos.find({
      status: 'live',
      ...platforms[0] && { platform_id: { $in: platforms } },
      ...ORGANIZATIONS[0] && { organization: {
        ...EXCLUDE_ORG
          ? { $not: { $regex: ORGANIZATIONS, $options: 'i' } }
          : { $regex: ORGANIZATIONS, $options: 'i' }
      } }
    }).sort({ 'time.start': 1 })
      .lean()
      .exec();

    memcache.set(CACHE_KEY, uncachedVideos, 60);
    return uncachedVideos;
  } catch (error) {
    throw new ApolloError(error);
  }
}
Example #2
Source File: resolver.ts    From one-platform with MIT License 5 votes vote down vote up
CommonResolver: IResolvers = {
  DateTime: new GraphQLScalarType({
    name: 'DateTime',
    description: 'DateTime scalar for parsing and serializing DateTime objects',
    serialize: (value: any) => {
      return value.toISOString(); // Convert outgoing Date to ISO String for JSON
    },
    parseValue: (value: any) => {
      if (isNaN(Date.parse(value))) {
        throw new UserInputError('Invalid date format');
      }
      return new Date(value); // Convert incoming string | integer to Date
    },
    parseLiteral: (ast) => {
      if (ast.kind === Kind.INT) {
        return new Date(parseInt(ast.value, 10)); // Convert hard-coded AST string to integer and then to Date
      } else if (ast.kind === Kind.STRING) {
        return new Date(ast.value);
      }
      throw new UserInputError('Invalid date format'); // Invalid hard-coded value (not an integer)
    },
  }),
  ObjectId: new GraphQLScalarType({
    name: 'ObjectId',
    description: 'MongoDB ObjectID',
    serialize: (value: any) => {
      return value.toString();
    },
    parseValue: (value: any) => {
      if (ObjectId.isValid(value)) {
        return new ObjectId(value);
      }
      throw new UserInputError('Invalid ObjectID');
    },
    parseLiteral: (ast) => {
      if (ast.kind === Kind.STRING && ObjectId.isValid(ast.value)) {
        return new ObjectId(ast.value);
      }
      throw new UserInputError('Invalid ObjectId');
    },
  }),
}
Example #3
Source File: channels.ts    From vt-api with GNU Affero General Public License v3.0 5 votes vote down vote up
export async function channels(_, query: ChannelsQuery) {
  try {
    const {
      _id = [],
      name = '',
      organizations = [],
      exclude_organizations = [],
      exclude_channel_id = [],
      channel_id = [],
      platforms = [],
      page_token = '',
      limit
    } = query;
    if (limit < 1 || limit > 50) {
      return new UserInputError('limit must be between 1-50 inclusive.');
    }
    if (organizations.length && exclude_organizations.length) {
      return new UserInputError('Setting both organizations and exclude_organizations is redundant. Only choose one.');
    }
    if (channel_id.length && exclude_channel_id.length) {
      return new UserInputError('Setting both channel_id and exclude_channel_id is redundant. Only choose one.');
    }
    const EXCLUDE_ORG = !organizations.length;
    const EXCLUDE_IDS = !channel_id.length;
    const [ORDER_BY, ORDER_BY_KEY] = firstField(query.order_by);
    const [ORDER_KEY, ORDER_VALUE] = Object.entries(ORDER_BY)[0];
    const sortById = ORDER_KEY === '_id';
    const sortBy = sortById ? ORDER_BY : { [`channel_stats.${ORDER_KEY}`]: ORDER_VALUE };
    const ORGANIZATIONS = parseOrganization(EXCLUDE_ORG ? exclude_organizations : organizations);
    const CHANNEL_IDS = EXCLUDE_IDS ? exclude_channel_id : channel_id;
    const CACHE_KEY = getCacheKey(`CHNLS:${+EXCLUDE_ORG}${+EXCLUDE_IDS}${_id}${(name)}${cutGroupString(ORGANIZATIONS)}${cutChannelIds(CHANNEL_IDS)}${platforms}${limit}${ORDER_BY_KEY}${page_token}`, false);

    const cached = await memcache.get(CACHE_KEY);
    if (cached) return cached;

    const QUERY = {
      _id: { [_id[0] ? '$in' : '$nin']: _id },
      ...name && { $or: getNameQueries(name) },
      ...ORGANIZATIONS[0] && { organization: {
        ...EXCLUDE_ORG
          ? { $not: { $regex: ORGANIZATIONS, $options: 'i' } }
          : { $regex: ORGANIZATIONS, $options: 'i' }
      } },
      ...channel_id[0] && { channel_id: { [EXCLUDE_IDS ? '$nin' : '$in']: CHANNEL_IDS } },
      ...platforms[0] && { platform_id: { $in: platforms } }
    };

    const getChannelCount = Channels.countDocuments(QUERY);
    const getUncachedChannels = Channels
      .find({
        ...QUERY,
        ...page_token && { [Object.keys(sortBy)[0]]: { [ORDER_VALUE === 'asc' ? '$gte' : '$lte']: parseToken(page_token) } },
      })
      .sort(sortBy)
      .limit(limit + 1)
      .lean()
      .exec();

    const [channelCount, uncachedChannels] = await Promise.all([getChannelCount, getUncachedChannels]);
    const results = {
      items: uncachedChannels,
      next_page_token: null,
      page_info: {
        total_results: channelCount,
        results_per_page: limit
      }
    };

    const hasNextPage = uncachedChannels.length > limit && results.items.pop();
    if (hasNextPage) {
      const token = sortById ? hasNextPage._id : hasNextPage.channel_stats[ORDER_KEY];
      results.next_page_token = getNextToken(token);
    }

    memcache.set(CACHE_KEY, results, CACHE_TTL);
    return results;
  } catch(err) {
    throw new ApolloError(err);
  }
}
Example #4
Source File: data.ts    From vt-api with GNU Affero General Public License v3.0 5 votes vote down vote up
export async function data(_, query: Query) {
  try {
    const {
      organizations = [],
      exclude_organizations = [],
      channel_id = [],
      exclude_channel_id = [],
      platforms = []
    } = query;
    if (organizations.length && exclude_organizations.length) {
      return new UserInputError('Setting both organizations and exclude_organizations is redundant. Only choose one.');
    }
    if (channel_id.length && exclude_channel_id.length) {
      return new UserInputError('Setting both channel_id and exclude_channel_id is redundant. Only choose one.');
    }
    const EXCLUDE_ORG = !organizations.length;
    const EXCLUDE_IDS = !channel_id.length;
    const ORGANIZATIONS = parseOrganization(EXCLUDE_ORG ? exclude_organizations : organizations);
    const CHANNEL_IDS = EXCLUDE_IDS ? exclude_channel_id : channel_id;
    const CACHE_KEY = getCacheKey(`CHNLS:${+EXCLUDE_ORG}${cutGroupString(ORGANIZATIONS)}${cutChannelIds(CHANNEL_IDS)}${platforms}`, false);

    const cached = await memcache.get(CACHE_KEY);
    if (cached) return cached;

    const QUERY = {
      ...ORGANIZATIONS[0] && { organization: {
        ...EXCLUDE_ORG
          ? { $not: { $regex: ORGANIZATIONS, $options: 'i' } }
          : { $regex: ORGANIZATIONS, $options: 'i' }
      } },
      ...channel_id[0] && { channel_id: { [EXCLUDE_IDS ? '$nin' : '$in']: CHANNEL_IDS } },
      ...platforms[0] && { platform_id: { $in: platforms } }
    };

    const getChannelList = Channels.find(QUERY).lean().exec();
    const getVideoCount = Videos.countDocuments(QUERY).lean().exec();
    const [channelList, videoCount] = await Promise.all([getChannelList, getVideoCount]);
    const groupList = channelList
      .map(value => value.organization)
      .filter((value, index, array) => array.indexOf(value) === index);
    const channelCount = channelList.length;

    const result = {
      organizations: groupList,
      channels: channelCount,
      videos: videoCount
    };

    memcache.set(CACHE_KEY, result, CACHE_TTL);
    return result;
  } catch(err) {
    throw new ApolloError(err);
  }
}
Example #5
Source File: resolver.ts    From one-platform with MIT License 4 votes vote down vote up
NotificationResolver: IResolvers<any, IGQLContext> = {
  Query: {
    notifications: async (
      _,
      { filter, since, first = 10, after = 0, sort = { field: 'createdOn', order: -1 } },
      { userId }
    ) => {
      const notifications = await findNotificationsForUser({
        filter: {
          ...filter,
          ...(since && {
            createdOn: {
              $gte: since,
            },
          }),
        },
        userId,
      });

      const edges = notifications
        .sort((prev: NotificationType, next: NotificationType) => {
          const prevField = prev[sort.field as keyof NotificationType];
          const nextField = next[sort.field as keyof NotificationType];
          if (sort.order < 0) {
            return prevField > nextField ? -1 : 1;
          } else {
            return prevField < nextField ? -1 : 1;
          }
        })
        .slice(after, first + after);

      const { totalCount, pageInfo } = prepareList(
        notifications,
        edges,
        first,
        after
      );

      return {
        totalCount,
        edges,
        pageInfo,
      };
    },
    allNotifications: async (
      _,
      {
        filter,
        first = 10,
        after = 0,
        sort = { field: 'createdOn', order: -1 },
      }
    ) => {
      const notificationsCollection = getNotifications().find(filter);

      const edges = await notificationsCollection
        .clone()
        .sort(sort.field, sort.order)
        .skip(after)
        .limit(first)
        .toArray();

      const { totalCount, pageInfo } = prepareList(
        await notificationsCollection.toArray(),
        edges,
        first,
        after
      );

      return {
        totalCount,
        edges,
        pageInfo,
      };
    },
    notification: async (_, { _id }, { userId }) => {
      const notifications = await findNotificationsForUser({
        filter: { _id },
        userId,
      });

      if (notifications.length === 0) {
        throw new NotFoundError('Notification not found.');
      }
      return notifications[0];
    },
  },
  Mutation: {
    publish: async (
      _,
      { event, payload, channel, schedule, additionalRecipients },
      { userId }
    ) => {
      /* TODO: Verify is the user is allowed to publish notifications for the given event */

      const scheduled = Boolean(schedule);
      const scheduledFor = schedule;
      if (schedule <= new Date()) {
        throw new UserInputError(
          'Cannot schedule a notification in the past.',
          {
            argumentName: 'schedule',
          }
        );
      }

      /* create notification */
      const notification = {
        ...event,
        channel,
        payload,
        scheduled,
        scheduledFor,
        createdOn: new Date(),
        createdBy: userId,
      };

      /* Set the appropriate job name */
      const jobName = channel === 'EMAIL' ? EMAIL_JOB_NAME : PUSH_JOB_NAME;
      /* Push the notification to the queue */
      const result = await agenda.schedule(scheduled ? schedule : 'now', jobName, {
        notification,
        additionalRecipients,
      });

      return {
        ok: true,
        _id: result.attrs._id,
      };
    },
  },
}
Example #6
Source File: videos.ts    From vt-api with GNU Affero General Public License v3.0 4 votes vote down vote up
export async function videos(_, query: VideoQuery) {
  try {
    const {
      channel_id = [],
      status = [],
      title,
      organizations = [],
      exclude_organizations = [],
      platforms = [],
      max_upcoming_mins,
      page_token = '',
      limit
    } = query;

    if (limit < 1 || limit > 50) {
      return new UserInputError('limit must be between 1-50 inclusive.');
    }
    if (max_upcoming_mins < 0 || max_upcoming_mins > 2880) {
      return new UserInputError('max_upcoming_mins must be between 0-2880 inclusive.');
    }
    if (organizations.length && exclude_organizations.length) {
      return new UserInputError('Setting both organizations and exclude_organizations is redundant. Only choose one.');
    }
    const EXCLUDE_ORG = !organizations.length;
    const MAX_UPCOMING = max_upcoming_mins * 6e4;
    const TITLE = title && escapeRegex(title);
    const ORGANIZATIONS = parseOrganization(EXCLUDE_ORG ? exclude_organizations : organizations);
    const [ORDER_BY, ORDER_BY_KEY] = firstField(query.order_by);
    const [ORDER_KEY, ORDER_VALUE] = Object.entries(ORDER_BY)[0];
    const orderBy = { [`time.${ORDER_KEY}`]: ORDER_VALUE };
    const CACHE_KEY = getCacheKey(`VIDS:${+EXCLUDE_ORG}${cutGroupString(ORGANIZATIONS)}${channel_id}${status}${TITLE}${platforms}${max_upcoming_mins}${ORDER_BY_KEY}${limit}${page_token}`);

    const cached = await memcache.get(CACHE_KEY);
    if (cached) return cached;

    const QUERY: any = { // any because typescript gets mad for some reason.
      status: status[0] ? { $in: status } : { $ne: 'missing' },
      ...channel_id[0] && { channel_id: { $in: channel_id } },
      ...TITLE && { title: { $regex: TITLE, $options: 'i' } },
      ...ORGANIZATIONS[0] && { organization: {
        ...EXCLUDE_ORG
          ? { $not: { $regex: ORGANIZATIONS, $options: 'i' } }
          : { $regex: ORGANIZATIONS, $options: 'i' }
      } },
      ...platforms[0] && { platform_id: { $in: platforms } },
      ...max_upcoming_mins && { 'time.scheduled': { $lte: Date.now() + MAX_UPCOMING } }
    };

    const getVideoCount = Videos.countDocuments(QUERY);
    const getUncachedVideos = Videos
      .find({
        ...QUERY,
        ...page_token && { [Object.keys(orderBy)[0]]: { [ORDER_VALUE === 'asc' ? '$gte' : '$lte']: parseToken(page_token) } },
      })
      .sort(orderBy)
      .limit(limit + 1)
      .lean()
      .exec();

    const [videoCount, uncachedVideos] = await Promise.all([getVideoCount, getUncachedVideos]);
    const results = {
      items: uncachedVideos,
      next_page_token: null,
      page_info: {
        total_results: videoCount,
        results_per_page: limit
      }
    };

    const hasNextPage = uncachedVideos.length > limit && results.items.pop();
    if (hasNextPage) {
      const token = hasNextPage.time[ORDER_KEY];
      results.next_page_token = getNextToken(token);
    }

    memcache.set(CACHE_KEY, results, 60);
    return results;
  } catch(err) {
    throw new ApolloError(err);
  }
}