Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unique #5755

Merged
merged 5 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cyan-grapes-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/graphql": major
---

Remove support for `connectOrCreate` operations
5 changes: 5 additions & 0 deletions .changeset/short-pillows-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/graphql": major
---

Remove support for `@unique` directive
16 changes: 0 additions & 16 deletions packages/graphql/src/classes/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ type MutableField = PrimitiveField | CustomScalarField | CustomEnumField | Union

type AuthableField = PrimitiveField | CustomScalarField | CustomEnumField | UnionField | TemporalField | CypherField;

type ConstrainableField = PrimitiveField | CustomScalarField | CustomEnumField | TemporalField;

export type RootTypeFieldNames = {
create: string;
read: string;
Expand Down Expand Up @@ -178,20 +176,6 @@ class Node extends GraphElement {
];
}

public get constrainableFields(): ConstrainableField[] {
return [
...this.primitiveFields,
...this.scalarFields,
...this.enumFields,
...this.temporalFields,
...this.pointFields,
];
}

public get uniqueFields(): ConstrainableField[] {
return this.constrainableFields.filter((field) => field.unique);
}

private get pascalCaseSingular(): string {
return upperFirst(this.singular);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import type { Driver, Session } from "neo4j-driver";
import { DEBUG_EXECUTE } from "../../constants";
import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel";
import type { ConcreteEntity } from "../../schema-model/entity/ConcreteEntity";
import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter";
import type { Neo4jGraphQLSessionConfig } from "../Executor";

const debug = Debug(DEBUG_EXECUTE);
Expand Down Expand Up @@ -229,36 +228,5 @@ async function getMissingConstraints({

const missingConstraints: MissingConstraint[] = [];

for (const entity of schemaModel.concreteEntities) {
const entityAdapter = new ConcreteEntityAdapter(entity);
for (const uniqueField of entityAdapter.uniqueFields) {
if (!uniqueField.annotations.unique) {
continue;
}

let anyLabelHasConstraint = false;
for (const label of entity.labels) {
// If any of the constraints for the label already exist, skip to the next unique field
if (existingConstraints[label]?.includes(uniqueField.databaseName)) {
anyLabelHasConstraint = true;
break;
}
}

if (anyLabelHasConstraint === false) {
// TODO: The fallback value of `${entity.name}_${uniqueField.databaseName}` should be changed to use the main label of the entity
// But this can only be done once the translation layer has been updated to use the schema model instead of the Node class
const constraintName =
uniqueField.annotations.unique.constraintName || `${entity.name}_${uniqueField.databaseName}`;

missingConstraints.push({
constraintName,
label: entityAdapter.getMainLabel(),
property: uniqueField.databaseName,
});
}
}
}

return missingConstraints;
}
1 change: 0 additions & 1 deletion packages/graphql/src/graphql/directives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,4 @@ export { selectableDirective } from "./selectable";
export { settableDirective } from "./settable";
export { subscriptionDirective } from "./subscription";
export { timestampDirective } from "./timestamp";
export { uniqueDirective } from "./unique";
export { vectorDirective } from "./vector";
34 changes: 0 additions & 34 deletions packages/graphql/src/graphql/directives/unique.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/graphql/src/schema-model/annotation/Annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { parseSettableAnnotation } from "../parser/annotations-parser/settable-a
import { parseSubscriptionAnnotation } from "../parser/annotations-parser/subscription-annotation";
import { parseSubscriptionsAuthorizationAnnotation } from "../parser/annotations-parser/subscriptions-authorization-annotation";
import { parseTimestampAnnotation } from "../parser/annotations-parser/timestamp-annotation";
import { parseUniqueAnnotation } from "../parser/annotations-parser/unique-annotation";
import { parseVectorAnnotation } from "../parser/annotations-parser/vector-annotation";
import type { AuthenticationAnnotation } from "./AuthenticationAnnotation";
import type { AuthorizationAnnotation } from "./AuthorizationAnnotation";
Expand All @@ -64,7 +63,6 @@ import type { SettableAnnotation } from "./SettableAnnotation";
import type { SubscriptionAnnotation } from "./SubscriptionAnnotation";
import type { SubscriptionsAuthorizationAnnotation } from "./SubscriptionsAuthorizationAnnotation";
import type { TimestampAnnotation } from "./TimestampAnnotation";
import type { UniqueAnnotation } from "./UniqueAnnotation";
import type { VectorAnnotation } from "./VectorAnnotation";

export interface Annotation {
Expand Down Expand Up @@ -102,7 +100,6 @@ export type Annotations = CheckAnnotationName<{
subscription: SubscriptionAnnotation;
subscriptionsAuthorization: SubscriptionsAuthorizationAnnotation;
timestamp: TimestampAnnotation;
unique: UniqueAnnotation;
}>;

export type AnnotationParser<T extends Annotation> = (
Expand Down Expand Up @@ -134,7 +131,6 @@ export const annotationsParsers: { [key in keyof Annotations]: AnnotationParser<
subscription: parseSubscriptionAnnotation,
subscriptionsAuthorization: parseSubscriptionsAuthorizationAnnotation,
timestamp: parseTimestampAnnotation,
unique: parseUniqueAnnotation,
relayId: () => new RelayIDAnnotation(),
vector: parseVectorAnnotation,
};
29 changes: 0 additions & 29 deletions packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/

import { CypherAnnotation } from "../../annotation/CypherAnnotation";
import { UniqueAnnotation } from "../../annotation/UniqueAnnotation";
import { Attribute } from "../Attribute";
import {
EnumType,
Expand Down Expand Up @@ -555,18 +554,6 @@ describe("Attribute", () => {
});

describe("annotation assertions", () => {
test("isUnique", () => {
const attribute = new AttributeAdapter(
new Attribute({
name: "test",
annotations: { unique: new UniqueAnnotation({ constraintName: "test" }) },
type: new ScalarType(GraphQLBuiltInScalarType.ID, true),
args: [],
})
);
expect(attribute.isUnique()).toBe(true);
});

test("isCypher", () => {
const attribute = new AttributeAdapter(
new Attribute({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,10 @@ export class AttributeAdapter {
);
}

isUnique(): boolean {
return !!this.annotations.unique || this.isGlobalIDAttribute() === true;
}

isCypher(): boolean {
return !!this.annotations.cypher;
}

isConstrainable(): boolean {
return (
this.typeHelper.isGraphQLBuiltInScalar() ||
this.typeHelper.isUserScalar() ||
this.typeHelper.isEnum() ||
this.typeHelper.isTemporal() ||
this.typeHelper.isSpatial() ||
this.typeHelper.isBigInt()
);
}

isObjectField(): boolean {
return (
this.typeHelper.isScalar() ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/

import { CypherAnnotation } from "../../annotation/CypherAnnotation";
import { UniqueAnnotation } from "../../annotation/UniqueAnnotation";
import { Attribute } from "../../attribute/Attribute";
import { GraphQLBuiltInScalarType, ScalarType } from "../../attribute/AttributeType";
import { AttributeAdapter } from "../../attribute/model-adapters/AttributeAdapter";
Expand All @@ -34,7 +33,7 @@ describe("ConcreteEntityAdapter", () => {
beforeAll(() => {
const idAttribute = new Attribute({
name: "id",
annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) },
annotations: {},
type: new ScalarType(GraphQLBuiltInScalarType.ID, true),
args: [],
});
Expand Down Expand Up @@ -95,11 +94,6 @@ describe("ConcreteEntityAdapter", () => {
expect(userAdapter.getMainLabel()).toBe("User");
});

test("should return the correct unique fields", () => {
expect(userAdapter.uniqueFields).toHaveLength(1);
expect(userAdapter.uniqueFields).toStrictEqual([userId]);
});

test("should return the correct singular name", () => {
expect(userAdapter.singular).toBe("user");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ export class ConcreteEntityAdapter {

// These keys allow to store the keys of the map in memory and avoid keep iterating over the map.
private mutableFieldsKeys: string[] = [];
private uniqueFieldsKeys: string[] = [];
private constrainableFieldsKeys: string[] = [];

private _relatedEntities: EntityAdapter[] | undefined;

Expand Down Expand Up @@ -78,13 +76,6 @@ export class ConcreteEntityAdapter {
this.mutableFieldsKeys.push(attribute.name);
}

if (attributeAdapter.isConstrainable()) {
this.constrainableFieldsKeys.push(attribute.name);
if (attributeAdapter.isUnique()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the whole logic could isConstrainable can be removed from both here and Interfaces, could you confirm this?

this.uniqueFieldsKeys.push(attribute.name);
}
}

if (attributeAdapter.isGlobalIDAttribute()) {
this._globalIdField = attributeAdapter;
}
Expand Down Expand Up @@ -157,14 +148,6 @@ export class ConcreteEntityAdapter {
return this.mutableFieldsKeys.map((key) => getFromMap(this.attributes, key));
}

public get uniqueFields(): AttributeAdapter[] {
return this.uniqueFieldsKeys.map((key) => getFromMap(this.attributes, key));
}

public get constrainableFields(): AttributeAdapter[] {
return this.constrainableFieldsKeys.map((key) => getFromMap(this.attributes, key));
}

public get relatedEntities(): EntityAdapter[] {
if (!this._relatedEntities) {
this._relatedEntities = [...this.relationships.values()].map((relationship) => relationship.target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type { Attribute } from "../../attribute/Attribute";
import { AttributeAdapter } from "../../attribute/model-adapters/AttributeAdapter";
import { RelationshipDeclarationAdapter } from "../../relationship/model-adapters/RelationshipDeclarationAdapter";
import type { RelationshipDeclaration } from "../../relationship/RelationshipDeclaration";
import { getFromMap } from "../../utils/get-from-map";
import { plural, singular } from "../../utils/string-manipulation";
import type { ConcreteEntity } from "../ConcreteEntity";
import type { InterfaceEntity } from "../InterfaceEntity";
Expand All @@ -36,7 +35,6 @@ export class InterfaceEntityAdapter {
public readonly attributes: Map<string, AttributeAdapter> = new Map();
public readonly relationshipDeclarations: Map<string, RelationshipDeclarationAdapter> = new Map();
public readonly annotations: Partial<Annotations>;
private uniqueFieldsKeys: string[] = [];

private _singular: string | undefined;
private _plural: string | undefined;
Expand Down Expand Up @@ -112,10 +110,6 @@ export class InterfaceEntityAdapter {
* used to generate different types for the Entity that contains these Attributes
*/

public get uniqueFields(): AttributeAdapter[] {
return this.uniqueFieldsKeys.map((key) => getFromMap(this.attributes, key));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uniqueFieldsKeys attribute has to be removed as well

}

public get sortableFields(): AttributeAdapter[] {
return Array.from(this.attributes.values()).filter((attribute) => attribute.isSortableField());
}
Expand Down Expand Up @@ -167,9 +161,6 @@ export class InterfaceEntityAdapter {
for (const [attributeName, attribute] of attributes.entries()) {
const attributeAdapter = new AttributeAdapter(attribute);
this.attributes.set(attributeName, attributeAdapter);
if (attributeAdapter.isConstrainable() && attributeAdapter.isUnique()) {
this.uniqueFieldsKeys.push(attribute.name);
}
}
}

Expand Down
5 changes: 2 additions & 3 deletions packages/graphql/src/schema-model/library-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Annotations } from "./annotation/Annotation";
import { annotationsParsers } from "./annotation/Annotation";
import type { DEPRECATED } from "../constants";
import { SHAREABLE } from "../constants";
import type { ValueOf } from "../utils/value-of";
import type { Annotations } from "./annotation/Annotation";
import { annotationsParsers } from "./annotation/Annotation";

const additionalDirectives = [
"alias",
Expand Down Expand Up @@ -60,7 +60,6 @@ export const FIELD_DIRECTIVES = [
"relayId",
"subscriptionsAuthorization",
"timestamp",
"unique",
"declareRelationship",
...SCHEMA_CONFIGURATION_FIELD_DIRECTIVES,
] as const satisfies readonly LibraryDirectives[];
Expand Down
Loading
Loading