Skip to content

Commit

Permalink
Add openapi code generation script and api versioning (#235)
Browse files Browse the repository at this point in the history
## Problem

We need a way to generate code from updated openapi specs

## Solution

- Copy & modify overall approach used in python repository for similar
purpose
- Create new `codegen/` directory to be the home for code generation
stuff
  - Use git submodules to get information from `apis` repo
- Build using the `typescript-fetch` generator in the v7 openapi
generator cli. This is the same as we have used in the past, to keep the
diff somewhat small.
- Build data and control planes separately and copy outputs into
`src/pinecone-generated-ts-fetch` directory in source. This has been the
traditional home for these generated files.
- Since we're no longer mashing specs together ahead of code generation,
I need to adjust import paths to find either
`pinecone-generated-ts-fetch/control` or
`pinecone-generated-ts-fetch/data` as appropriate
- In addition to the default generated output, write a constant called
`X_PINECONE_API_VERSION` in each generated module. This will be used
when building header configuration with `X-Pinecone-Api-Version` for
each request.

## Type of Change

- [x] Infrastructure change (CI configs, etc)

## Test Plan

`npm run generate:openapi`
  • Loading branch information
jhamon authored Jul 15, 2024
1 parent 23a669f commit 7eff996
Show file tree
Hide file tree
Showing 145 changed files with 6,322 additions and 4,913 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.env

# Generated files
build/
coverage/
dist/
docs/
Expand All @@ -13,4 +14,4 @@ ts-compilation-test/lib/
# Clutter
.vscode
.DS_Store
scratch
scratch
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "codegen/apis"]
path = codegen/apis
url = git@github.com:pinecone-io/apis.git
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
dist
node_modules
src/pinecone-generated-ts-fetch
codegen
1 change: 1 addition & 0 deletions codegen/apis
Submodule apis added at fbd9d8
81 changes: 81 additions & 0 deletions codegen/build-oas.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/bin/bash

set -eux -o pipefail

version=$1 # e.g. 2024-07
modules=("control" "data")

destination="src/pinecone-generated-ts-fetch"
build_dir="build"

update_apis_repo() {
echo "Updating apis repo"
pushd codegen/apis
git fetch
git pull
just build
popd
}

verify_spec_version() {
local version=$1
echo "Verifying spec version $version exists in apis repo"
if [ -z "$version" ]; then
echo "Version is required"
exit 1
fi

verify_directory_exists "codegen/apis/_build/${version}"
}

verify_file_exists() {
local filename=$1
if [ ! -f "$filename" ]; then
echo "File does not exist at $filename"
exit 1
fi
}

verify_directory_exists() {
local directory=$1
if [ ! -d "$directory" ]; then
echo "Directory does not exist at $directory"
exit 1
fi
}

