Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9b1c402
Add directive annotations (#3615)
mjfwebb Jul 19, 2023
c54a10b
Schema model re-design (#3596)
MacondoExpress Jul 19, 2023
eabd22e
merge schema model changes with authorization changes
MacondoExpress Jul 19, 2023
705d287
fix relationship-field parsing on schema model
MacondoExpress Jul 19, 2023
b6c113a
organise parser folder
MacondoExpress Jul 19, 2023
9ba7489
Add missing GraphQL models to the Schema Models
MacondoExpress Jul 19, 2023
bdb4da8
change Schema Model test to showcase how to dynamically get EntityMod…
MacondoExpress Jul 19, 2023
2a0621c
improve coverage on the Attribute Models
MacondoExpress Jul 20, 2023
48fddb6
add more tests to the ConcreteEntityModel
MacondoExpress Jul 20, 2023
cca7480
Apply suggestions from code review
MacondoExpress Jul 20, 2023
b622a67
lint fixes
MacondoExpress Jul 20, 2023
4ed9e46
rename graphql-models to model-adapters
MacondoExpress Jul 21, 2023
692cb2e
remove @relationshipProperties annotation as it is implicit within th…
MacondoExpress Jul 21, 2023
bef4a2f
remove code duplicity around leadingUnderscores utility and test it
MacondoExpress Jul 21, 2023
25eccd4
merge with 4.0.0
MacondoExpress Jul 24, 2023
37da8bf
parseArguments of directive using default values, add Schema Model te…
MacondoExpress Jul 28, 2023
5828de2
remove not necessary node directive
MacondoExpress Jul 28, 2023
1ae82b0
polishing
MacondoExpress Jul 29, 2023
4ed44b7
add license header
MacondoExpress Jul 29, 2023
68a189f
Update packages/graphql/src/schema-model/parser/parse-arguments.ts
MacondoExpress Jul 31, 2023
dac22c2
add graphqlDefaultValue to Attribute
MacondoExpress Aug 3, 2023
73fbe01
remove the graphqlDefault logic
MacondoExpress Aug 4, 2023
8409271
add comment on the PluralAnnotation
MacondoExpress Aug 4, 2023
6f0c0f2
Merge branch '4.0.0' into schema-model-refactoring
MacondoExpress Aug 4, 2023
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
17 changes: 7 additions & 10 deletions packages/graphql/src/classes/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { GraphElement } from "./GraphElement";
import type { NodeDirective } from "./NodeDirective";
import type { QueryOptionsDirective } from "./QueryOptionsDirective";
import type { SchemaConfiguration } from "../schema/schema-configuration";
import { leadingUnderscores } from "../utils/leading-underscore";

export interface NodeConstructor extends GraphElementConstructor {
name: string;
Expand Down Expand Up @@ -169,8 +170,8 @@ class Node extends GraphElement {
...this.temporalFields,
...this.enumFields,
...this.objectFields,
...this.scalarFields,
...this.primitiveFields,
...this.scalarFields, // these are just custom scalars
...this.primitiveFields, // these are instead built-in scalars
...this.interfaceFields,
...this.objectFields,
...this.unionFields,
Expand All @@ -179,6 +180,7 @@ class Node extends GraphElement {
}

/** Fields you can apply auth allow and bind to */
// Maybe we can remove this as they may not be used anymore in the new auth system
public get authableFields(): AuthableField[] {
return [
...this.primitiveFields,
Expand Down Expand Up @@ -233,6 +235,7 @@ class Node extends GraphElement {
};
}


public get fulltextTypeNames(): FulltextTypeNames {
return {
result: `${this.pascalCaseSingular}FulltextResult`,
Expand Down Expand Up @@ -318,20 +321,14 @@ class Node extends GraphElement {
private generateSingular(): string {
const singular = camelcase(this.name);

return `${this.leadingUnderscores(this.name)}${singular}`;
return `${leadingUnderscores(this.name)}${singular}`;
}

private generatePlural(inputPlural: string | undefined): string {
const name = inputPlural || this.plural || this.name;
const plural = inputPlural || this.plural ? camelcase(name) : pluralize(camelcase(name));

return `${this.leadingUnderscores(name)}${plural}`;
}

private leadingUnderscores(name: string): string {
const re = /^(_+).+/;
const match = re.exec(name);
return match?.[1] || "";
return `${leadingUnderscores(name)}${plural}`;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/graphql/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const NODE_OR_EDGE_KEYS = ["node", "edge"];
export const LOGICAL_OPERATORS = ["AND", "OR", "NOT"] as const;

// aggregation
export const AGGREGATION_COMPARISON_OPERATORS = ["EQUAL", "GT", "GTE", "LT", "LTE"];
export const AGGREGATION_COMPARISON_OPERATORS = ["EQUAL", "GT", "GTE", "LT", "LTE"] as const;
export const AGGREGATION_AGGREGATE_COUNT_OPERATORS = ["count", "count_LT", "count_LTE", "count_GT", "count_GTE"];

export const WHERE_AGGREGATION_TYPES = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const PopulatedByOperationEnum = new GraphQLEnumType({
name: "PopulatedByOperation",
description: "*For use in the @populatedBy directive only*",
values: {
CREATE: {},
UPDATE: {},
CREATE: { value: "CREATE" },
UPDATE: { value: "UPDATE"},
},
});
8 changes: 8 additions & 0 deletions packages/graphql/src/schema-model/Neo4jGraphQLSchemaModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { annotationToKey } from "./annotation/Annotation";
import { CompositeEntity } from "./entity/CompositeEntity";
import { ConcreteEntity } from "./entity/ConcreteEntity";
import type { Entity } from "./entity/Entity";
import { ConcreteEntityAdapter } from "./entity/model-adapters/ConcreteEntityAdapter";

export type Operations = {
Query?: Operation;
Mutation?: Operation;
Expand Down Expand Up @@ -52,6 +54,7 @@ export class Neo4jGraphQLSchemaModel {
acc.set(entity.name, entity);
return acc;
}, new Map<string, Entity>());

this.concreteEntities = concreteEntities;
this.compositeEntities = compositeEntities;
this.operations = operations;
Expand All @@ -65,6 +68,11 @@ export class Neo4jGraphQLSchemaModel {
return this.entities.get(name);
}

public getConcreteEntityAdapter(name: string): ConcreteEntityAdapter | undefined {
const concreteEntity = this.concreteEntities.find((entity) => entity.name === name);
return concreteEntity ? new ConcreteEntityAdapter(concreteEntity) : undefined;
}

public getEntitiesByLabels(labels: string[]): ConcreteEntity[] {
return this.concreteEntities.filter((entity) => entity.matchLabels(labels));
}
Expand Down
102 changes: 99 additions & 3 deletions packages/graphql/src/schema-model/annotation/Annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,81 @@

import { AuthenticationAnnotation } from "./AuthenticationAnnotation";
import { AuthorizationAnnotation } from "./AuthorizationAnnotation";
import { CoalesceAnnotation } from "./CoalesceAnnotation";
import { CustomResolverAnnotation } from "./CustomResolverAnnotation";
import { CypherAnnotation } from "./CypherAnnotation";
import { DefaultAnnotation } from "./DefaultAnnotation";
import { FilterableAnnotation } from "./FilterableAnnotation";
import { FullTextAnnotation } from "./FullTextAnnotation";
import { IDAnnotation } from "./IDAnnotation";
import { JWTClaimAnnotation } from "./JWTClaimAnnotation";
import { JWTPayloadAnnotation } from "./JWTPayloadAnnotation";
import { KeyAnnotation } from "./KeyAnnotation";
import { MutationAnnotation } from "./MutationAnnotation";
import { PluralAnnotation } from "./PluralAnnotation";
import { PopulatedByAnnotation } from "./PopulatedByAnnotation";
import { PrivateAnnotation } from "./PrivateAnnotation";
import { QueryAnnotation } from "./QueryAnnotation";
import { QueryOptionsAnnotation } from "./QueryOptionsAnnotation";
import { SelectableAnnotation } from "./SelectableAnnotation";
import { SettableAnnotation } from "./SettableAnnotation";
import { SubscriptionAnnotation } from "./SubscriptionAnnotation";
import { SubscriptionsAuthorizationAnnotation } from "./SubscriptionsAuthorizationAnnotation";
import { TimestampAnnotation } from "./TimestampAnnotation";
import { UniqueAnnotation } from "./UniqueAnnotation";

export type Annotation =
| CypherAnnotation
| AuthorizationAnnotation
| AuthenticationAnnotation
| KeyAnnotation
| SubscriptionsAuthorizationAnnotation;
| SubscriptionsAuthorizationAnnotation
| QueryOptionsAnnotation
| DefaultAnnotation
| CoalesceAnnotation
| CustomResolverAnnotation
| IDAnnotation
| MutationAnnotation
| PluralAnnotation
| FilterableAnnotation
| FullTextAnnotation
| PopulatedByAnnotation
| QueryAnnotation
| PrivateAnnotation
| SelectableAnnotation
| SettableAnnotation
| TimestampAnnotation
| UniqueAnnotation
| SubscriptionAnnotation
| JWTClaimAnnotation
| JWTPayloadAnnotation;


export enum AnnotationsKey {
cypher = "cypher",
authorization = "authorization",
authentication = "authentication",
authorization = "authorization",
coalesce = "coalesce",
customResolver = "customResolver",
cypher = "cypher",
default = "default",
filterable = "filterable",
fulltext = "fulltext",
id = "id",
jwtClaim = "jwtClaim",
jwtPayload = "jwtPayload",
key = "key",
mutation = "mutation",
plural = "plural",
populatedBy = "populatedBy",
private = "private",
query = "query",
queryOptions = "queryOptions",
selectable = "selectable",
settable = "settable",
subscription = "subscription",
subscriptionsAuthorization = "subscriptionsAuthorization",
timestamp = "timestamp",
unique = "unique",
}

export type Annotations = {
Expand All @@ -44,6 +102,25 @@ export type Annotations = {
[AnnotationsKey.authentication]: AuthenticationAnnotation;
[AnnotationsKey.key]: KeyAnnotation;
[AnnotationsKey.subscriptionsAuthorization]: SubscriptionsAuthorizationAnnotation;
[AnnotationsKey.queryOptions]: QueryOptionsAnnotation;
[AnnotationsKey.default]: DefaultAnnotation;
[AnnotationsKey.coalesce]: CoalesceAnnotation;
[AnnotationsKey.customResolver]: CustomResolverAnnotation;
[AnnotationsKey.id]: IDAnnotation;
[AnnotationsKey.mutation]: MutationAnnotation;
[AnnotationsKey.plural]: PluralAnnotation;
[AnnotationsKey.filterable]: FilterableAnnotation;
[AnnotationsKey.fulltext]: FullTextAnnotation;
[AnnotationsKey.populatedBy]: PopulatedByAnnotation;
[AnnotationsKey.query]: QueryAnnotation;
[AnnotationsKey.private]: PrivateAnnotation;
[AnnotationsKey.selectable]: SelectableAnnotation;
[AnnotationsKey.settable]: SettableAnnotation;
[AnnotationsKey.timestamp]: TimestampAnnotation;
[AnnotationsKey.unique]: UniqueAnnotation;
[AnnotationsKey.subscription]: SubscriptionAnnotation;
[AnnotationsKey.jwtClaim]: JWTClaimAnnotation;
[AnnotationsKey.jwtPayload]: JWTPayloadAnnotation;
};

export function annotationToKey(ann: Annotation): keyof Annotations {
Expand All @@ -52,5 +129,24 @@ export function annotationToKey(ann: Annotation): keyof Annotations {
if (ann instanceof AuthenticationAnnotation) return AnnotationsKey.authentication;
if (ann instanceof KeyAnnotation) return AnnotationsKey.key;
if (ann instanceof SubscriptionsAuthorizationAnnotation) return AnnotationsKey.subscriptionsAuthorization;
if (ann instanceof QueryOptionsAnnotation) return AnnotationsKey.queryOptions;
if (ann instanceof DefaultAnnotation) return AnnotationsKey.default;
if (ann instanceof CoalesceAnnotation) return AnnotationsKey.coalesce;
if (ann instanceof CustomResolverAnnotation) return AnnotationsKey.customResolver;
if (ann instanceof IDAnnotation) return AnnotationsKey.id;
if (ann instanceof MutationAnnotation) return AnnotationsKey.mutation;
if (ann instanceof PluralAnnotation) return AnnotationsKey.plural;
if (ann instanceof FilterableAnnotation) return AnnotationsKey.filterable;
if (ann instanceof FullTextAnnotation) return AnnotationsKey.fulltext;
if (ann instanceof PopulatedByAnnotation) return AnnotationsKey.populatedBy;
if (ann instanceof QueryAnnotation) return AnnotationsKey.query;
if (ann instanceof PrivateAnnotation) return AnnotationsKey.private;
if (ann instanceof SelectableAnnotation) return AnnotationsKey.selectable;
if (ann instanceof SettableAnnotation) return AnnotationsKey.settable;
if (ann instanceof TimestampAnnotation) return AnnotationsKey.timestamp;
if (ann instanceof UniqueAnnotation) return AnnotationsKey.unique;
if (ann instanceof SubscriptionAnnotation) return AnnotationsKey.subscription;
if (ann instanceof JWTClaimAnnotation) return AnnotationsKey.jwtClaim;
if (ann instanceof JWTPayloadAnnotation) return AnnotationsKey.jwtPayload;
throw new Error("annotation not known");
}
28 changes: 28 additions & 0 deletions packages/graphql/src/schema-model/annotation/CoalesceAnnotation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export type CoalesceAnnotationValue = string | number | boolean;

export class CoalesceAnnotation {
public readonly value: CoalesceAnnotationValue;

constructor({ value }: { value: CoalesceAnnotationValue }) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export class CustomResolverAnnotation {
public readonly requires: string;

constructor({ requires }: { requires: string }) {
this.requires = requires;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
* limitations under the License.
*/


export class CypherAnnotation {
public statement: string;
public columnName: string;

constructor({ statement }: { statement: string }) {
constructor({ statement, columnName }: { statement: string, columnName: string }) {
this.statement = statement;
this.columnName = columnName;
}
}
28 changes: 28 additions & 0 deletions packages/graphql/src/schema-model/annotation/DefaultAnnotation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export type DefaultAnnotationValue = string | number | boolean;

export class DefaultAnnotation {
public readonly value: DefaultAnnotationValue;

constructor({ value }: { value: DefaultAnnotationValue }) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export class FilterableAnnotation {
public readonly byValue: boolean;
public readonly byAggregate: boolean;

constructor({ byValue, byAggregate }: { byValue: boolean; byAggregate: boolean }) {
this.byValue = byValue;
this.byAggregate = byAggregate;
}
}
Loading