Skip to content

0.1.4

Pre-release
Pre-release
Compare
Choose a tag to compare
@ProvorovOleksii ProvorovOleksii released this 25 Oct 13:09
632a318

Support Metafields

Tento now supports Metafields

  1. Metafields have the same number of field definitions and validations as Metaobjects.

Note

We currently support only the 'PRODUCT' owner type.

import { metafield } from '@drizzle-team/tento';

export const name = metafield({
  name: "Name",
  ownerType: "PRODUCT",
  pin: true,
  description: "",
  key: "name",
  namespace: "custom",
  visibleToStorefrontApi: true,
  fieldDefinition: (f) =>
    f.singleLineTextField({
      validations: (v) => [v.min(1), v.max(50)],
    }),
});

export const description = metafield({
  name: "Description",
  ownerType: "PRODUCT",
  fieldDefinition: (f) => f.multiLineTextField(),
});
  1. Additionally, Metafields can now include a Metaobject reference field definition.
import { metafield, metaobject } from '@drizzle-team/tento';

export const designer = metaobject({
  name: "Designer",
  type: "designer",
  fieldDefinitions: (f) => ({
    name: f.singleLineTextField({
      name: "Title",
      required: true,
      validations: (v) => [v.min(1), v.max(50)],
    }),
    description: f.multiLineTextField({
      name: "Description",
    }),
    website: f.url({
      name: "Website",
    }),
  }),
});

export const designer_reference = metafield({
  name: "Designer",
  ownerType: "PRODUCT",
  fieldDefinition: (f) =>
    f.metaobjectReference({
      validations: (v) => [v.metaobjectDefinitionType(() => designer.type)],
    }),
});

Updated Metaobjects

  1. Previously, we had simple $inferInsert and $inferSelect type definitions based on the Metaobject schema model, which allowed for straightforward types with all required fields. With this release, we are introducing new $inferInsert and $inferSelect type definitions. Now, Tento will specify exactly which fields are required for insertion and which fields can be nullable in the response.
import { metaobject } from '@drizzle-team/tento';

export const designer = metaobject({
  name: "Designer",
  type: "designer",
  fieldDefinitions: (f) => ({
    name: f.singleLineTextField({
      name: "Title",
      required: true,
      validations: (v) => [v.min(1), v.max(50)],
    }),
    description: f.multiLineTextField({
      name: "Description",
    }),
    website: f.url({
      name: "Website",
    }),
  }),
});

type InsertModel = typeof designer.$inferInsert;
type SelectModel = typeof designer.$inferSelect;

Before

type InsertModel = {
    name: string;
    description: string;
    website: string;
}

type SelectModel = {
    _id: string;
    _handle: string;
    _updatedAt: Date;
    name: string;
    description: string;
    website: string;
}

After

type InsertModel = {
    name: string;
    description?: string | undefined;
    website?: string | undefined;
}

type SelectModel = {
    _id: string;
    _handle: string;
    _displayName: string;
    _updatedAt: Date;
    name: string;
    description: string | null;
    website: string | null;
}
  1. Added new API for upsert
import { tento, metaobject } from "@drizzle-team/tento";

export const designer = metaobject({
  name: "Designer",
  type: "designer",
  fieldDefinitions: (f) => ({
    name: f.singleLineTextField({
      name: "Title",
      required: true,
      validations: (v) => [v.min(1), v.max(50)],
    }),
    description: f.multiLineTextField({
      name: "Description",
    }),
    website: f.url({
      name: "Website",
    }),
  }),
});

const result = await client.metaobjects.designer.upsert("gid://shopify/Metaobject/1", {
    fields: {
      name: "Alex",
    },
});
const result: {
    _id: string;
    _handle: string;
    _displayName: string;
    _updatedAt: Date;
    name: string;
    description: string | null;
    website: string | null;
}
  1. The bulkDelete API has been updated to return a job instance in the response. This instance includes one field and one asynchronous function:
  • job.done: This field contains the actual result and resolves only once the job is complete.
  • job.checkDone(): This asynchronous function calls Shopify to retrieve an updated result for the job.
import { tento, metaobject } from "@drizzle-team/tento";

export const designer = metaobject({
  name: "Designer",
  type: "designer",
  fieldDefinitions: (f) => ({
    name: f.singleLineTextField({
      name: "Title",
      required: true,
      validations: (v) => [v.min(1), v.max(50)],
    }),
    description: f.multiLineTextField({
      name: "Description",
    }),
    website: f.url({
      name: "Website",
    }),
  }),
});

/**
* job.done -> boolean
* job.chekDone() -> Promise<boolean>
*/
const job: ShopifyJobOperation = await client.metaobjects.designer.bulkDelete(['gid://shopify/Metaobject/1']);
const isDone = await job.checkDone();
const isDone: boolean

Support Products

Starting with this release, we are introducing new Products API's. We will gradually improve and add all shopify Product API's

  1. products.list is actually products shopify query
import { tento } from "@drizzle-team/tento";

