sanity-io / sanity-algolia

Utilities for indexing Sanity documents in Algolia
MIT License
67 stars 16 forks source link

Cannot read properties of undefined (reading 'created') #33

Open Christian-Garrison opened 1 year ago

Christian-Garrison commented 1 year ago

Constants

export const RESOURCES_INDEX =
  process.env.NODE_ENV === 'development' ? 'dev_resources' : 'prod_resources';

export const RESOURCES_QUERY_SUGGESTIONS =
  process.env.NODE_ENV === 'development'
    ? 'dev_resources_query_suggestions'
    : 'prod_resources_query_suggestions';

export const RESOURCES_PROJECTION = `{
  _id,
  _type,
  _createdAt,
  _updatedAt,
  episode,
  name,
  slug,
  publicationDate,
  abstract,
  'place': location->{...},
  'author': author->{...},
  'categories': categories[]->{...},
  tags,
  metadata,
}`;

Configurations

import type { SanityDocumentStub } from '@sanity/client';
import { resourceTypesArray } from '@utils/resourceTypes';
import type { SearchIndex } from 'algoliasearch';
import algoliasearch from 'algoliasearch';
import indexer from 'sanity-algolia';
import { RESOURCES_INDEX, RESOURCES_PROJECTION } from './constants';

interface TypeConfig {
  index: SearchIndex;
  projection?: string;
}

interface IndexMap {
  [key: string]: TypeConfig;
}

export const algoliaInstance = algoliasearch(
  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID as string,
  process.env.ALGOLIA_ADMIN_KEY as string,
);

export const searchClient = algoliasearch(
  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID as string,
  process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY as string,
);

export const INDEXMAP = resourceTypesArray
  ?.map((type) => ({
    [type]: {
      index: algoliaInstance.initIndex(RESOURCES_INDEX),
      projection: RESOURCES_PROJECTION,
    },
  }))
  .reduce((acc, cur) => ({ ...acc, ...cur }), {}) as IndexMap;

export const sanityAlgolia = indexer(
  {
    ...INDEXMAP,
  },
  (document: SanityDocumentStub) => document,
);

Async Serverless Functions "w/ Secret"

import { sanityAlgolia } from '@lib/algolia';
import { sanityClient } from '@lib/sanity';
import { isValidSignature, SIGNATURE_HEADER_NAME } from '@sanity/webhook';
import { getErrorMessage } from '@utils/getErrorMessage';
import { readBody } from '@utils/readBody';
import type { NextApiRequest, NextApiResponse } from 'next';

// Next.js will by default parse the body, which can lead to invalid signatures
export const config = {
  api: {
    bodyParser: false,
  },
};

const SANITY_WEBHOOK_SECRET = process.env.SANITY_INDEX_SECRET?.trim() || '';

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const signature = req.headers[SIGNATURE_HEADER_NAME] as string;
  const body = await readBody(req); // Read the body into a string
  if (!isValidSignature(body, signature, SANITY_WEBHOOK_SECRET)) {
    const invalidSignature = 'Invalid signature';
    console.error(`[revalidate] ${invalidSignature}`);
    return res.status(401).json({ success: false, message: invalidSignature });
  }

  const client = sanityClient.withConfig({ apiVersion: '2021-03-25' });

  return sanityAlgolia
    .webhookSync(client as any, req.body)
    .then(() => {
      res.status(200).send('ok');
    })
    .catch((error) => {
      console.error(`[update-algolia] ${getErrorMessage(error)}`);
      res.status(500).json({ error: getErrorMessage(error) });
    });
};

export default handler;
Christian-Garrison commented 1 year ago

@runeb pinging for visibility

felipeorlando commented 1 year ago

Same problem here 😕

felipeorlando commented 1 year ago

@Christian-Garrison Just discovered that we need to create a Webhook in a legacy way. This is described on the readme, and this link helps.

Christian-Garrison commented 1 year ago

@felipeorlando I do have the legacy webhook setup, by chance are you able to share your code files around this? "assuming you have it working"

ApinisMikelis commented 1 year ago

@felipeorlando @Christian-Garrison If you had success with this please share the working code if possible :)

thormuller commented 1 year ago

@ApinisMikelis @felipeorlando @Christian-Garrison I'm another in the line of people with this issue. Any tips for resolving?

jmurphy-dev commented 1 year ago

I am also having this issue.

felipeorlando commented 1 year ago

Hey yall,

I really don't remember what I was doing here... but here is the code I implemented to solve the problem:

https://gist.github.com/felipeorlando/17381f55a2e3b0e65ab64018caed4159#file-places-indexer-js

Again, as I mentioned, is required to create a Webhook in a legacy way. This is described on the readme of this (sanity-algolia) project. You can see how to do it on the "Migrating the legacy webhook behavior to GROQ-powered Webhooks" article.

jho14 commented 1 year ago

Hey all,

Ran into this issue as well. If we look into the source code we can find the 'created' that the error is coming from

const { created = [], updated = [] } = body.ids https://github.com/sanity-io/sanity-algolia/blob/b93dbde5795ef577c56828f90a7a3686070c570d/src/index.ts#L72C52-L72C52

From the request.body it expects the leading property to be .ids which you can define in your webhook GROQ. If you don't have this defined then body.ids is undefined and throws the error.

This is an example of the GROQ you should put in your webhook configurations. You may run into issues with created / deleted / updated values being [null] which will also throw an error in the package but haven't figured out that part yet. Pasted Graphic

koc-he commented 1 year ago

Using the fix mentioned here will solve this issue.