Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/7.x' into generic-scalar-filters
Browse files Browse the repository at this point in the history
  • Loading branch information
angrykoala committed Dec 6, 2024
2 parents 5f6e689 + cebde09 commit d3af3e8
Show file tree
Hide file tree
Showing 84 changed files with 5,107 additions and 850 deletions.
5 changes: 5 additions & 0 deletions .changeset/sixty-clocks-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/introspector": patch
---

Changed how "@neo4j/introspector" generates list fields that now are generated as a list of non-nullable elements, as a list of nullable elements is not supported by Neo4j.
5 changes: 5 additions & 0 deletions .changeset/stale-knives-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/graphql": patch
---

Deprecates `@private` directive. The private directive was aimed to be used in conjunction with the OGM, which is no longer supported.
5 changes: 5 additions & 0 deletions .changeset/tame-melons-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/graphql": patch
---

Added a validation rule to avoid defining fields as lists of nullable elements, as Neo4j does not support this.
2 changes: 1 addition & 1 deletion .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: srvaroa/labeler@29471ee1118fa4e10b011964e6e8fe2fd243e700 # v1.11.1
- uses: srvaroa/labeler@fe4b1c73bb8abf2f14a44a6912a8b4fee835d631 # v1.12.0
env:
GITHUB_TOKEN: ${{ secrets.NEO4J_TEAM_GRAPHQL_PERSONAL_ACCESS_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/reusable-codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
with:
node-version: lts/*
- name: Initialize CodeQL
uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3
with:
config-file: ./.github/codeql/codeql-config.yml
languages: javascript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3
uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3
584 changes: 292 additions & 292 deletions .yarn/releases/yarn-4.5.1.cjs → .yarn/releases/yarn-4.5.2.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.5.1.cjs
yarnPath: .yarn/releases/yarn-4.5.2.cjs
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"devDependencies": {
"@tsconfig/node16": "1.0.4",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"@typescript-eslint/parser": "8.15.0",
"concurrently": "9.1.0",
"dotenv": "16.4.5",
"eslint": "8.57.1",
Expand All @@ -47,9 +47,9 @@
"ts-jest": "29.2.5",
"typescript": "5.1.6"
},
"packageManager": "[email protected].1",
"packageManager": "[email protected].2",
"dependencies": {
"@changesets/changelog-github": "0.5.0",
"@changesets/cli": "2.27.9"
"@changesets/cli": "2.27.10"
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "apollo-federation-subgraph-compatibility",
"version": "1.0.0",
"packageManager": "[email protected].1",
"packageManager": "[email protected].2",
"scripts": {
"build": "webpack --config webpack.config.js",
"test:docker": "yarn build && fedtest docker --compose docker-compose.yml --schema schema.graphql",
Expand All @@ -10,7 +10,7 @@
"dependencies": {
"@apollo/server": "^4.7.0",
"@graphql-tools/wrap": "^10.0.0",
"@neo4j/graphql": "^6.1.0",
"@neo4j/graphql": "^6.2.1",
"graphql": "16.9.0",
"graphql-tag": "^2.12.6",
"neo4j-driver": "^5.8.0"
Expand Down
37 changes: 37 additions & 0 deletions packages/graphql/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
# @neo4j/graphql

## 6.2.1

### Patch Changes

- [#5861](https://github.com/neo4j/graphql/pull/5861) [`f2e1575`](https://github.com/neo4j/graphql/commit/f2e15751657725abd9086b1fdce13b360bd72eab) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Fixed bug where fields decorated with `@customResolver` were included in the projection of the generated Cypher query

- [#5865](https://github.com/neo4j/graphql/pull/5865) [`258ff53`](https://github.com/neo4j/graphql/commit/258ff53204746ce04828f6ff5ad6422260a3c895) Thanks [@darrellwarde](https://github.com/darrellwarde)! - `@default` directive fixed to work as expected on fields of temporal type, and `BigInt` fields

## 6.2.0

### Minor Changes

- [#5856](https://github.com/neo4j/graphql/pull/5856) [`49549cd`](https://github.com/neo4j/graphql/commit/49549cd0d7805ae8500f05bdd509941d3e754880) Thanks [@mjfwebb](https://github.com/mjfwebb)! - Add filtering on many to many relationship custom cypher fields

## 6.1.1

### Patch Changes

- [#5835](https://github.com/neo4j/graphql/pull/5835) [`cc1a8dd`](https://github.com/neo4j/graphql/commit/cc1a8ddde06059f1d2aa481e68a433535649f152) Thanks [@angrykoala](https://github.com/angrykoala)! - Add support for filtering GraphQL only events in CDC subscriptions with the option `onlyGraphQLEvents` passed to `Neo4jGraphQLSubscriptionsCDCEngine`

```ts
const engine = new Neo4jGraphQLSubscriptionsCDCEngine({
driver,
onlyGraphQLEvents: true,
});

const neoSchema = new Neo4jGraphQL({
typeDefs,
driver,
features: {
subscriptions: engine,
},
});
```

- [#5834](https://github.com/neo4j/graphql/pull/5834) [`67f915e`](https://github.com/neo4j/graphql/commit/67f915e05c029ce9a595c05cf2f42de232ff2d37) Thanks [@angrykoala](https://github.com/angrykoala)! - CDC subscription optimization. Only node events with labels present in the GraphQL schema will be queried. This will reduce the number of subscription events queried by skipping events to nodes that cannot be subscribed through GraphQL

## 6.1.0

### Minor Changes
Expand Down
6 changes: 3 additions & 3 deletions packages/graphql/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@neo4j/graphql",
"version": "6.1.0",
"version": "6.2.1",
"description": "A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations",
"keywords": [
"neo4j",
Expand Down Expand Up @@ -47,7 +47,7 @@
"@types/is-uuid": "1.0.2",
"@types/jest": "29.5.14",
"@types/jsonwebtoken": "9.0.7",
"@types/node": "22.9.0",
"@types/node": "22.9.2",
"@types/pluralize": "0.0.33",
"@types/randomstring": "1.3.0",
"@types/semver": "7.5.8",
Expand Down Expand Up @@ -79,7 +79,7 @@
"@graphql-tools/resolvers-composition": "^7.0.0",
"@graphql-tools/schema": "^10.0.0",
"@graphql-tools/utils": "^10.0.0",
"@neo4j/cypher-builder": "^2.0.0",
"@neo4j/cypher-builder": "2.0.2",
"camelcase": "^6.3.0",
"debug": "^4.3.4",
"dot-prop": "^6.0.1",
Expand Down
6 changes: 2 additions & 4 deletions packages/graphql/src/classes/Executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ import type {
} from "neo4j-driver";
import { Neo4jError } from "neo4j-driver";
import {
APP_ID,
AUTH_FORBIDDEN_ERROR,
AUTH_UNAUTHENTICATED_ERROR,
DEBUG_EXECUTE,
RELATIONSHIP_REQUIREMENT_PREFIX,
} from "../constants";
import { debugCypherAndParams } from "../debug/debug-cypher-and-params";
import environment from "../environment";
import type { CypherQueryOptions } from "../types";
import {
Neo4jGraphQLAuthenticationError,
Expand Down Expand Up @@ -186,12 +186,10 @@ export class Executor {
}

private getTransactionConfig(info?: GraphQLResolveInfo): TransactionConfig {
const app = `${environment.NPM_PACKAGE_NAME}@${environment.NPM_PACKAGE_VERSION}`;

const transactionConfig: TransactionConfig = {
metadata: {
...this.transactionMetadata,
app,
app: APP_ID,
type: "user-transpiled",
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
* limitations under the License.
*/

import Cypher from "@neo4j/cypher-builder";
import { EventEmitter } from "events";
import type { Driver, QueryConfig } from "neo4j-driver";
import { Memoize } from "typescript-memoize";
import { APP_ID } from "../../constants";
import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel";
import type { Neo4jGraphQLSubscriptionsEngine, SubscriptionEngineContext, SubscriptionsEvent } from "../../types";
import { CDCApi } from "./cdc/cdc-api";
import { CDCEventParser } from "./cdc/cdc-event-parser";
Expand All @@ -33,17 +36,23 @@ export class Neo4jGraphQLSubscriptionsCDCEngine implements Neo4jGraphQLSubscript
private timer: ReturnType<typeof setTimeout> | undefined;
private closed = false;

private subscribeToLabels: string[] | undefined;
private onlyGraphQLEvents: boolean;

constructor({
driver,
pollTime = 1000,
queryConfig,
onlyGraphQLEvents = false,
}: {
driver: Driver;
pollTime?: number;
queryConfig?: QueryConfig;
onlyGraphQLEvents?: boolean;
}) {
this.cdcApi = new CDCApi(driver, queryConfig);
this.pollTime = pollTime;
this.onlyGraphQLEvents = onlyGraphQLEvents;
}

// This memoize is done to keep typings correct whilst avoiding the performance ir of the throw
Expand All @@ -63,6 +72,9 @@ export class Neo4jGraphQLSubscriptionsCDCEngine implements Neo4jGraphQLSubscript
public async init({ schemaModel }: SubscriptionEngineContext): Promise<void> {
await this.cdcApi.updateCursor();
this._parser = new CDCEventParser(schemaModel);
this.subscribeToLabels = this.getLabelsToFilter(schemaModel);

schemaModel.concreteEntities.map((e) => Array.from(e.labels));
this.triggerPoll();
}

Expand Down Expand Up @@ -91,12 +103,25 @@ export class Neo4jGraphQLSubscriptionsCDCEngine implements Neo4jGraphQLSubscript
}

private async pollEvents(): Promise<void> {
const cdcEvents = await this.cdcApi.queryEvents();
let txFilter: Cypher.Map | undefined;
if (this.onlyGraphQLEvents) {
const appMetadata = new Cypher.Param(APP_ID);
txFilter = new Cypher.Map({
app: appMetadata,
});
}
const cdcEvents = await this.cdcApi.queryEvents(this.subscribeToLabels, txFilter);
for (const cdcEvent of cdcEvents) {
const parsedEvent = this.parser.parseCDCEvent(cdcEvent);
if (parsedEvent) {
this.events.emit(parsedEvent.event, parsedEvent);
}
}
}

private getLabelsToFilter(schemaModel: Neo4jGraphQLSchemaModel): string[] {
const uniqueLabels = new Set(schemaModel.concreteEntities.flatMap((e) => Array.from(e.labels)));

return Array.from(uniqueLabels);
}
}
47 changes: 30 additions & 17 deletions packages/graphql/src/classes/subscription/cdc/cdc-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import Cypher from "@neo4j/cypher-builder";
import type { Driver, QueryConfig } from "neo4j-driver";
import { filterTruthy } from "../../../utils/utils";
import type { CDCQueryResponse } from "./cdc-types";