const products = await client.products.list();
const products: {
    items: {
        id: string;
        createdAt: Date;
        updatedAt: Date;
        isGiftCard: boolean;
        category?: {
            fullName: string;
            id: string;
            isLeaf: boolean;
            isRoot: boolean;
            name: string;
            ancestorIds: number[];
            childrenIds: number[];
            isArchived: boolean;
            level: number;
            parentId?: number | undefined;
        } | undefined;
        ... 30 more ...;
        vendor: string;
    }[];
    pageInfo: {
        startCursor: string;
        endCursor: string;
        hasNextPage: boolean;
        hasPreviousPage: boolean;
    };
}

By default, all fields are included in the response. However, you can specify which fields to include or exclude as needed. You can query all available fields in the 2024-10 Shopify API version with these filters

Using include in your query will return only the specified fields:

import { tento } from "@drizzle-team/tento";

const products = await client.products.list({
    first: 10,
    fields: {
      id: true,
      title: true,
      handle: true,
      productType: true,
      tags: true,
    },
    query: {
      title: "product",
      $or: [
        {
          tag: "tento",
        },
        {
          tag: "custom",
        },
      ],
    },
  });
const products: {
    items: {
        id: string;
        title: string;
        handle: string;
        productType: string;
        tags: string[];
    }[];
    pageInfo: {
        startCursor: string;
        endCursor: string;
        hasNextPage: boolean;
        hasPreviousPage: boolean;
    };
}

Using exclude in your query will return all fields except the specified ones:

const products = await client.products.list({
    first: 10,
    fields: {
      id: false,
      title: false,
      handle: false,
      productType: false,
      tags: false,
    },
    query: {
      title: "product",
      $or: [
        {
          tag: "tento",
        },
        {
          tag: "custom",
        },
      ],
    },
  });
const products: {
    items: {
        description: string;
        createdAt: Date;
        updatedAt: Date;
        isGiftCard: boolean;
        category: {
            fullName: string;
            id: string;
            isLeaf: boolean;
            isRoot: boolean;
            name: string;
            ... 4 more ...;
            parentId?: number | undefined;
        } | undefined;
        ... 25 more ...;
        vendor: string;
    }[];
    pageInfo: {
        startCursor: string;
        endCursor: string;
        hasNextPage: boolean;
        hasPreviousPage: boolean;
    };
}
  1. The products.update function corresponds to the productUpdate mutation in Shopify.
import { tento, metaobject, metafield } from "@drizzle-team/tento";

export const designer = metaobject({
  name: "Designer",
  type: "designer",
  fieldDefinitions: (f) => ({
    name: f.singleLineTextField({
      name: "Title",
      required: true,
      validations: (v) => [v.min(1), v.max(50)],
    }),
    description: f.multiLineTextField({
      name: "Description",
    }),
    website: f.url({
      name: "Website",
    }),
  }),
});

export const designer_reference = metafield({
  name: "Designer",
  ownerType: "PRODUCT",
  fieldDefinition: (f) =>
    f.metaobjectReference({
      validations: (v) => [v.metaobjectDefinitionType(() => designer.type)],
    }),
});

const gqlClient = new shopify.clients.Graphql({
  session,
});

const client = tento({
  client: gqlClient,
  schema: { designer, designer_reference },
});

/**
* Designer key field is accessed as designer_reference.name, which will be autocompleted by TypeScript
*/
const products = await client.products.update("gid://shopify/Product/1", {
    fields: {
      title: "product title",
      metafields: {
        Designer: "metaobject ID",
      },
    },
  });
const products: {
    title: string;
    metafields: {
        key: string;
        type: MetafieldFieldType;
        value: string;
        id: string;
        namespace: string;
        ownerType: 'PRODUCT';
    }[];
}

Common

We’ve added common objects similar to those in Shopify. currently, this includes only the job query

The job query returns the following fields:

  • id: A globally unique ID that is generated when running an asynchronous mutation.
  • done: This field resolves only once the job is complete.
import { tento } from "@drizzle-team/tento";

const job = await client.jobs.get('gid://shopify/Job/1');
const job: {
    id: string;
    done: boolean;
}

Migrations

Migrations now also support Metafields in both the CLI and client.

CLI

tento pull
tento push

Client

import { tento } from "@drizzle-team/tento";

await client.applySchema();

We have also added a configuration option for the Client: { unknownEntities: 'ignore' | 'delete' }. This allows you to specify a strategy for handling metaobjects and metafields

Important

unknownEntities- helps define the scope of metaobjects and metafields to be migrated, preventing unnecessary downloads and avoiding the deletion of other existing items.

-ignore- This means that only the metaobjects and metafields from your schema will be processed.
-delete- This means that all metaobjects and metafields will be processed. If you select this option and run applySchema(), we will create or update your schema in Shopify and remove all metaobjects and metafields that do not exist in your schema.

Note

If you don't specify the configuration, the default option will be ignore

  1. applySchema() with config
await client.applySchema({ unknownEntities: 'ignore' });
  1. We have also added a new API to remove the current schema from Shopify, which includes the same configuration option.
await client.removeSchema({ unknownEntities: 'ignore' });