generate_client() {
local module_name=$1

oas_file="codegen/apis/_build/${version}/${module_name}_${version}.oas.yaml"

verify_file_exists $oas_file

# Cleanup previous build files
echo "Cleaning up previous build files"
rm -rf "${build_dir}"

# Generate client module
docker run --rm -v $(pwd):/workspace openapitools/openapi-generator-cli:v7.0.0 generate \
--input-spec "/workspace/$oas_file" \
--generator-name typescript-fetch \
--output "/workspace/${build_dir}"

# Copy the generated module to the correct location
rm -rf "${destination}/${module_name}"
mkdir -p "${destination}/${module_name}"
cp -r ${build_dir}/* "${destination}/${module_name}"

echo "export const X_PINECONE_API_VERSION = '${version}';" > ${destination}/${module_name}/api_version.ts
echo "export * from './api_version';" >> ${destination}/${module_name}/index.ts
}

update_apis_repo
verify_spec_version $version

rm -rf "${destination}"
mkdir -p "${destination}"

for module in "${modules[@]}"; do
generate_client $module
done
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"build": "rm -rf dist/ && tsc",
"version": "jq --null-input --arg version $npm_package_version '{\"name\": \"@pinecone-database/pinecone\", \"version\": $version}' > src/version.json",
"docs:build": "typedoc --plugin ./assets/docs-theme.mjs",
"generate:openapi": "./codegen/build-oas.sh 2024-07 && npm run build && npm run format",
"format": "prettier --write .",
"lint": "eslint src/ --ext .ts",
"repl": "npm run build && node utils/replInit.ts",
Expand Down
4 changes: 2 additions & 2 deletions src/control/__tests__/configureIndex.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { configureIndex } from '../configureIndex';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';
import type {
ConfigureIndexOperationRequest,
IndexModel,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/control';

describe('configureIndex', () => {
test('calls the openapi configure endpoint', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/control/__tests__/configureIndex.validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

import { configureIndex } from '../configureIndex';
import { PineconeArgumentError } from '../../errors';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';

describe('configureIndex argument validations', () => {
let MIA: ManageIndexesApi;
beforeEach(() => {
MIA = { configureIndex: jest.fn() };
jest.mock('../../pinecone-generated-ts-fetch', () => ({
jest.mock('../../pinecone-generated-ts-fetch/control', () => ({
IndexOperationsApi: MIA,
}));
});
Expand Down
4 changes: 2 additions & 2 deletions src/control/__tests__/createCollection.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createCollection } from '../createCollection';
import { PineconeArgumentError } from '../../errors';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';
import type {
CollectionModel,
CreateCollectionOperationRequest,
IndexList,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/control';

const setOpenAPIResponse = (fakeCreateCollectionResponse) => {
const fakeCreateCollection: (
Expand Down
4 changes: 2 additions & 2 deletions src/control/__tests__/createIndex.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createIndex } from '../createIndex';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';
import type {
CreateIndexOperationRequest,
DescribeIndexRequest,
IndexModel,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/control';

// describeIndexResponse can either be a single response, or an array of responses for testing polling scenarios
const setupCreateIndexResponse = (
Expand Down
4 changes: 2 additions & 2 deletions src/control/__tests__/createIndex.validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

import { createIndex } from '../createIndex';
import { PineconeArgumentError } from '../../errors';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';

describe('createIndex argument validations', () => {
let MIA: ManageIndexesApi;
beforeEach(() => {
MIA = { createIndex: jest.fn() };
jest.mock('../../pinecone-generated-ts-fetch', () => ({
jest.mock('../../pinecone-generated-ts-fetch/control', () => ({
ManageIndexesApi: MIA,
}));
});
Expand Down
9 changes: 4 additions & 5 deletions src/control/__tests__/deleteCollection.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { deleteCollection } from '../deleteCollection';
import { PineconeArgumentError } from '../../errors';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';
import type {
DeleteCollectionRequest,
CollectionList,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/control';

const setupMocks = (
deleteResponse,
listCollectionResponse = () => Promise.resolve([])
) => {
const fakeDeleteCollection: (
req: DeleteCollectionRequest
) => Promise<string> = jest.fn().mockImplementation(deleteResponse);
const fakeDeleteCollection: (req: DeleteCollectionRequest) => Promise<void> =
jest.fn().mockImplementation(deleteResponse);
const fakeListCollections: () => Promise<CollectionList> = jest
.fn()
.mockImplementation(listCollectionResponse);
Expand Down
4 changes: 2 additions & 2 deletions src/control/__tests__/describeCollection.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { describeCollection } from '../describeCollection';
import { PineconeArgumentError } from '../../errors';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../../pinecone-generated-ts-fetch/control';
import type {
DescribeCollectionRequest,
CollectionList,
CollectionModel,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/control';

const setupMocks = (
describeResponse,
Expand Down
6 changes: 3 additions & 3 deletions src/control/__tests__/indexOperationsBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { indexOperationsBuilder } from '../indexOperationsBuilder';
import { Configuration } from '../../pinecone-generated-ts-fetch';
import { Configuration } from '../../pinecone-generated-ts-fetch/control';

jest.mock('../../pinecone-generated-ts-fetch', () => ({
...jest.requireActual('../../pinecone-generated-ts-fetch'),
jest.mock('../../pinecone-generated-ts-fetch/control', () => ({
...jest.requireActual('../../pinecone-generated-ts-fetch/control'),
Configuration: jest.fn(),
}));

Expand Down
2 changes: 1 addition & 1 deletion src/control/configureIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
ManageIndexesApi,
IndexModel,
ConfigureIndexRequestSpecPod,
} from '../pinecone-generated-ts-fetch';
} from '../pinecone-generated-ts-fetch/control';
import { PineconeArgumentError } from '../errors';
import { buildValidator } from '../validator';
import type { IndexName } from './types';
Expand Down
2 changes: 1 addition & 1 deletion src/control/createCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
CollectionModel,
CreateCollectionRequest,
ManageIndexesApi,
} from '../pinecone-generated-ts-fetch';
} from '../pinecone-generated-ts-fetch/control';
import { buildConfigValidator } from '../validator';
import { CollectionNameSchema, IndexNameSchema } from './types';
import { Type } from '@sinclair/typebox';
Expand Down
2 changes: 1 addition & 1 deletion src/control/createIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
IndexModel,
ManageIndexesApi,
CreateIndexRequestMetricEnum,
} from '../pinecone-generated-ts-fetch';
} from '../pinecone-generated-ts-fetch/control';
import { buildConfigValidator } from '../validator';
import { debugLog } from '../utils';
import { handleApiError } from '../errors';
Expand Down
2 changes: 1 addition & 1 deletion src/control/deleteCollection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ManageIndexesApi } from '../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../pinecone-generated-ts-fetch/control';
import { buildConfigValidator } from '../validator';
import { CollectionNameSchema } from './types';
import type { CollectionName } from './types';
Expand Down
2 changes: 1 addition & 1 deletion src/control/deleteIndex.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ManageIndexesApi } from '../pinecone-generated-ts-fetch';
import { ManageIndexesApi } from '../pinecone-generated-ts-fetch/control';
import { buildConfigValidator } from '../validator';
import { IndexName, IndexNameSchema } from './types';

Expand Down
2 changes: 1 addition & 1 deletion src/control/describeCollection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
ManageIndexesApi,
CollectionModel,
} from '../pinecone-generated-ts-fetch';
} from '../pinecone-generated-ts-fetch/control';
import { buildConfigValidator } from '../validator';
import { CollectionNameSchema } from './types';
import type { CollectionName } from './types';
Expand Down
5 changes: 4 additions & 1 deletion src/control/describeIndex.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { buildConfigValidator } from '../validator';
import { IndexModel, ManageIndexesApi } from '../pinecone-generated-ts-fetch';
import {
IndexModel,
ManageIndexesApi,
} from '../pinecone-generated-ts-fetch/control';
import { IndexNameSchema } from './types';
import type { IndexName } from './types';

Expand Down
6 changes: 4 additions & 2 deletions src/control/indexOperationsBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {
ManageIndexesApi,
Configuration,
} from '../pinecone-generated-ts-fetch';
X_PINECONE_API_VERSION,
} from '../pinecone-generated-ts-fetch/control';
import {
queryParamsStringify,
buildUserAgent,
Expand All @@ -10,7 +11,7 @@ import {
} from '../utils';
import { middleware } from '../utils/middleware';
import type { PineconeConfiguration } from '../data/types';
import type { ConfigurationParameters as IndexOperationsApiConfigurationParameters } from '../pinecone-generated-ts-fetch';
import type { ConfigurationParameters as IndexOperationsApiConfigurationParameters } from '../pinecone-generated-ts-fetch/control';

export const indexOperationsBuilder = (
config: PineconeConfiguration
Expand All @@ -25,6 +26,7 @@ export const indexOperationsBuilder = (
queryParamsStringify,
headers: {
'User-Agent': buildUserAgent(config),
'X-Pinecone-Api-Version': X_PINECONE_API_VERSION,
...headers,
},
fetchApi: getFetch(config),
Expand Down
2 changes: 1 addition & 1 deletion src/control/listCollections.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
ManageIndexesApi,
CollectionList,
} from '../pinecone-generated-ts-fetch';
} from '../pinecone-generated-ts-fetch/control';

export const listCollections = (api: ManageIndexesApi) => {
return async (): Promise<CollectionList> => {
Expand Down
5 changes: 4 additions & 1 deletion src/control/listIndexes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { ManageIndexesApi, IndexList } from '../pinecone-generated-ts-fetch';
import {
ManageIndexesApi,
IndexList,
} from '../pinecone-generated-ts-fetch/control';

export const listIndexes = (api: ManageIndexesApi) => {
return async (): Promise<IndexList> => {
Expand Down
6 changes: 3 additions & 3 deletions src/data/__tests__/dataOperationsProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DataOperationsProvider } from '../dataOperationsProvider';
import { IndexHostSingleton } from '../indexHostSingleton';
import { Configuration } from '../../pinecone-generated-ts-fetch';
import { Configuration } from '../../pinecone-generated-ts-fetch/data';

jest.mock('../../pinecone-generated-ts-fetch', () => ({
...jest.requireActual('../../pinecone-generated-ts-fetch'),
jest.mock('../../pinecone-generated-ts-fetch/data', () => ({
...jest.requireActual('../../pinecone-generated-ts-fetch/data'),
Configuration: jest.fn(),
}));

Expand Down
2 changes: 1 addition & 1 deletion src/data/__tests__/deleteOne.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { deleteOne } from '../deleteOne';
import type {
DeleteOperationRequest,
DataPlaneApi,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/data';
import { DataOperationsProvider } from '../dataOperationsProvider';

const setupDeleteResponse = (response, isSuccess) => {
Expand Down
4 changes: 2 additions & 2 deletions src/data/__tests__/describeIndexStats.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describeIndexStats } from '../describeIndexStats';
import { DataPlaneApi } from '../../pinecone-generated-ts-fetch';
import { DataPlaneApi } from '../../pinecone-generated-ts-fetch/data';
import { DataOperationsProvider } from '../dataOperationsProvider';
import type { DescribeIndexStatsOperationRequest } from '../../pinecone-generated-ts-fetch';
import type { DescribeIndexStatsOperationRequest } from '../../pinecone-generated-ts-fetch/data';

const setupResponse = (response, isSuccess) => {
const fakeDescribeIndexStats: (
Expand Down
4 changes: 2 additions & 2 deletions src/data/__tests__/fetch.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { FetchCommand } from '../fetch';
import { DataPlaneApi } from '../../pinecone-generated-ts-fetch';
import { DataPlaneApi } from '../../pinecone-generated-ts-fetch/data';
import { DataOperationsProvider } from '../dataOperationsProvider';
import type {
FetchRequest,
FetchResponse,
} from '../../pinecone-generated-ts-fetch';
} from '../../pinecone-generated-ts-fetch/data';

const setupResponse = (response, isSuccess) => {
const fakeFetch: (req: FetchRequest) => Promise<FetchResponse> = jest
Expand Down
5 changes: 0 additions & 5 deletions src/data/__tests__/indexHostSingleton.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,6 @@ describe('IndexHostSingleton', () => {
expect(mockDescribeIndex).toHaveBeenNthCalledWith(1, 'index-1');
expect(mockDescribeIndex).toHaveBeenNthCalledWith(2, 'index-2');

console.log(
'mockIndexOperationsBuilder',
JSON.stringify(mockIndexOperationsBuilder.mock)
);

expect(mockIndexOperationsBuilder).toHaveBeenCalledTimes(2);
expect(mockIndexOperationsBuilder).toHaveBeenNthCalledWith(
1,
Expand Down
Loading

0 comments on commit 7eff996

Please sign in to comment.