export class CDCApi {
Expand All @@ -33,13 +32,21 @@ export class CDCApi {
}

/** Queries events since last call to queryEvents */
public async queryEvents(): Promise<CDCQueryResponse[]> {
public async queryEvents(labels?: string[], txFilter?: Cypher.Map): Promise<CDCQueryResponse[]> {
if (!this.cursor) {
this.cursor = await this.fetchCurrentChangeId();
}

const cursorLiteral = new Cypher.Literal(this.cursor);
const queryProcedure = CDCProcedures.query(cursorLiteral);

const selectors = this.createQuerySelectors(labels);
if (txFilter) {
selectors.map((selector) => {
selector.set("txMetadata", txFilter);
});
}

const queryProcedure = Cypher.db.cdc.query(cursorLiteral, selectors);

const events = await this.runProcedure<CDCQueryResponse>(queryProcedure);
this.updateChangeIdWithLastEvent(events);
Expand All @@ -51,7 +58,7 @@ export class CDCApi {
}

private async fetchCurrentChangeId(): Promise<string> {
const currentProcedure = CDCProcedures.current();
const currentProcedure = Cypher.db.cdc.current();

const result = await this.runProcedure<{ id: string }>(currentProcedure);

Expand All @@ -69,24 +76,30 @@ export class CDCApi {
}
}

private createQuerySelectors(labels: string[] | undefined): Cypher.Map[] {
if (labels) {
return labels.map(
(l) =>
new Cypher.Map({
select: new Cypher.Literal("n"),
labels: new Cypher.Literal([l]),
})
);
} else {
// Filters nodes
return [
new Cypher.Map({
select: new Cypher.Literal("n"),
}),
];
}
}

private async runProcedure<T>(procedure: Cypher.Clause): Promise<T[]> {
const { cypher, params } = procedure.build();

const result = await this.driver.executeQuery(cypher, params, this.queryConfig);
return result.records.map((record) => {
return record.toObject() as Record<string, any>;
}) as T[];
}
}

/** Wrapper of Cypher Builder for CDC */
class CDCProcedures {
static current(): Cypher.Procedure {
return new Cypher.Procedure<"id">("cdc.current");
}

static query(from: Cypher.Expr, selectors?: Cypher.Expr): Cypher.Procedure {
const procedureParams = filterTruthy([from, selectors]);
return new Cypher.Procedure<"id" | "txId" | "seq" | "metadata" | "event">("cdc.query", procedureParams);
}
}
4 changes: 4 additions & 0 deletions packages/graphql/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* limitations under the License.
*/

import environment from "./environment";

const DEBUG_PREFIX = "@neo4j/graphql";

export const AUTH_FORBIDDEN_ERROR = "@neo4j/graphql/FORBIDDEN";
Expand Down Expand Up @@ -96,3 +98,5 @@ export const DEPRECATED = "deprecated";
export const SHAREABLE = "shareable";
export const PROPAGATED_DIRECTIVES = [SHAREABLE, DEPRECATED] as const;
export const SCORE_FIELD = "score";

export const APP_ID = `${environment.NPM_PACKAGE_NAME}@${environment.NPM_PACKAGE_VERSION}`;
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class AttributeAdapter {
(this.typeHelper.isEnum() ||
this.typeHelper.isSpatial() ||
this.typeHelper.isScalar() ||
(this.isCypherRelationshipField() && !this.typeHelper.isList())) &&
this.isCypherRelationshipField()) &&
this.isFilterable() &&
!this.isCustomResolvable()
);
Expand Down
10 changes: 8 additions & 2 deletions packages/graphql/src/schema/get-obj-field-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,15 +421,21 @@ export function getObjFieldMeta({
}
primitiveField.defaultValue = parseInt(value.value, 10);
break;
case "BigInt":
if (value?.kind !== Kind.INT && value?.kind !== Kind.STRING) {
throw new Error(typeError);
}
primitiveField.defaultValue = parseInt(value.value, 10);
break;
case "Float":
if (value?.kind !== Kind.FLOAT) {
if (value?.kind !== Kind.FLOAT && value?.kind !== Kind.INT) {
throw new Error(typeError);
}
primitiveField.defaultValue = parseFloat(value.value);
break;
default:
throw new Error(
"@default directive can only be used on types: Int | Float | String | Boolean | ID | DateTime | Enum"
"@default directive can only be used on fields of type Int, Float, String, Boolean, ID, BigInt, DateTime, Date, Time, LocalDateTime or LocalTime."
);
}
}
Expand Down
Loading

0 comments on commit d3af3e8

Please sign in to comment.