├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── Our.Umbraco.GraphQL.sln ├── README.md ├── appveyor.yml ├── build.cmd ├── docs └── index.md ├── samples └── Website │ ├── .gitignore │ ├── App_Plugins │ └── Bergmania.OpenStreetMap │ │ ├── bergmania.openstreetmap.controller.js │ │ ├── bergmania.openstreetmap.html │ │ ├── lang │ │ ├── da-DK.xml │ │ └── en-US.xml │ │ ├── lib │ │ ├── autocomplete │ │ │ ├── css │ │ │ │ └── autocomplete.css │ │ │ └── js │ │ │ │ ├── autocomplete.js │ │ │ │ ├── autocomplete.min.js │ │ │ │ └── autocomplete.umd.min.js │ │ └── leaflet │ │ │ ├── images │ │ │ ├── layers-2x.png │ │ │ ├── layers.png │ │ │ ├── marker-icon-2x.png │ │ │ ├── marker-icon.png │ │ │ └── marker-shadow.png │ │ │ ├── leaflet-src.esm.js │ │ │ ├── leaflet-src.esm.js.map │ │ │ ├── leaflet-src.js │ │ │ └── leaflet.css │ │ └── package.manifest │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Startup.cs │ ├── Views │ ├── Blog.cshtml │ ├── Blogpost.cshtml │ ├── MacroPartials │ │ ├── FeaturedProducts.cshtml │ │ └── LatestBlogposts.cshtml │ ├── Partials │ │ ├── CategoryLinks.cshtml │ │ ├── ContactForm.cshtml │ │ ├── Grid │ │ │ ├── Bootstrap3-Fluid.cshtml │ │ │ ├── Bootstrap3.cshtml │ │ │ └── Editors │ │ │ │ ├── Base.cshtml │ │ │ │ ├── Embed.cshtml │ │ │ │ ├── Macro.cshtml │ │ │ │ ├── Media.cshtml │ │ │ │ ├── Rte.cshtml │ │ │ │ └── Textstring.cshtml │ │ ├── Navigation │ │ │ ├── SubNavigation.cshtml │ │ │ └── TopNavigation.cshtml │ │ ├── SectionHeader.cshtml │ │ └── blocklist │ │ │ └── default.cshtml │ ├── Person.cshtml │ ├── Product.cshtml │ ├── Products.cshtml │ ├── _ViewImports.cshtml │ ├── contact.cshtml │ ├── contentPage.cshtml │ ├── home.cshtml │ ├── master.cshtml │ └── people.cshtml │ ├── Website.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── css │ └── umbraco-starterkit-style.css │ ├── favicon.ico │ └── scripts │ └── umbraco-starterkit-app.js ├── src └── Our.Umbraco.GraphQL │ ├── Adapters │ ├── Builders │ │ └── FieldBuilderExtensions.cs │ ├── Examine │ │ ├── Types │ │ │ ├── ExamineGraphTypeExtensions.cs │ │ │ ├── ExamineQuery.cs │ │ │ ├── ExamineSearcherGraphType.cs │ │ │ ├── ExamineSearcherQuery.cs │ │ │ ├── SearchResultFieldsGraphType.cs │ │ │ ├── SearchResultGraphType.cs │ │ │ ├── SearchResultInterfaceGraphType.cs │ │ │ ├── SearchResultsGraphType.cs │ │ │ └── SearchResultsInterfaceGraphType.cs │ │ └── Visitors │ │ │ └── ExamineVisitor.cs │ ├── GraphTypeAdapter.cs │ ├── IGraphTypeAdapter.cs │ ├── PublishedContent │ │ ├── Types │ │ │ ├── BlockListItemGraphType.cs │ │ │ ├── ContentVariationGraphType.cs │ │ │ ├── HtmlEncodedStringGraphType.cs │ │ │ ├── PublishedContentCompositionGraphType.cs │ │ │ ├── PublishedContentGraphType.cs │ │ │ ├── PublishedContentInterfaceGraphType.cs │ │ │ ├── PublishedContentTypeGraphType.cs │ │ │ ├── PublishedElementGraphType.cs │ │ │ ├── PublishedElementInterfaceGraphType.cs │ │ │ ├── PublishedGraphTypeExtensions.cs │ │ │ ├── PublishedItemTypeGraphType.cs │ │ │ ├── PublishedPropertyFieldType.cs │ │ │ └── UrlModeGraphType.cs │ │ └── Visitors │ │ │ └── PublishedContentVisitor.cs │ ├── Resolvers │ │ └── FieldResolver.cs │ ├── Types │ │ ├── GuidGraphType.cs │ │ ├── HtmlGraphType.cs │ │ ├── IdGraphType.cs │ │ ├── JsonGraphType.cs │ │ ├── LinkGraphType.cs │ │ ├── LinkTypeGraphType.cs │ │ ├── OrderByGraphType.cs │ │ ├── Relay │ │ │ ├── ConnectionGraphType.cs │ │ │ ├── EdgeGraphType.cs │ │ │ └── PageInfoGraphType.cs │ │ ├── Resolution │ │ │ ├── ITypeRegistry.cs │ │ │ └── TypeRegistry.cs │ │ └── UdiGraphType.cs │ └── Visitors │ │ ├── CompositeGraphVisitor.cs │ │ ├── GraphVisitor.cs │ │ └── IGraphVisitor.cs │ ├── Attributes │ ├── DefaultValueAttribute.cs │ ├── DeprecatedAttribute.cs │ ├── DescriptionAttribute.cs │ ├── IgnoreAttribute.cs │ ├── InjectAttribute.cs │ ├── NameAttribute.cs │ ├── NonNullAttribute.cs │ └── NonNullItemAttribute.cs │ ├── Builders │ ├── BuiltSchema.cs │ ├── ISchemaBuilder.cs │ └── SchemaBuilder.cs │ ├── Compose │ ├── GraphQLComponent.cs │ ├── GraphQLComposer.cs │ ├── GraphQLPipelineFilter.cs │ └── GraphQLUmbracoOptionsSetup.cs │ ├── Composing │ ├── CompositionExtensions.cs │ ├── FieldMiddlewareCollection.cs │ ├── FieldMiddlewareCollectionBuilder.cs │ ├── GraphVisitorCollection.cs │ └── GraphVisitorCollectionBuilder.cs │ ├── Constants.cs │ ├── FieldMiddleware │ └── IFieldMiddleware.cs │ ├── Filters │ ├── AndFilter.cs │ ├── ContainsFilter.cs │ ├── EndsWithFilter.cs │ ├── EqFilter.cs │ ├── GtFilter.cs │ ├── GteFilter.cs │ ├── IFilter.cs │ ├── InFilter.cs │ ├── LtFilter.cs │ ├── LteFilter.cs │ ├── NotFilter.cs │ ├── OrFilter.cs │ └── StartsWithFilter.cs │ ├── IUserContext.cs │ ├── Json │ └── Converters │ │ └── InputsConverter.cs │ ├── Our.Umbraco.GraphQL.csproj │ ├── Properties │ └── AssemblyInfo.cs │ ├── Reflection │ └── TypeInfoExtensions.cs │ ├── Resources │ └── playground.html │ ├── Types │ ├── ConnectionExtensions.cs │ ├── Id.cs │ ├── Mutation.cs │ ├── OrderBy.cs │ ├── OrderByExtensions.cs │ ├── PublishedContent │ │ ├── PublishedContentAtRootQuery.cs │ │ ├── PublishedContentByTypeQuery.cs │ │ ├── PublishedContentQuery.cs │ │ └── UmbracoQuery.cs │ ├── Query.cs │ ├── Relay │ │ ├── Connection.cs │ │ ├── Edge.cs │ │ └── PageInfo.cs │ ├── Schema.cs │ └── SortOrder.cs │ ├── Web │ ├── GraphQLServerOptions.cs │ └── Middleware │ │ └── GraphQLMiddleware.cs │ ├── content │ └── App_Start │ │ └── GraphQLComponent.cs.pp │ └── readme.txt ├── test └── Our.Umbraco.GraphQL.Tests │ ├── Adapters │ ├── GraphTypeAdapterTests.cs │ ├── PublishedContent │ │ └── Types │ │ │ ├── LinkTypeGraphTypeTests.cs │ │ │ ├── PublishedContentCompositionGraphTypeTests.cs │ │ │ ├── PublishedContentGraphTypeTests.cs │ │ │ ├── PublishedContentInterfaceGraphTypeTests.cs │ │ │ ├── PublishedContentTypeGraphTypeTests.cs │ │ │ ├── PublishedElementGraphTypeTests.cs │ │ │ ├── PublishedElementInterfaceGraphTypeTests.cs │ │ │ ├── PublishedItemTypeGraphType.cs │ │ │ └── UrlModeGraphTypeTests.cs │ ├── Resolvers │ │ └── FieldResolverTests.cs │ ├── Types │ │ ├── GuidGraphTypeTests.cs │ │ ├── HtmlGraphTypeTests.cs │ │ ├── IdGraphTypeTests.cs │ │ ├── JsonGraphTypeTests.cs │ │ ├── LinkGraphTypeTests.cs │ │ ├── LinkTypeGraphTypeTests.cs │ │ ├── OrderByGraphTypeTests.cs │ │ ├── Relay │ │ │ ├── ConnectionGraphTypeTests.cs │ │ │ ├── EdgeGraphTypeTests.cs │ │ │ └── PageInfoGraphTypeTests.cs │ │ ├── Resolution │ │ │ └── TypeRegistryTests.cs │ │ └── UdiGraphTypeTests.cs │ └── Visitors │ │ └── CompositeGraphVisitorTests.cs │ ├── Builders │ └── SchemaBuilderTests.cs │ ├── Filters │ ├── EqFilterTests.cs │ └── FilterTest.cs │ ├── Json │ └── InputConverterTests.cs │ ├── Our.Umbraco.GraphQL.Tests.csproj │ └── Types │ ├── ConnectionExtensionsTests.cs │ ├── IdTests.cs │ ├── OrderByExtensionsTests.cs │ └── PublishedContent │ ├── PublishedContentAtRootQueryTests.cs │ ├── PublishedContentQueryTests.cs │ └── UmbracoQueryTests.cs └── tools ├── .gitignore ├── build.fsx └── build.fsx.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # General settings for whole project 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | # Format specific overrides 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.{css,js,json,yml}] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [develop] 7 | 8 | jobs: 9 | build: 10 | env: 11 | NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} 12 | NUGET_FEED: https://api.nuget.org/v3/index.json 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 6.0.x 21 | 22 | - name: Add Beta source 23 | run: dotnet nuget add source "https://www.myget.org/F/umbracoprereleases/api/v3/index.json" -n "Umbraco Prereleases" 24 | 25 | - name: Build 26 | run: dotnet pack --include-source --include-symbols -p:SymbolPackageFormat=snupkg -c Release src/Our.Umbraco.GraphQL/Our.Umbraco.GraphQL.csproj -o . 27 | 28 | # - name: Test 29 | # run: dotnet test --no-build --verbosity normal 30 | 31 | - name: Publish NuGet 32 | run: dotnet nuget push *.nupkg -s '${{ env.NUGET_FEED }}' -k '${{ env.NUGET_TOKEN }}' 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.suo 3 | *.log 4 | *.swp 5 | *.exe 6 | *.bak 7 | *.orig 8 | *.zip 9 | .vs/ 10 | .idea/ 11 | /build/ 12 | /packages/ 13 | /artifacts/ 14 | bower_components/ 15 | node_modules/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | CurrentSettings.vssettings 19 | Visual Studio 2017/* 20 | /src/Our.Umbraco.GraphQL/umbraco/config/ 21 | /src/Our.Umbraco.GraphQL/umbraco/PartialViewMacros/ 22 | /src/Our.Umbraco.GraphQL/umbraco/UmbracoBackOffice/ 23 | /src/Our.Umbraco.GraphQL/umbraco/UmbracoInstall/ 24 | /src/Our.Umbraco.GraphQL/umbraco/UmbracoWebsite/ 25 | /src/Our.Umbraco.GraphQL/wwwroot/umbraco/ 26 | /samples/Website/umbraco 27 | /samples/Website/wwwroot/umbraco 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | ## 0.1.0 - 2018-10-12 7 | 8 | - First release 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Bug Reports & Feature Requests 4 | 5 | Please use the [issue tracker](https://github.com/rasmusjp/umbraco-graphql/issues) to report any bugs or file feature requests. 6 | 7 | ## Developing 8 | 9 | ### Prerequisites 10 | 11 | - Visual Studio 12 | - IIS Express 13 | 14 | ### Setup 15 | 16 | First build the Solution 17 | 18 | Run the `samples/Website` project, this will start IIS Express listeninng on `http://localhost:49937` 19 | 20 | Install Umbraco with the starter kit, so you have some sample data. 21 | 22 | ### Folder structure 23 | 24 | ``` 25 | . 26 | ├── samples # Sample projects 27 | │   └── Website # Sample/development site 28 | ├── src # Source Files 29 | │   └── Our.Umbraco.GraphQL # Main Project 30 | ├── test # Tests Projects 31 | │   └── Our.Umbraco.GraphQL.Tests # Unit Tests 32 | └── tools # Build tools 33 | ``` 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Rasmus John Pedersen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Our.Umbraco.GraphQL.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32929.385 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{D56E0413-4D79-45DF-BECA-09D6732637F5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Website", "samples\Website\Website.csproj", "{C2001952-0774-4BFA-AF30-043B3B16D275}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BE019E17-BF39-443C-8D62-74940D3B6560}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Our.Umbraco.GraphQL", "src\Our.Umbraco.GraphQL\Our.Umbraco.GraphQL.csproj", "{59294783-3A17-479B-90C5-A3A2967176DE}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Our.Umbraco.GraphQL.Tests", "test\Our.Umbraco.GraphQL.Tests\Our.Umbraco.GraphQL.Tests.csproj", "{9CC632BF-46A8-4BA6-B51F-CDFF3A935974}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9B941129-D270-4DD7-A43E-CF2808BFDEE7}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {C2001952-0774-4BFA-AF30-043B3B16D275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {C2001952-0774-4BFA-AF30-043B3B16D275}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {C2001952-0774-4BFA-AF30-043B3B16D275}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {59294783-3A17-479B-90C5-A3A2967176DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {59294783-3A17-479B-90C5-A3A2967176DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {59294783-3A17-479B-90C5-A3A2967176DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {59294783-3A17-479B-90C5-A3A2967176DE}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {9CC632BF-46A8-4BA6-B51F-CDFF3A935974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {9CC632BF-46A8-4BA6-B51F-CDFF3A935974}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {9CC632BF-46A8-4BA6-B51F-CDFF3A935974}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {9CC632BF-46A8-4BA6-B51F-CDFF3A935974}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | GlobalSection(NestedProjects) = preSolution 40 | {C2001952-0774-4BFA-AF30-043B3B16D275} = {D56E0413-4D79-45DF-BECA-09D6732637F5} 41 | {59294783-3A17-479B-90C5-A3A2967176DE} = {BE019E17-BF39-443C-8D62-74940D3B6560} 42 | {9CC632BF-46A8-4BA6-B51F-CDFF3A935974} = {9B941129-D270-4DD7-A43E-CF2808BFDEE7} 43 | EndGlobalSection 44 | GlobalSection(ExtensibilityGlobals) = postSolution 45 | SolutionGuid = {E20ED350-4522-4159-8FDC-A80A46E7DD7F} 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL for Umbraco 2 | 3 | [![NuGet release](https://img.shields.io/nuget/v/Our.Umbraco.GraphQL.svg)](https://www.nuget.org/packages/Our.Umbraco.GraphQL) 4 | 5 | > **NOTE** 6 | > This branch is for the latest version of this plugin that supports Umbraco version **11**. 7 | 8 | For other versions, check out: 9 | 10 | - [v7](https://github.com/umbraco-community/umbraco-graphql/blob/v7/dev/README.md) 11 | - [v8](https://github.com/umbraco-community/umbraco-graphql/blob/v8/dev/README.md) 12 | - [v9](https://github.com/umbraco-community/umbraco-graphql/blob/v9/dev/README.md) 13 | - [v10](https://github.com/umbraco-community/umbraco-graphql/blob/v10/dev/README.md) 14 | - develop - THIS BRANCH 15 | 16 | ## What is this 17 | 18 | An implementation of [GraphQL](https://graphql.org) for Umbraco using [GraphQL for .NET](https://github.com/graphql-dotnet/graphql-dotnet). 19 | 20 | Please note this **should not be used in production**, since there are **no security** and all you data will be **publicly available**. 21 | 22 | ## How does it work 23 | 24 | GraphQL types are dynamically generated for all Umbraco document types (content and media), with all the properties as fields. 25 | 26 | ## Installation 27 | 28 | The preferred way to install GraphQL for Umbraco is through NuGet 29 | 30 | ### Option 1: NuGet 31 | 32 | GraphQL for Umbraco is available as a [NuGet package](https://www.nuget.org/packages/Our.Umbraco.GraphQL). 33 | 34 | To install run the following command in the [Package Manager Console](https://docs.nuget.org/docs/start-here/using-the-package-manager-console) 35 | 36 | ```powershell 37 | PM> Install-Package Our.Umbraco.GraphQL 38 | ``` 39 | 40 | ### Option 2: From source 41 | 42 | Clone the repository and run the Website (F5 in Visual Studio), install Umbraco with the starter kit and start exploring the API using the GraphQL Playground by opening `/umbraco/graphql`. 43 | 44 | ## Docs 45 | 46 | The docs can be found [here](docs/index.md) 47 | 48 | ## TODO 49 | 50 | - [x] GraphQL Playground 51 | - [x] Schema Stitching (extending types) 52 | - [x] Metrics 53 | - [x] Published Content 54 | - [ ] Published Media 55 | - [ ] Dictionary 56 | - [ ] Statistics (field usage etc.) 57 | - [ ] Deprecation (Content Types and Properties) 58 | - [ ] API Tokens (OAUTH) with permissions (for content types and properties) 59 | - [ ] Data Types 60 | - [ ] Document Types 61 | - [ ] Media Types 62 | - [ ] Member Types 63 | - [ ] Content 64 | - [ ] Media 65 | - [ ] Members 66 | - [ ] Documentation 67 | 68 | ## Contributing 69 | 70 | Anyone can help make this project better - check out our [Contributing guide](CONTRIBUTING.md) 71 | 72 | ## Authors 73 | 74 | - [Rasmus John Pedersen](https://www.github.com/rasmusjp) 75 | 76 | ## License 77 | 78 | Copyright © 2018 Rasmus John Pedersen 79 | 80 | GraphQL for Umbraco is released under the [MIT License](LICENSE) 81 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | os: Visual Studio 2017 3 | shallow_clone: true 4 | init: 5 | - set PATH=C:\Ruby25\bin;%PATH% 6 | - git config --global core.autocrlf input 7 | - git config --global core.longpaths true 8 | install: 9 | - gem install sass 10 | build_script: 11 | build.cmd package 12 | test: off 13 | artifacts: 14 | - path: artifacts\*.nupkg 15 | name: NuGetPackages 16 | deploy: off 17 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL EnableDelayedExpansion 3 | CLS 4 | 5 | SET "buildVersion=" 6 | IF DEFINED APPVEYOR_BUILD_NUMBER ( 7 | SET buildVersion=%APPVEYOR_BUILD_NUMBER% 8 | SET buildVersion=00000!buildVersion! 9 | SET buildVersion=!buildVersion:~-6! 10 | SET buildVersion=build!buildVersion! 11 | ) ELSE ( 12 | SET /p versionSuffix="Please enter a version suffix (e.g. alpha001):" 13 | SET buildVersion=!versionSuffix! 14 | ) 15 | 16 | SET "target=%*" 17 | IF NOT DEFINED target ( 18 | SET "target=default" 19 | ) 20 | 21 | dotnet tool install fake-cli --tool-path tools\bin\ --version 5.* 22 | tools\bin\fake.exe run tools\build.fsx --parallel 3 --target %target% -e buildVersion=!buildVersion! 23 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Configuration 4 | 5 | When installing the [NuGet package](https://www.nuget.org/packages/Our.Umbraco.GraphQL) a new file `App_Start/GraphQLComponent.cs` is added to the project, it contains the bootstrapping code for adding GraphQL to the project and the default configuration 6 | 7 | ```csharp 8 | var path = $"/{_globalSettings.GetUmbracoMvcArea()}/graphql"; 9 | 10 | app.UseUmbracoGraphQL(path, _factory, opts => 11 | { 12 | opts.Debug = HostingEnvironment.IsDevelopmentEnvironment; 13 | opts.EnableMetrics = true; 14 | opts.EnableMiniProfiler = false; 15 | opts.EnablePlayground = true; 16 | }); 17 | ``` 18 | 19 | The configuration options are: 20 | 21 | | Option | Description | 22 | | -------------------------------- | -------------------------------------------------------------------------------------------------------- | 23 | | CorsPolicyProvider | The Cors Policy Provider | 24 | | Debug | Should exceptions be exposed in the response | 25 | | EnableMetrics | Should metrics be enabled | 26 | | EnableMiniProfiler | Should MiniProfiler be enabled | 27 | | EnablePlayground | Should the GraphQL Playground be enabled | 28 | | PlaygroundSettings | Custom settings for the [GraphQL Playground](https://github.com/prisma-labs/graphql-playground#settings) | 29 | | SetCorsPolicy(CorsPolicy policy) | Sets the Cors Policy | 30 | 31 | ## Default Urls 32 | 33 | | Method | Url | Description | 34 | | ------ | ---------------- | ------------------ | 35 | | GET | /umbraco/graphql | GraphQL Playground | 36 | | POST | /umbraco/graphql | GraphQL endpoint | 37 | 38 | ## Querying 39 | 40 | The Umbraco queries/types can be found under the `umbraco` field. 41 | 42 | ```graphql 43 | { 44 | umbraco { 45 | content { 46 | atRoot { 47 | all { 48 | } 49 | # ... document types are added as fields 50 | } 51 | byId(id: "id") { 52 | } 53 | byType { 54 | # ... document types are added as fields 55 | } 56 | byUrl(url: "url") { 57 | } 58 | } 59 | } 60 | } 61 | ``` 62 | 63 | ## Metrics 64 | 65 | [Apollo Tracing](https://github.com/apollographql/apollo-tracing) is enabled by default and is displayed in the GraphQL Playground, it collects the execution time for each field. 66 | 67 | If you need more insight, [Miniprofiler](https://miniprofiler.com/dotnet/) can be enabled by setting the option `EnableMiniprofiler=true`, MiniProfiler is implemented throughout the Umbraco code base and collects a lot of metrics. The data will be accessiblo in the `extensions.miniProfiler` field in the response. 68 | -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/bergmania.openstreetmap.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 12 | 13 |
14 | 15 |
16 |
17 | Latitude: {{model.value.marker.latitude}}, 18 | Longitude: {{model.value.marker.longitude}} 19 |
20 | 29 | 30 |
31 | 32 |
-------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lang/da-DK.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Breddegrad 6 | Længdegrad 7 | 8 | -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lang/en-US.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Latitude 6 | Longitude 7 | 8 | -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umbraco-community/umbraco-graphql/0ebb0284dd9b9ef728367cf2deb24e84af8b8c54/samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umbraco-community/umbraco-graphql/0ebb0284dd9b9ef728367cf2deb24e84af8b8c54/samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/layers.png -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umbraco-community/umbraco-graphql/0ebb0284dd9b9ef728367cf2deb24e84af8b8c54/samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umbraco-community/umbraco-graphql/0ebb0284dd9b9ef728367cf2deb24e84af8b8c54/samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umbraco-community/umbraco-graphql/0ebb0284dd9b9ef728367cf2deb24e84af8b8c54/samples/Website/App_Plugins/Bergmania.OpenStreetMap/lib/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /samples/Website/App_Plugins/Bergmania.OpenStreetMap/package.manifest: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /samples/Website/Program.cs: -------------------------------------------------------------------------------- 1 | namespace umbraco10 2 | { 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | => CreateHostBuilder(args) 7 | .Build() 8 | .Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) => 11 | Host.CreateDefaultBuilder(args) 12 | .ConfigureUmbracoDefaults() 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStaticWebAssets(); 16 | webBuilder.UseStartup(); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/Website/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:7868", 8 | "sslPort": 44362 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "Umbraco.Web.UI": { 20 | "commandName": "Project", 21 | "dotnetRunMessages": true, 22 | "launchBrowser": true, 23 | "applicationUrl": "https://localhost:44362;http://localhost:7868", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /samples/Website/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace umbraco10 2 | { 3 | public class Startup 4 | { 5 | private readonly IWebHostEnvironment _env; 6 | private readonly IConfiguration _config; 7 | 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The web hosting environment. 12 | /// The configuration. 13 | /// 14 | /// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337. 15 | /// 16 | public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config) 17 | { 18 | _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); 19 | _config = config ?? throw new ArgumentNullException(nameof(config)); 20 | } 21 | 22 | /// 23 | /// Configures the services. 24 | /// 25 | /// The services. 26 | /// 27 | /// This method gets called by the runtime. Use this method to add services to the container. 28 | /// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940. 29 | /// 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | services.AddUmbraco(_env, _config) 33 | .AddBackOffice() 34 | .AddWebsite() 35 | .AddComposers() 36 | .Build(); 37 | } 38 | 39 | /// 40 | /// Configures the application. 41 | /// 42 | /// The application builder. 43 | /// The web hosting environment. 44 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 45 | { 46 | if (env.IsDevelopment()) 47 | { 48 | app.UseDeveloperExceptionPage(); 49 | } 50 | 51 | app.UseUmbraco() 52 | .WithMiddleware(u => 53 | { 54 | u.UseBackOffice(); 55 | u.UseWebsite(); 56 | }) 57 | .WithEndpoints(u => 58 | { 59 | u.UseInstallerEndpoints(); 60 | u.UseBackOfficeEndpoints(); 61 | u.UseWebsiteEndpoints(); 62 | }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /samples/Website/Views/Blog.cshtml: -------------------------------------------------------------------------------- 1 | @using ContentModels = Umbraco.Cms.Web.Common.PublishedModels 2 | 3 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 4 | 5 | @{ 6 | Layout = "master.cshtml"; 7 | } 8 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml") 9 | 10 |
11 | 12 |
13 | @await Umbraco.RenderMacroAsync("latestBlogposts", 14 | new 15 | { 16 | numberOfPosts = Model.HowManyPostsShouldBeShown, 17 | startNodeId = Model.Id 18 | }) 19 |
20 | 21 |
-------------------------------------------------------------------------------- /samples/Website/Views/Blogpost.cshtml: -------------------------------------------------------------------------------- 1 | @using ContentModels = Umbraco.Cms.Web.Common.PublishedModels 2 | @using Microsoft.AspNetCore.Mvc.Rendering 3 | @using Umbraco.Extensions 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | @{ 6 | Layout = "master.cshtml"; 7 | } 8 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml") 9 | 10 |
11 |
12 |
13 |
14 | @Model.CreateDate.ToShortDateString() 15 | 16 | @Html.Partial("~/Views/Partials/CategoryLinks.cshtml", Model.Categories) 17 | 18 |
19 |

@Model.Excerpt

20 | @Html.GetGridHtml(Model, "bodyText", "bootstrap3-fluid") 21 | 22 |
23 |
24 |
-------------------------------------------------------------------------------- /samples/Website/Views/MacroPartials/FeaturedProducts.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Core.Models.PublishedContent 2 | @using Umbraco.Cms.Core.Routing 3 | @using Umbraco.Cms.Web.Common 4 | @using Umbraco.Extensions 5 | @inherits Umbraco.Cms.Web.Common.Macros.PartialViewMacroPage 6 | @inject UmbracoHelper Umbraco 7 | @inject IPublishedValueFallback PublishedValueFallback 8 | @inject IPublishedUrlProvider PublishedUrlProvider 9 | @* 10 | This snippet lists the items from a Multinode tree picker, using the pickers default settings. 11 | Content Values stored as xml. 12 | 13 | To get it working with any site's data structure, set the selection equal to the property which has the 14 | multinode treepicker (so: replace "PropertyWithPicker" with the alias of your property). 15 | *@ 16 | 17 | @{ var selection = Model.Content.Value>(PublishedValueFallback, "PropertyWithPicker").ToArray(); } 18 | 19 |
    20 | @foreach (var id in selection) 21 | { 22 | var item = Umbraco.Content(id); 23 |
  • 24 | @item.Name 25 |
  • 26 | } 27 |
28 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/CategoryLinks.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @foreach (var category in Model) 3 | { 4 | 5 | @category 6 | 7 | } 8 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/ContactForm.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @using Umbraco.SampleSite 3 | @model Umbraco.SampleSite.ContactFormViewModel 4 | 5 | @{ 6 | var message = TempData["Message"]?.ToString(); 7 | } 8 |
9 | @using (Html.BeginUmbracoForm(nameof(ContactFormController.Submit))) 10 | { 11 |
12 | @Html.TextBoxFor(m => m.Name, new {@class="form-control", placeholder = Html.DisplayNameFor(m => m.Name)}) 13 | @Html.ValidationMessageFor(m => m.Name) 14 |
15 |
16 | @Html.TextBoxFor(m => m.Email, new {@class="form-control", placeholder = Html.DisplayNameFor(m => m.Email)}) 17 | @Html.ValidationMessageFor(m => m.Email) 18 |
19 |
20 | @Html.TextAreaFor(m => m.Message, new {@class="form-control", placeholder = Html.DisplayNameFor(m => m.Message)}) 21 | @Html.ValidationMessageFor(m => m.Message) 22 |
23 | 24 | @if (!string.IsNullOrEmpty(message)) 25 | { 26 | @message 27 | } 28 | } 29 |
-------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Bootstrap3-Fluid.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web 2 | @using Microsoft.AspNetCore.Html 3 | @using Newtonsoft.Json.Linq 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | 6 | @* 7 | Razor helpers located at the bottom of this file 8 | *@ 9 | 10 | @if (Model is JObject && Model?.sections is not null) 11 | { 12 | var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; 13 | 14 |
15 | @if (oneColumn) 16 | { 17 | foreach (var section in Model.sections) 18 | { 19 |
20 | @foreach (var row in section.rows) 21 | { 22 | renderRow(row); 23 | } 24 |
25 | } 26 | } 27 | else 28 | { 29 |
30 | @foreach (var sec in Model.sections) 31 | { 32 |
33 |
34 | @foreach (var row in sec.rows) 35 | { 36 | renderRow(row); 37 | } 38 |
39 |
40 | } 41 |
42 | } 43 |
44 | } 45 | 46 | @functions{ 47 | 48 | private async Task renderRow(dynamic row) 49 | { 50 |
51 |
52 | @foreach (var area in row.areas) 53 | { 54 |
55 |
56 | @foreach (var control in area.controls) 57 | { 58 | if (control?.editor?.view != null) 59 | { 60 | @await Html.PartialAsync("grid/editors/base", (object)control) 61 | } 62 | } 63 |
64 |
65 | } 66 |
67 |
68 | } 69 | } 70 | 71 | @functions{ 72 | 73 | public static HtmlString RenderElementAttributes(dynamic contentItem) 74 | { 75 | var attrs = new List(); 76 | JObject cfg = contentItem.config; 77 | 78 | if (cfg != null) 79 | { 80 | foreach (JProperty property in cfg.Properties()) 81 | { 82 | var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); 83 | attrs.Add(property.Name + "=\"" + propertyValue + "\""); 84 | } 85 | } 86 | 87 | JObject style = contentItem.styles; 88 | 89 | if (style != null) { 90 | var cssVals = new List(); 91 | foreach (JProperty property in style.Properties()) 92 | { 93 | var propertyValue = property.Value.ToString(); 94 | if (string.IsNullOrWhiteSpace(propertyValue) == false) 95 | { 96 | cssVals.Add(property.Name + ":" + propertyValue + ";"); 97 | } 98 | } 99 | 100 | if (cssVals.Any()) 101 | attrs.Add("style='" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "'"); 102 | } 103 | 104 | return new HtmlString(string.Join(" ", attrs)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Bootstrap3.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web 2 | @using Microsoft.AspNetCore.Html 3 | @using Newtonsoft.Json.Linq 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | 6 | @if (Model is JObject && Model?.sections is not null) 7 | { 8 | var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; 9 | 10 |
11 | @if (oneColumn) 12 | { 13 | foreach (var section in Model.sections) 14 | { 15 |
16 | @foreach (var row in section.rows) 17 | { 18 | renderRow(row, true); 19 | } 20 |
21 | } 22 | } 23 | else 24 | { 25 |
26 |
27 | @foreach (var sec in Model.sections) 28 | { 29 |
30 |
31 | @foreach (var row in sec.rows) 32 | { 33 | renderRow(row, false); 34 | } 35 |
36 |
37 | } 38 |
39 |
40 | } 41 |
42 | } 43 | 44 | @functions{ 45 | 46 | private async Task renderRow(dynamic row, bool singleColumn) 47 | { 48 |
49 | @if (singleColumn) { 50 | @:
51 | } 52 |
53 | @foreach (var area in row.areas) 54 | { 55 |
56 |
57 | @foreach (var control in area.controls) 58 | { 59 | if (control?.editor?.view != null) 60 | { 61 | @await Html.PartialAsync("grid/editors/base", (object)control) 62 | } 63 | } 64 |
65 |
66 | } 67 |
68 | @if (singleColumn) { 69 | @:
70 | } 71 |
72 | } 73 | 74 | } 75 | 76 | @functions{ 77 | 78 | public static HtmlString RenderElementAttributes(dynamic contentItem) 79 | { 80 | var attrs = new List(); 81 | JObject cfg = contentItem.config; 82 | 83 | if (cfg != null) 84 | { 85 | foreach (JProperty property in cfg.Properties()) 86 | { 87 | var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); 88 | attrs.Add(property.Name + "=\"" + propertyValue + "\""); 89 | } 90 | } 91 | 92 | JObject style = contentItem.styles; 93 | 94 | if (style != null) 95 | { 96 | var cssVals = new List(); 97 | foreach (JProperty property in style.Properties()) 98 | { 99 | var propertyValue = property.Value.ToString(); 100 | if (string.IsNullOrWhiteSpace(propertyValue) == false) 101 | { 102 | cssVals.Add(property.Name + ":" + propertyValue + ";"); 103 | } 104 | } 105 | 106 | if (cssVals.Any()) 107 | attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\""); 108 | } 109 | 110 | return new HtmlString(string.Join(" ", attrs)); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Editors/Base.cshtml: -------------------------------------------------------------------------------- 1 | @model dynamic 2 | 3 | @try 4 | { 5 | string editor = EditorView(Model); 6 | @await Html.PartialAsync(editor, Model as object) 7 | } 8 | catch (Exception ex) 9 | { 10 |
@ex.ToString()
11 | } 12 | 13 | @functions{ 14 | 15 | public static string EditorView(dynamic contentItem) 16 | { 17 | string view = contentItem.editor.render != null ? contentItem.editor.render.ToString() : contentItem.editor.view.ToString(); 18 | view = view.Replace(".html", ".cshtml"); 19 | 20 | if (!view.Contains("/")) 21 | { 22 | view = "grid/editors/" + view; 23 | } 24 | 25 | return view; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Editors/Embed.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | 3 | @if (Model is not null) 4 | { 5 | string embedValue = Convert.ToString(Model.value); 6 | embedValue = embedValue.DetectIsJson() ? Model.value.preview : Model.value; 7 | 8 |
9 | @Html.Raw(embedValue) 10 |
11 | } 12 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Editors/Macro.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | 3 | @if (Model?.value is not null) 4 | { 5 | string macroAlias = Model.value.macroAlias.ToString(); 6 | var parameters = new Dictionary(); 7 | foreach (var mpd in Model.value.macroParamsDictionary) 8 | { 9 | parameters.Add(mpd.Name, mpd.Value); 10 | } 11 | 12 | 13 | @await Umbraco.RenderMacroAsync(macroAlias, parameters) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Editors/Media.cshtml: -------------------------------------------------------------------------------- 1 | @model dynamic 2 | @using Umbraco.Cms.Core.Media 3 | @using Umbraco.Cms.Core.PropertyEditors.ValueConverters 4 | @inject IImageUrlGenerator ImageUrlGenerator 5 | 6 | @if (Model?.value is not null) 7 | { 8 | var url = Model.value.image; 9 | 10 | if (Model.editor.config != null && Model.editor.config.size != null) 11 | { 12 | if (Model.value.coordinates != null) 13 | { 14 | url = ImageCropperTemplateCoreExtensions.GetCropUrl( 15 | (string)url, 16 | ImageUrlGenerator, 17 | width: (int)Model.editor.config.size.width, 18 | height: (int)Model.editor.config.size.height, 19 | cropAlias: "default", 20 | cropDataSet: new ImageCropperValue 21 | { 22 | Crops = new[] 23 | { 24 | new ImageCropperValue.ImageCropperCrop 25 | { 26 | Alias = "default", 27 | Coordinates = new ImageCropperValue.ImageCropperCropCoordinates 28 | { 29 | X1 = (decimal)Model.value.coordinates.x1, 30 | Y1 = (decimal)Model.value.coordinates.y1, 31 | X2 = (decimal)Model.value.coordinates.x2, 32 | Y2 = (decimal)Model.value.coordinates.y2 33 | } 34 | } 35 | } 36 | }); 37 | } 38 | else 39 | { 40 | url = ImageCropperTemplateCoreExtensions.GetCropUrl( 41 | (string)url, 42 | ImageUrlGenerator, 43 | width: (int)Model.editor.config.size.width, 44 | height: (int)Model.editor.config.size.height, 45 | cropDataSet: new ImageCropperValue 46 | { 47 | FocalPoint = new ImageCropperValue.ImageCropperFocalPoint 48 | { 49 | Top = Model.value.focalPoint == null ? 0.5m : Model.value.focalPoint.top, 50 | Left = Model.value.focalPoint == null ? 0.5m : Model.value.focalPoint.left 51 | } 52 | }); 53 | } 54 | } 55 | 56 | var altText = Model.value.altText ?? Model.value.caption ?? string.Empty; 57 | 58 | @altText 59 | 60 | if (Model.value.caption != null) 61 | { 62 |

@Model.value.caption

63 | } 64 | } 65 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Editors/Rte.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Core.Templates 2 | @model dynamic 3 | @inject HtmlLocalLinkParser HtmlLocalLinkParser; 4 | @inject HtmlUrlParser HtmlUrlParser; 5 | @inject HtmlImageSourceParser HtmlImageSourceParser; 6 | 7 | @{ 8 | var value = HtmlLocalLinkParser.EnsureInternalLinks(Model?.value.ToString()); 9 | value = HtmlUrlParser.EnsureUrls(value); 10 | value = HtmlImageSourceParser.EnsureImageSources(value); 11 | } 12 | 13 | @Html.Raw(value) 14 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Grid/Editors/Textstring.cshtml: -------------------------------------------------------------------------------- 1 | @model dynamic 2 | 3 | @if (Model?.editor.config.markup is not null) 4 | { 5 | string markup = Model.editor.config.markup.ToString(); 6 | markup = markup.Replace("#value#", Html.ReplaceLineBreaks((string)Model.value.ToString()).ToString()); 7 | 8 | if (Model.editor.config.style != null) 9 | { 10 | markup = markup.Replace("#style#", Model.editor.config.style.ToString()); 11 | } 12 | 13 | 14 | @Html.Raw(markup) 15 | 16 | } 17 | else 18 | { 19 | 20 |
@Model?.value
21 |
22 | } 23 | -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Navigation/SubNavigation.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | @using Umbraco.Cms.Core.Models.PublishedContent 3 | @using Umbraco.Cms.Core.Routing 4 | @using Umbraco.Extensions 5 | 6 | @inject IPublishedValueFallback PublishedValueFallback 7 | @inject IPublishedUrlProvider PublishedUrlProvider 8 | @{ 9 | var siteSection = Model.AncestorOrSelf(2); 10 | var selection = siteSection.Children.Where(x => x.IsVisible(PublishedValueFallback)); 11 | } 12 | 13 | @foreach (var item in selection) 14 | { 15 | @item.Name 16 | } -------------------------------------------------------------------------------- /samples/Website/Views/Partials/Navigation/TopNavigation.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Core.Models.PublishedContent 2 | @using Umbraco.Cms.Core.Routing 3 | @using Umbraco.Extensions 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | @{ 6 | var site = Model.Root(); 7 | var selection = site.Children.Where(x => x.IsVisible()); 8 | } 9 | 10 | @site.Name 11 | @foreach (var item in selection) 12 | { 13 | @item.Name 14 | } -------------------------------------------------------------------------------- /samples/Website/Views/Partials/SectionHeader.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 |
3 |
4 |

@Model.GetProperty("PageTitle").GetValue()

5 |
6 |
-------------------------------------------------------------------------------- /samples/Website/Views/Partials/blocklist/default.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | @{ 3 | if (Model?.Any() != true) { return; } 4 | } 5 |
6 | @foreach (var block in Model) 7 | { 8 | if (block?.ContentUdi == null) { continue; } 9 | var data = block.Content; 10 | 11 | @await Html.PartialAsync("blocklist/Components/" + data.ContentType.Alias, block) 12 | } 13 |
14 | -------------------------------------------------------------------------------- /samples/Website/Views/Person.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "master.cshtml"; 3 | } 4 |

Nothing to see, but we could make a lesson to display a person

-------------------------------------------------------------------------------- /samples/Website/Views/Product.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Web.Common.PublishedModels 2 | @using Umbraco.Extensions 3 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 4 | @{ 5 | Layout = "master.cshtml"; 6 | var defaultCurrency = Model.Parent().DefaultCurrency; 7 | } 8 | 9 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml", Model.Parent) 10 | 11 |
12 |
13 |
14 |
15 |
16 | @Model.ProductName image 17 |
18 |
19 |
20 |

@Model.ProductName

21 |
@defaultCurrency @Model.Price.ToString("F")
22 |
@Model.Description
23 |
24 | 25 |
26 |
27 | @if (Model.Features != null) 28 | { 29 | foreach (var feature in Model.Features) 30 | { 31 |
32 |

@feature.FeatureName

33 |
@feature.FeatureDetails
34 |
35 | } 36 | } 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 45 | @Html.GetGridHtml(Model, "bodyText", "bootstrap3-fluid") 46 |
47 |
-------------------------------------------------------------------------------- /samples/Website/Views/Products.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Web.Common.PublishedModels; 2 | @using Umbraco.Extensions 3 | @using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; 4 | 5 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 6 | 7 | @{ 8 | Layout = "master.cshtml"; 9 | } 10 | 11 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml") 12 | 13 |
14 | 15 |
16 | 17 | 18 | 27 |
28 | @if (Model.FeaturedProducts != null) 29 | { 30 | foreach (Product product in Model.FeaturedProducts) 31 | { 32 | 33 |
34 |
@product.ProductName
35 |
@Model.DefaultCurrency @product.Price.ToString("F")
36 |
37 |
38 | } 39 | } 40 |
41 |
42 | 43 |
-------------------------------------------------------------------------------- /samples/Website/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @using umbraco10 3 | @using Umbraco.Cms.Web.Common.PublishedModels 4 | @using Umbraco.Cms.Web.Common.Views 5 | @using Umbraco.Cms.Core.Models.PublishedContent 6 | @using Microsoft.AspNetCore.Html 7 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 8 | @addTagHelper *, Smidge 9 | @inject Smidge.SmidgeHelper SmidgeHelper 10 | -------------------------------------------------------------------------------- /samples/Website/Views/contact.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.SampleSite; 2 | @using ContentModels = Umbraco.Cms.Web.Common.PublishedModels 3 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 4 | @{ 5 | Layout = "master.cshtml"; 6 | } 7 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml") 8 |
9 |
10 | 11 |
12 |
13 |

@Model.MapHeader

14 | @Model.MapCoordinates 15 | 16 |
17 | 18 |
19 |

@Model.ContactFormHeader

20 | @Model.ContactIntro 21 | @{ 22 | await Html.RenderPartialAsync("Partials/ContactForm", new ContactFormViewModel()); 23 | } 24 |
25 | 26 |
27 |
28 |
-------------------------------------------------------------------------------- /samples/Website/Views/contentPage.cshtml: -------------------------------------------------------------------------------- 1 | @using ContentModels = Umbraco.Cms.Web.Common.PublishedModels 2 | @using Umbraco.Extensions 3 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 4 | @{ 5 | Layout = "master.cshtml"; 6 | } 7 | 8 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml") 9 | 10 |
11 | 12 |
13 | 14 |
15 | 18 |
19 | 20 |
21 |
22 | @Html.GetGridHtml(Model, "bodyText", "bootstrap3-fluid") 23 |
24 |
25 |
26 | 27 |
-------------------------------------------------------------------------------- /samples/Website/Views/home.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @using Umbraco.Extensions 3 | @using Umbraco.Cms.Web.Common.PublishedModels; 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | @{ 6 | Layout = "master.cshtml"; 7 | var backgroundImage = Model.HeroBackgroundImage != null ? Model.HeroBackgroundImage.Url() : String.Empty; 8 | } 9 |
11 |
12 |

@Model.HeroHeader

13 |

@Model.HeroDescription

14 | @if (Model.HeroCtalink != null) 15 | { 16 | 17 | @Model.HeroCtacaption 18 | 19 | } 20 |
21 |
22 | 23 |
24 | @Html.GetGridHtml(Model, "bodyText", "bootstrap3-fluid") 25 |
26 | 27 | 28 |
29 | 30 |
31 |
32 | 33 |
34 |

@Model.FooterHeader

35 |

@Model.FooterDescription

36 | 37 | 38 | @Model.FooterCtacaption 39 | 40 | 41 |
42 | 43 |
44 |
45 | 46 |
-------------------------------------------------------------------------------- /samples/Website/Views/master.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Web.Common.PublishedModels; 2 | @using Umbraco.Extensions 3 | 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | @{ 6 | Layout = null; 7 | // Get basic design settings from the homepage 8 | var home = (Home)Model.Root(); 9 | var font = home.Font; 10 | var colorTheme = home.ColorTheme; 11 | } 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | @Model.Name - @home.Sitename 20 | 21 | 22 | 23 | 24 | @RenderSection("Header", required: false) 25 | 26 | 27 | 28 |
29 | 32 |
33 | 34 |
35 | 36 | 48 | 49 | 52 | 53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 | 61 |
62 | @RenderBody() 63 |
64 | 65 |
66 |
67 |
68 |
69 | @home.FooterAddress 70 |
71 |
72 |
73 |
74 | 75 | @* 76 | Wish not to use JQuery? 77 | Insert this method call to load Umbraco Forms client dependencies without JQuery. @Html.RenderUmbracoFormDependencies() 78 | *@ 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /samples/Website/Views/people.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Web.Common.PublishedModels; 2 | @using Umbraco.Extensions 3 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 4 | @{ 5 | Layout = "master.cshtml"; 6 | } 7 | @{ 8 | void SocialLink(string content, string service) 9 | { 10 | if (!string.IsNullOrEmpty(content)) 11 | { 12 | ; //semicolon needed otherwise cannot be resolved 13 | @service 14 | } 15 | } 16 | } 17 | 18 | @Html.Partial("~/Views/Partials/SectionHeader.cshtml") 19 | 20 |
21 | 22 |
23 | 24 | 33 |
34 | @foreach (Person person in Model.Children()) 35 | { 36 | 37 |
38 |
39 |
40 |

@person.Name

41 | @if (!string.IsNullOrEmpty(person.Email)) 42 | { 43 | 44 | } 45 |
46 | @{ SocialLink(person.FacebookUsername, "Facebook"); } 47 | @{ SocialLink(person.TwitterUsername, "Twitter"); } 48 | @{ SocialLink(person.LinkedInUsername, "LinkedIn"); } 49 | @{ SocialLink(person.InstagramUsername, "Instagram"); } 50 |
51 |
52 |
53 | } 54 |
55 |
56 | 57 |
-------------------------------------------------------------------------------- /samples/Website/Website.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | true 23 | 24 | 25 | 26 | 27 | false 28 | false 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /samples/Website/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./appsettings-schema.json", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information" 6 | }, 7 | "WriteTo": [ 8 | { 9 | "Name": "Async", 10 | "Args": { 11 | "configure": [ 12 | { 13 | "Name": "Console" 14 | } 15 | ] 16 | } 17 | } 18 | ] 19 | }, 20 | "Umbraco": { 21 | "CMS": { 22 | "Content": { 23 | "MacroErrors": "Throw" 24 | }, 25 | "Hosting": { 26 | "Debug": true 27 | }, 28 | "RuntimeMinification": { 29 | "UseInMemoryCache": true, 30 | "CacheBuster": "Timestamp" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/Website/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./appsettings-schema.json", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information", 6 | "Override": { 7 | "Microsoft": "Warning", 8 | "Microsoft.Hosting.Lifetime": "Information", 9 | "System": "Warning" 10 | } 11 | } 12 | }, 13 | "GraphQL": { 14 | "Server": { 15 | "Path": "/umbraco/graphql", 16 | "EnableMetrics": false, 17 | "EnablePlayground": true, 18 | "EnableCors": false 19 | //"Complexity": { 20 | // "MaxDepth": null, 21 | // "MaxComplexity": null, 22 | // "FieldImpact": null, 23 | // "MaxRecursionCount": 250 24 | //}, 25 | //"Playground": { 26 | 27 | //} 28 | } 29 | }, 30 | "Umbraco": { 31 | "CMS": { 32 | "Global": { 33 | "Id": "86da5b07-cc91-4bd3-b90e-35480a43f8e4", 34 | "SanitizeTinyMce": true 35 | }, 36 | "Content": { 37 | "AllowEditInvariantFromNonDefault": true, 38 | "ContentVersionCleanupPolicy": { 39 | "EnableCleanup": true 40 | } 41 | } 42 | } 43 | }, 44 | "ConnectionStrings": { 45 | "umbracoDbDSN": "Data Source=|DataDirectory|/Umbraco10.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True", 46 | "umbracoDbDSN_ProviderName": "Microsoft.Data.Sqlite" 47 | } 48 | } -------------------------------------------------------------------------------- /samples/Website/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umbraco-community/umbraco-graphql/0ebb0284dd9b9ef728367cf2deb24e84af8b8c54/samples/Website/wwwroot/favicon.ico -------------------------------------------------------------------------------- /samples/Website/wwwroot/scripts/umbraco-starterkit-app.js: -------------------------------------------------------------------------------- 1 | !function e(o, r, n) { 2 | function a(l, t) { 3 | if (!r[l]) { 4 | if (!o[l]) { 5 | var s = "function" == typeof require && require; 6 | if (!t && s) return s(l, !0); 7 | if (i) return i(l, !0); 8 | var c = new Error("Cannot find module '" + l + "'"); 9 | throw c.code = "MODULE_NOT_FOUND", c 10 | } 11 | var d = r[l] = { exports: {} }; 12 | o[l][0].call(d.exports, 13 | function(e) { 14 | var r = o[l][1][e]; 15 | return a(r ? r : e) 16 | }, 17 | d, 18 | d.exports, 19 | e, 20 | o, 21 | r, 22 | n) 23 | } 24 | return r[l].exports 25 | } 26 | 27 | for (var i = "function" == typeof require && require, l = 0; l < n.length; l++) a(n[l]); 28 | return a 29 | }({ 30 | 1: [ 31 | function(e, o, r) { 32 | !function() { 33 | "use strict"; 34 | $(document).ready(function() { 35 | $(window).bind("scroll", 36 | function() { 37 | var e = 150; 38 | $(window).scrollTop() > e 39 | ? $(".header").addClass("header--fixed") 40 | : $(".header").removeClass("header--fixed") 41 | }), $(".mobile-nav-handler").click(function(e) { 42 | $(".mobile-nav").toggleClass("mobile-nav--open"), $(".header").toggleClass("header--hide"), 43 | $("body").toggleClass("no-scroll"), $("#toggle-nav").toggleClass("active") 44 | }), $(".nav-link").click(function(e) { 45 | $(".mobile-nav").removeClass("mobile-nav--open"), $(".header").removeClass("header--hide"), 46 | $("body").removeClass("no-scroll"), $("#toggle-nav").removeClass("active") 47 | }) 48 | }) 49 | }() 50 | }, {} 51 | ] 52 | }, 53 | {}, 54 | [1]); -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Builders/FieldBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Builders; 2 | using GraphQL.Types; 3 | using Our.Umbraco.GraphQL.Adapters.Types; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.Builders 6 | { 7 | public static class FieldBuilderExtensions 8 | { 9 | public static FieldBuilder Metadata( 10 | this FieldBuilder builder, string key, object value) 11 | { 12 | builder.FieldType.Metadata.Add(key, value); 13 | return builder; 14 | } 15 | 16 | public static ConnectionBuilder Metadata( 17 | this ConnectionBuilder builder, string key, object value) 18 | { 19 | builder.FieldType.Metadata.Add(key, value); 20 | return builder; 21 | } 22 | 23 | public static ConnectionBuilder Orderable( 24 | this ConnectionBuilder builder) where TNodeType : IComplexGraphType 25 | { 26 | builder.Argument>>>("orderBy", ""); 27 | return builder; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/ExamineQuery.cs: -------------------------------------------------------------------------------- 1 | using Our.Umbraco.GraphQL.Attributes; 2 | 3 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 4 | { 5 | public class ExamineQuery 6 | { 7 | } 8 | 9 | public class ExtendUmbracoQueryWithExamineQuery 10 | { 11 | [NonNull] 12 | [Description("Query Examine indexes")] 13 | public ExamineQuery Examine([Inject] ExamineQuery query) => query; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/ExamineSearcherQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 2 | { 3 | public class ExamineSearcherQuery 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/SearchResultFieldsGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using System.Collections.Generic; 3 | 4 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 5 | { 6 | public class SearchResultFieldsGraphType : ObjectGraphType>> 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/SearchResultGraphType.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using GraphQL.Types; 3 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 4 | using System; 5 | using System.Collections.Generic; 6 | using Umbraco.Cms.Core.PublishedCache; 7 | 8 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 9 | { 10 | public class SearchResultGraphType : ObjectGraphType 11 | { 12 | public SearchResultGraphType(IPublishedSnapshotAccessor snapshotAccessor, string searcherSafeName, IEnumerable fields) 13 | { 14 | Name = $"{searcherSafeName}Result"; 15 | 16 | Interface(); 17 | 18 | this.AddBuiltInFields(fields == null); 19 | 20 | if (fields != null) 21 | { 22 | foreach (var field in fields) 23 | { 24 | Field().Name(field.SafeName()).Resolve(ctx => ctx.Source.AllValues.TryGetValue(field, out var list) ? string.Join(", ", list) : null); 25 | 26 | if (field == "__NodeId") 27 | { 28 | Field().Name("_ContentNode").Description("The published content associated with the specified __NodeId value") 29 | .Resolve(ctx => 30 | { 31 | if (!snapshotAccessor.TryGetPublishedSnapshot(out var publishedSnapshot)) 32 | { 33 | throw new InvalidOperationException("Wasn't possible to a get a valid Snapshot"); 34 | } 35 | 36 | return int.TryParse(ctx.Source.Id, out var id) ? publishedSnapshot.Content.GetById(id) : null; 37 | }); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/SearchResultInterfaceGraphType.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using GraphQL; 3 | using GraphQL.Types; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class SearchResultInterfaceGraphType : InterfaceGraphType 9 | { 10 | private const string TypeName = "SearchResult"; 11 | public SearchResultInterfaceGraphType() 12 | { 13 | Name = TypeName; 14 | 15 | this.AddBuiltInFields(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/SearchResultsGraphType.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using GraphQL.Types; 3 | using System.Collections.Generic; 4 | using Umbraco.Cms.Core.PublishedCache; 5 | 6 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 7 | { 8 | public class SearchResultsGraphType : ObjectGraphType 9 | { 10 | public SearchResultsGraphType(IPublishedSnapshotAccessor snapshotAccessor, string searcherSafeName, IEnumerable fields) 11 | { 12 | Name = $"{searcherSafeName}Results"; 13 | 14 | Interface(); 15 | 16 | this.AddBuiltInFields(snapshotAccessor, searcherSafeName, fields); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Types/SearchResultsInterfaceGraphType.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using GraphQL; 3 | using GraphQL.Types; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class SearchResultsInterfaceGraphType : InterfaceGraphType 9 | { 10 | private const string TypeName = "SearchResults"; 11 | public SearchResultsInterfaceGraphType() 12 | { 13 | Name = TypeName; 14 | 15 | this.AddBuiltInFields(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Examine/Visitors/ExamineVisitor.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using GraphQL.Types; 3 | using Our.Umbraco.GraphQL.Adapters.Examine.Types; 4 | using Our.Umbraco.GraphQL.Adapters.Visitors; 5 | using System; 6 | using System.Linq; 7 | using Umbraco.Cms.Core.PublishedCache; 8 | 9 | namespace Our.Umbraco.GraphQL.Adapters.Examine.Visitors 10 | { 11 | public class ExamineVisitor : GraphVisitor 12 | { 13 | private readonly IPublishedSnapshotAccessor _snapshotAccessor; 14 | private readonly IExamineManager _examineManager; 15 | private readonly Lazy _graphTypeAdapter; 16 | private readonly Lazy _visitor; 17 | 18 | public ExamineVisitor(IPublishedSnapshotAccessor snapshotAccessor, IExamineManager examineManager, Lazy graphTypeAdapter, Lazy visitor) 19 | { 20 | _snapshotAccessor = snapshotAccessor; 21 | _examineManager = examineManager; 22 | _graphTypeAdapter = graphTypeAdapter; 23 | _visitor = visitor; 24 | } 25 | 26 | public override void Visit(ISchema schema) 27 | { 28 | var searchers = _examineManager.Indexes.Select(i => i.Searcher).Concat(_examineManager.RegisteredSearchers).ToList(); 29 | var examineQuery = (ObjectGraphType)_graphTypeAdapter.Value.Adapt(); 30 | 31 | foreach (var searcher in searchers) 32 | { 33 | var loopCaptured = searcher; 34 | var searcherSafeName = searcher.Name.SafeName(); 35 | if (searcherSafeName.EndsWith("Searcher")) searcherSafeName = searcherSafeName.Substring(0, searcherSafeName.Length - 8); 36 | 37 | var graphType = new ExamineSearcherGraphType(_snapshotAccessor, loopCaptured, searcherSafeName); 38 | examineQuery.Field().Name(searcherSafeName).Resolve(ctx => new ExamineSearcherQuery()); 39 | examineQuery.GetField(searcherSafeName).ResolvedType = graphType; 40 | 41 | _visitor.Value.Visit(graphType); 42 | schema.RegisterType(graphType); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/IGraphTypeAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using GraphQL.Types; 3 | 4 | namespace Our.Umbraco.GraphQL.Adapters 5 | { 6 | public interface IGraphTypeAdapter 7 | { 8 | IGraphType Adapt(); 9 | IGraphType Adapt(TypeInfo typeInfo); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/BlockListItemGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using Our.Umbraco.GraphQL.Adapters.Types; 3 | using Umbraco.Cms.Core.Models.Blocks; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | public class BlockListItemGraphType : ObjectGraphType 8 | { 9 | public BlockListItemGraphType() 10 | { 11 | Name = "BlockListItem"; 12 | 13 | Field("content", resolve: ctx => ctx.Source?.Content); 14 | Field("contentUdi", resolve: ctx => ctx.Source?.ContentUdi); 15 | Field("settings", resolve: ctx => ctx.Source?.Settings); 16 | Field("settingsUdi", resolve: ctx => ctx.Source?.SettingsUdi); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/ContentVariationGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Models; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class ContentVariationGraphType : EnumerationGraphType 9 | { 10 | private const string TypeName = "ContentVariation"; 11 | 12 | public ContentVariationGraphType() 13 | { 14 | Name = TypeName; 15 | Description = "Indicates how values can vary."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/HtmlEncodedStringGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Language.AST; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Strings; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | public class HtmlEncodedStringGraphType : ScalarGraphType 8 | { 9 | public HtmlEncodedStringGraphType() 10 | { 11 | Name = "HtmlEncodedString"; 12 | Description = "A string containing HTML code."; 13 | } 14 | 15 | public override object ParseLiteral(IValue value) 16 | { 17 | if (value is StringValue stringValue) 18 | return ParseValue(stringValue.Value); 19 | return null; 20 | } 21 | 22 | public override object ParseValue(object value) 23 | { 24 | if (value is string stringValue) 25 | return new HtmlEncodedString(stringValue); 26 | 27 | return null; 28 | } 29 | 30 | public override object Serialize(object value) 31 | { 32 | if (value is IHtmlEncodedString htmlString) 33 | return htmlString.ToHtmlString(); 34 | 35 | return null; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedContentCompositionGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using Microsoft.AspNetCore.Http; 3 | using Our.Umbraco.GraphQL.Adapters.Types.Resolution; 4 | using Umbraco.Cms.Core.Models; 5 | using Umbraco.Cms.Core.Models.PublishedContent; 6 | using Umbraco.Cms.Core.Routing; 7 | using Umbraco.Cms.Core.Web; 8 | using Umbraco.Extensions; 9 | 10 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 11 | { 12 | public class PublishedContentCompositionGraphType : PublishedContentInterfaceGraphType 13 | { 14 | public PublishedContentCompositionGraphType(IContentTypeComposition contentType, 15 | IPublishedContentType publishedContentType, ITypeRegistry typeRegistry, 16 | IUmbracoContextFactory umbracoContextFactory, IPublishedRouter publishedRouter, 17 | IHttpContextAccessor httpContextAccessor) 18 | { 19 | Name = $"{contentType.Alias.ToPascalCase()}Published{contentType.GetItemType()}"; 20 | 21 | foreach (var propertyType in contentType.CompositionPropertyTypes) 22 | base.AddField(new PublishedPropertyFieldType(publishedContentType, propertyType, typeRegistry, umbracoContextFactory, publishedRouter, httpContextAccessor)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedContentGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Microsoft.AspNetCore.Http; 4 | using Our.Umbraco.GraphQL.Adapters.Types.Resolution; 5 | using Umbraco.Cms.Core.Models; 6 | using Umbraco.Cms.Core.Models.PublishedContent; 7 | using Umbraco.Cms.Core.Routing; 8 | using Umbraco.Cms.Core.Web; 9 | using Umbraco.Extensions; 10 | 11 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 12 | { 13 | public class PublishedContentGraphType : ObjectGraphType 14 | { 15 | public PublishedContentGraphType(IContentTypeComposition contentType, 16 | IPublishedContentType publishedContentType, ITypeRegistry typeRegistry, 17 | IUmbracoContextFactory umbracoContextFactory, IPublishedRouter publishedRouter, 18 | IHttpContextAccessor httpContextAccessor) 19 | { 20 | Name = $"{contentType.Alias.ToPascalCase()}Published{contentType.GetItemType()}"; 21 | IsTypeOf = x => ((IPublishedContent)x).ContentType.Alias == contentType.Alias; 22 | 23 | Interface(); 24 | 25 | this.AddBuiltInFields(); 26 | 27 | foreach (var propertyType in contentType.CompositionPropertyTypes) 28 | base.AddField(new PublishedPropertyFieldType(publishedContentType, propertyType, typeRegistry, umbracoContextFactory, publishedRouter, httpContextAccessor)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedContentInterfaceGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class PublishedContentInterfaceGraphType : InterfaceGraphType 9 | { 10 | private const string TypeName = "PublishedContent"; 11 | public PublishedContentInterfaceGraphType() 12 | { 13 | Name = TypeName; 14 | 15 | this.AddBuiltInFields(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedContentTypeGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class PublishedContentTypeGraphType : ObjectGraphType 9 | { 10 | private const string TypeName = "PublishedContentType"; 11 | public PublishedContentTypeGraphType() 12 | { 13 | Name = TypeName; 14 | 15 | Field>().Name("alias").Resolve(ctx => ctx.Source.Alias); 16 | Field>>().Name("compositionAliases") 17 | .Resolve(ctx => ctx.Source.CompositionAliases); 18 | Field>().Name("itemType").Resolve(ctx => ctx.Source.ItemType); 19 | Field>().Name("variations") 20 | .Resolve(ctx => ctx.Source.Variations); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedElementGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Microsoft.AspNetCore.Http; 4 | using Our.Umbraco.GraphQL.Adapters.Types.Resolution; 5 | using Umbraco.Cms.Core.Models; 6 | using Umbraco.Cms.Core.Models.PublishedContent; 7 | 8 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 9 | { 10 | public class PublishedElementGraphType : ObjectGraphType 11 | { 12 | public PublishedElementGraphType(IContentTypeComposition contentType, 13 | IPublishedContentType publishedContentType, ITypeRegistry typeRegistry, IHttpContextAccessor httpContextAccessor) 14 | { 15 | Name = $"{contentType.Alias.ToPascalCase()}PublishedElement"; 16 | IsTypeOf = x => ((IPublishedElement) x).ContentType.Alias == contentType.Alias; 17 | 18 | Interface(); 19 | 20 | this.AddBuiltInFields(); 21 | 22 | foreach (var propertyType in contentType.CompositionPropertyTypes) 23 | base.AddField(new PublishedPropertyFieldType(publishedContentType, propertyType, typeRegistry, null, null, httpContextAccessor)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedElementInterfaceGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class PublishedElementInterfaceGraphType : InterfaceGraphType 9 | { 10 | private const string TypeName = "PublishedElement"; 11 | public PublishedElementInterfaceGraphType() 12 | { 13 | Name = TypeName; 14 | 15 | this.AddBuiltInFields(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/PublishedItemTypeGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class PublishedItemTypeGraphType : EnumerationGraphType 9 | { 10 | private const string TypeName = "PublishedItemType"; 11 | 12 | public PublishedItemTypeGraphType() 13 | { 14 | Name = TypeName; 15 | Description = "The type of published element."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/PublishedContent/Types/UrlModeGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.PublishedContent.Types 6 | { 7 | [GraphQLMetadata(TypeName)] 8 | public class UrlModeGraphType : EnumerationGraphType 9 | { 10 | private const string TypeName = "UrlMode"; 11 | 12 | public UrlModeGraphType() 13 | { 14 | Name = TypeName; 15 | Description = "Specifies the type of urls that the url provider should produce, Auto is the default."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Resolvers/FieldResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Threading; 5 | using GraphQL; 6 | using GraphQL.Resolvers; 7 | using GraphQL.Types; 8 | using Our.Umbraco.GraphQL.Attributes; 9 | 10 | namespace Our.Umbraco.GraphQL.Adapters.Resolvers 11 | { 12 | public class FieldResolver : IFieldResolver 13 | { 14 | private readonly FieldType _fieldType; 15 | private readonly IServiceProvider _serviceProvider; 16 | 17 | public FieldResolver(FieldType fieldType, IServiceProvider serviceProvider) 18 | { 19 | _fieldType = fieldType ?? throw new ArgumentNullException(nameof(fieldType)); 20 | _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); 21 | } 22 | 23 | public object Resolve(IResolveFieldContext context) 24 | { 25 | var memberInfo = _fieldType.GetMetadata(nameof(MemberInfo)); 26 | var source = context.Source; 27 | if(source == null || memberInfo.DeclaringType.IsInstanceOfType(source) == false) 28 | source = _serviceProvider.GetService(memberInfo.DeclaringType); 29 | 30 | switch (memberInfo) 31 | { 32 | case FieldInfo fieldInfo: 33 | return fieldInfo.GetValue(source); 34 | case MethodInfo methodInfo: 35 | return CallMethod(methodInfo, source, context); 36 | case PropertyInfo propertyInfo: 37 | return propertyInfo.GetValue(source); 38 | default: 39 | throw new ArgumentOutOfRangeException(nameof(context)); 40 | } 41 | } 42 | 43 | private object CallMethod(MethodInfo methodInfo, object source, IResolveFieldContext context) 44 | { 45 | var parameters = methodInfo.GetParameters().ToList(); 46 | var arguments = new object[parameters.Count]; 47 | 48 | var argumentsIndex = 0; 49 | for (var i = 0; i < parameters.Count; i++) 50 | { 51 | var parameterInfo = parameters[i]; 52 | var parameterType = parameterInfo.ParameterType; 53 | 54 | if (parameterInfo.GetCustomAttribute() != null) 55 | { 56 | arguments[i] = _serviceProvider.GetService(parameterType); 57 | continue; 58 | } 59 | 60 | if (parameterInfo.ParameterType == typeof(CancellationToken)) 61 | { 62 | arguments[i] = context.CancellationToken; 63 | continue; 64 | } 65 | 66 | var argument = _fieldType.Arguments[argumentsIndex]; 67 | argumentsIndex++; 68 | 69 | arguments[i] = context.GetArgument(parameterType, parameterInfo.Name, argument.DefaultValue); 70 | } 71 | 72 | return methodInfo.Invoke(source, arguments); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/GuidGraphType.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Adapters.Types 2 | { 3 | public class GuidGraphType : global::GraphQL.Types.GuidGraphType 4 | { 5 | public GuidGraphType() 6 | { 7 | Name = "Guid"; 8 | Description = "Globally Unique Identifier."; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/HtmlGraphType.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using GraphQL.Language.AST; 3 | using GraphQL.Types; 4 | using Microsoft.AspNetCore.Html; 5 | 6 | namespace Our.Umbraco.GraphQL.Adapters.Types 7 | { 8 | public class HtmlGraphType : ScalarGraphType 9 | { 10 | public HtmlGraphType() 11 | { 12 | Name = "HTML"; 13 | Description = "A string containing HTML code."; 14 | } 15 | 16 | public override object ParseLiteral(IValue value) 17 | { 18 | if (value is StringValue stringValue) 19 | return ParseValue(stringValue.Value); 20 | return null; 21 | } 22 | 23 | public override object ParseValue(object value) 24 | { 25 | if(value is string stringValue) 26 | return new HtmlString(stringValue); 27 | 28 | return null; 29 | } 30 | 31 | public override object Serialize(object value) 32 | { 33 | if (value is HtmlString htmlString) 34 | return htmlString.ToString(); 35 | 36 | return null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/IdGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Language.AST; 2 | using Our.Umbraco.GraphQL.Types; 3 | 4 | namespace Our.Umbraco.GraphQL.Adapters.Types 5 | { 6 | public class IdGraphType : global::GraphQL.Types.IdGraphType 7 | { 8 | public override object ParseValue(object value) 9 | { 10 | if (value is Id id) return id; 11 | var parsed = base.ParseValue(value); 12 | return parsed == null ? (Id?)null : new Id(parsed.ToString()); 13 | } 14 | 15 | public override object ParseLiteral(IValue value) 16 | { 17 | var parsed = base.ParseLiteral(value); 18 | return parsed == null ? (Id?)null : new Id(parsed.ToString()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/JsonGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | namespace Our.Umbraco.GraphQL.Adapters.Types 4 | { 5 | public class JsonGraphType : StringGraphType 6 | { 7 | public JsonGraphType() 8 | { 9 | Name = "JSON"; 10 | Description = "The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)."; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/LinkGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using Umbraco.Cms.Core.Models; 3 | 4 | namespace Our.Umbraco.GraphQL.Adapters.Types 5 | { 6 | public class LinkGraphType : ObjectGraphType 7 | { 8 | public LinkGraphType() 9 | { 10 | Name = "Link"; 11 | 12 | Field>("name"); 13 | Field("target"); 14 | Field>("type"); 15 | Field>("url"); 16 | Field("udi"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/LinkTypeGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using Umbraco.Cms.Core.Models; 3 | 4 | namespace Our.Umbraco.GraphQL.Adapters.Types 5 | { 6 | public class LinkTypeGraphType : EnumerationGraphType 7 | { 8 | public LinkTypeGraphType() 9 | { 10 | Name = "LinkType"; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/OrderByGraphType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using GraphQL; 4 | using GraphQL.Types; 5 | using Our.Umbraco.GraphQL.Types; 6 | 7 | namespace Our.Umbraco.GraphQL.Adapters.Types 8 | { 9 | public class OrderByGraphType : EnumerationGraphType 10 | { 11 | public OrderByGraphType(IComplexGraphType graphType) 12 | { 13 | if (graphType == null) throw new ArgumentNullException(nameof(graphType)); 14 | 15 | Name = $"{graphType.Name}Order"; 16 | 17 | foreach (var field in graphType.Fields) 18 | { 19 | Type fieldType = null; 20 | if (field.Type != null) 21 | { 22 | fieldType = field.Type; 23 | if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(NonNullGraphType<>)) 24 | fieldType = fieldType.GenericTypeArguments[0]; 25 | } 26 | 27 | if (field.ResolvedType != null) 28 | { 29 | fieldType = field.ResolvedType is NonNullGraphType nonNullGraphType 30 | ? nonNullGraphType.ResolvedType.GetType() 31 | : field.ResolvedType.GetType(); 32 | } 33 | 34 | if (fieldType == null || typeof(ScalarGraphType).IsAssignableFrom(fieldType) == false) continue; 35 | 36 | var name = field.GetMetadata(nameof(MethodInfo))?.Name ?? field.Name; 37 | 38 | AddValue($"{field.Name}_ASC", $"Order by {field.Name} ascending.", 39 | new OrderBy(name, SortOrder.Ascending, field.Resolver.Resolve)); 40 | AddValue($"{field.Name}_DESC", $"Order by {field.Name} descending.", 41 | new OrderBy(name, SortOrder.Descending, field.Resolver.Resolve)); 42 | } 43 | } 44 | } 45 | 46 | public class OrderByGraphType : OrderByGraphType where TNodeType : IComplexGraphType 47 | { 48 | public OrderByGraphType() : base((IComplexGraphType) typeof(TNodeType).BuildNamedType()) 49 | { 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/Relay/ConnectionGraphType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL; 3 | using GraphQL.Types; 4 | using GraphQL.Types.Relay; 5 | 6 | namespace Our.Umbraco.GraphQL.Adapters.Types.Relay 7 | { 8 | public class ConnectionGraphType : ConnectionType where T : IGraphType 9 | { 10 | public ConnectionGraphType() 11 | { 12 | var text = typeof(T).GraphQLName(); 13 | 14 | Name = text + "ConnectionGraph"; 15 | Description = $"A connection from an object to a list of objects of type `{text}`."; 16 | } 17 | } 18 | 19 | public class ConnectionGraphType : ConnectionGraphType 20 | { 21 | public ConnectionGraphType(IGraphType graphType) 22 | { 23 | ResolvedType = graphType ?? throw new ArgumentNullException(nameof(graphType)); 24 | 25 | Name = $"{graphType.Name}ConnectionGraph"; 26 | Description = $"A connection from an object to a list of objects of type `{graphType.Name}`."; 27 | 28 | GetField("edges").ResolvedType = new ListGraphType(new EdgeGraphType(graphType)); 29 | GetField("items").ResolvedType = new ListGraphType(graphType); 30 | } 31 | 32 | public IGraphType ResolvedType { get; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/Relay/EdgeGraphType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Types; 3 | using GraphQL.Types.Relay; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.Types.Relay 6 | { 7 | public class EdgeGraphType : EdgeType where T : IGraphType 8 | { 9 | } 10 | 11 | public class EdgeGraphType : EdgeGraphType 12 | { 13 | public EdgeGraphType(IGraphType graphType) 14 | { 15 | ResolvedType = graphType ?? throw new ArgumentNullException(nameof(graphType)); 16 | 17 | Name = $"{graphType.Name}Edge"; 18 | Description = $"An edge in a connection from an object to another object of type `{graphType.Name}`."; 19 | 20 | GetField("node").ResolvedType = graphType; 21 | } 22 | 23 | public IGraphType ResolvedType { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/Relay/PageInfoGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types.Relay; 2 | 3 | namespace Our.Umbraco.GraphQL.Adapters.Types.Relay 4 | { 5 | public class PageInfoGraphType : PageInfoType 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/Resolution/ITypeRegistry.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using GraphQL.Types; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.Types.Resolution 6 | { 7 | public interface ITypeRegistry 8 | { 9 | void Add() where TGraphType : IGraphType; 10 | TypeInfo Get(TypeInfo type); 11 | void Extend(); 12 | IEnumerable GetExtending(TypeInfo type); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/Resolution/TypeRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using GraphQL; 6 | using GraphQL.Types; 7 | using Microsoft.AspNetCore.Html; 8 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 9 | using Our.Umbraco.GraphQL.Adapters.Types.Relay; 10 | using Our.Umbraco.GraphQL.Reflection; 11 | using Our.Umbraco.GraphQL.Types; 12 | using Our.Umbraco.GraphQL.Types.Relay; 13 | using Umbraco.Cms.Core.Models.Blocks; 14 | using Umbraco.Cms.Core.Strings; 15 | 16 | namespace Our.Umbraco.GraphQL.Adapters.Types.Resolution 17 | { 18 | public class TypeRegistry : ITypeRegistry 19 | { 20 | private readonly Dictionary _types = new Dictionary(); 21 | private readonly Dictionary> _extends = new Dictionary>(); 22 | 23 | public TypeRegistry() 24 | { 25 | Add(); 26 | Add(); 27 | Add(); 28 | Add(); 29 | Add(); 30 | Add(); 31 | Add(); 32 | Add(); 33 | Add(); 34 | Add(); 35 | Add(); 36 | Add(); 37 | Add(); 38 | Add(); 39 | Add(); 40 | Add(); 41 | Add(); 42 | Add(); 43 | Add(); 44 | Add(); 45 | Add(); 46 | Add(); 47 | } 48 | 49 | public void Add() where TGraphType : IGraphType => 50 | _types[typeof(TType).GetTypeInfo()] = typeof(TGraphType).GetTypeInfo(); 51 | 52 | public TypeInfo Get(TypeInfo type) 53 | { 54 | if (type.IsNullable()) 55 | type = type.GenericTypeArguments[0].GetTypeInfo(); 56 | 57 | return _types.TryGetValue(type, out var graphType) ? graphType : null; 58 | } 59 | 60 | public void Extend() 61 | { 62 | var extend = typeof(TExtend).GetTypeInfo(); 63 | var with = typeof(TWith).GetTypeInfo(); 64 | 65 | if (_extends.TryGetValue(extend, out var list) == false) 66 | { 67 | list = new List(); 68 | _extends.Add(extend, list); 69 | } 70 | 71 | list.Add(with); 72 | } 73 | 74 | public IEnumerable GetExtending(TypeInfo type) => 75 | _extends.TryGetValue(type, out var list) 76 | ? list.AsReadOnly() 77 | : Enumerable.Empty(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Types/UdiGraphType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Language.AST; 2 | using GraphQL.Types; 3 | using Umbraco.Cms.Core; 4 | 5 | namespace Our.Umbraco.GraphQL.Adapters.Types 6 | { 7 | public class UdiGraphType : ScalarGraphType 8 | { 9 | public UdiGraphType() 10 | { 11 | Name = "UDI"; 12 | Description = "Represents an entity identifier."; 13 | } 14 | 15 | public override object ParseLiteral(IValue value) => value is StringValue stringValue ? ParseValue(stringValue.Value) : null; 16 | public override object ParseValue(object value) => value is Udi udi ? udi : (UdiParser.TryParse(value?.ToString(), out udi) ? udi : default); 17 | public override object Serialize(object value) => value is Udi udi ? udi.ToString() : null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Visitors/CompositeGraphVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Types; 3 | 4 | namespace Our.Umbraco.GraphQL.Adapters.Visitors 5 | { 6 | public class CompositeGraphVisitor : GraphVisitor 7 | { 8 | private readonly IGraphVisitor[] _visitors; 9 | 10 | public CompositeGraphVisitor(params IGraphVisitor[] visitors) 11 | { 12 | _visitors = visitors ?? throw new ArgumentNullException(nameof(visitors)); 13 | } 14 | 15 | public override void Visit(EnumerationGraphType graphType) 16 | { 17 | foreach (var visitor in _visitors) visitor.Visit(graphType); 18 | } 19 | 20 | public override void Visit(IInputObjectGraphType graphType) 21 | { 22 | 23 | foreach (var visitor in _visitors) visitor.Visit(graphType); 24 | } 25 | 26 | public override void Visit(IInterfaceGraphType graphType) 27 | { 28 | foreach (var visitor in _visitors) visitor.Visit(graphType); 29 | } 30 | 31 | public override void Visit(IObjectGraphType graphType) 32 | { 33 | foreach (var visitor in _visitors) visitor.Visit(graphType); 34 | } 35 | 36 | public override void Visit(ISchema schema) 37 | { 38 | foreach (var visitor in _visitors) visitor.Visit(schema); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Visitors/GraphVisitor.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | namespace Our.Umbraco.GraphQL.Adapters.Visitors 4 | { 5 | public abstract class GraphVisitor : IGraphVisitor 6 | { 7 | public virtual void Visit(EnumerationGraphType graphType) 8 | { 9 | } 10 | 11 | public virtual void Visit(IInputObjectGraphType graphType) 12 | { 13 | } 14 | 15 | public virtual void Visit(IInterfaceGraphType graphType) 16 | { 17 | } 18 | 19 | public virtual void Visit(IObjectGraphType graphType) 20 | { 21 | } 22 | 23 | public virtual void Visit(ISchema schema) 24 | { 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Adapters/Visitors/IGraphVisitor.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | namespace Our.Umbraco.GraphQL.Adapters.Visitors 4 | { 5 | public interface IGraphVisitor 6 | { 7 | void Visit(EnumerationGraphType graphType); 8 | void Visit(IInputObjectGraphType graphType); 9 | void Visit(IInterfaceGraphType graphType); 10 | void Visit(IObjectGraphType graphType); 11 | void Visit(ISchema schema); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/DefaultValueAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | 6 | AttributeTargets.Property)] 7 | public class DefaultValueAttribute : Attribute 8 | { 9 | public DefaultValueAttribute(object defaultValue) 10 | { 11 | DefaultValue = defaultValue; 12 | } 13 | 14 | public object DefaultValue { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/DeprecatedAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | 6 | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Property | 7 | AttributeTargets.Struct)] 8 | public class DeprecatedAttribute : Attribute 9 | { 10 | public DeprecatedAttribute(string deprecationReason) 11 | { 12 | DeprecationReason = deprecationReason; 13 | } 14 | 15 | public string DeprecationReason { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/DescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | 6 | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | 7 | AttributeTargets.Property | AttributeTargets.Struct)] 8 | public class DescriptionAttribute : Attribute 9 | { 10 | public DescriptionAttribute(string description) 11 | { 12 | Description = description; 13 | } 14 | 15 | public string Description { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/IgnoreAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property)] 6 | public class IgnoreAttribute : Attribute 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/InjectAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Parameter)] 6 | public class InjectAttribute : Attribute 7 | { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/NameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | 6 | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | 7 | AttributeTargets.Property | AttributeTargets.Struct)] 8 | public class NameAttribute : Attribute 9 | { 10 | public NameAttribute(string name) 11 | { 12 | Name = name ?? throw new ArgumentNullException(nameof(name)); 13 | } 14 | 15 | public string Name { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/NonNullAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | 6 | AttributeTargets.Property)] 7 | public class NonNullAttribute : Attribute 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Attributes/NonNullItemAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | 6 | AttributeTargets.Property)] 7 | public class NonNullItemAttribute : Attribute 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Builders/BuiltSchema.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Conversion; 3 | using GraphQL.Instrumentation; 4 | using GraphQL.Introspection; 5 | using GraphQL.Types; 6 | using GraphQL.Utilities; 7 | using Our.Umbraco.GraphQL.Types; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Reflection; 11 | 12 | namespace Our.Umbraco.GraphQL.Builders 13 | { 14 | public class BuiltSchema : ISchema 15 | { 16 | private readonly ISchemaBuilder _builder; 17 | private Lazy _schema; 18 | 19 | public BuiltSchema(ISchemaBuilder builder) 20 | { 21 | _builder = builder; 22 | _schema = new Lazy(BuildSchema); 23 | } 24 | 25 | public ExperimentalFeatures Features { get => _schema.Value.Features; set => _schema.Value.Features = value; } 26 | public bool Initialized => _schema.Value.Initialized; 27 | public INameConverter NameConverter => _schema.Value.NameConverter; 28 | public IFieldMiddlewareBuilder FieldMiddleware => _schema.Value.FieldMiddleware; 29 | public IObjectGraphType Query { get => _schema.Value.Query; set => _schema.Value.Query = value; } 30 | public IObjectGraphType Mutation { get => _schema.Value.Mutation; set => _schema.Value.Mutation = value; } 31 | public IObjectGraphType Subscription { get => _schema.Value.Subscription; set => _schema.Value.Subscription = value; } 32 | public SchemaDirectives Directives => _schema.Value.Directives; 33 | public SchemaTypes AllTypes => _schema.Value.AllTypes; 34 | public IEnumerable AdditionalTypes => _schema.Value.AdditionalTypes; 35 | public IEnumerable AdditionalTypeInstances => _schema.Value.AdditionalTypeInstances; 36 | public IEnumerable<(Type clrType, Type graphType)> TypeMappings => _schema.Value.TypeMappings; 37 | public IEnumerable<(Type clrType, Type graphType)> BuiltInTypeMappings => _schema.Value.BuiltInTypeMappings; 38 | public ISchemaFilter Filter { get => _schema.Value.Filter; set => _schema.Value.Filter = value; } 39 | public ISchemaComparer Comparer { get => _schema.Value.Comparer; set => _schema.Value.Comparer = value; } 40 | public FieldType SchemaMetaFieldType => _schema.Value.SchemaMetaFieldType; 41 | public FieldType TypeMetaFieldType => _schema.Value.TypeMetaFieldType; 42 | public FieldType TypeNameMetaFieldType => _schema.Value.TypeNameMetaFieldType; 43 | public Dictionary Metadata => _schema.Value.Metadata; 44 | public string Description { get => _schema.Value.Description; set => _schema.Value.Description = value; } 45 | 46 | public TType GetMetadata(string key, TType defaultValue = default) => _schema.Value.GetMetadata(key, defaultValue); 47 | public TType GetMetadata(string key, Func defaultValueFactory) => _schema.Value.GetMetadata(key, defaultValueFactory); 48 | public bool HasMetadata(string key) => _schema.Value.HasMetadata(key); 49 | public void Initialize() => _schema.Value.Initialize(); 50 | public void RegisterType(IGraphType type) => _schema.Value.RegisterType(type); 51 | public void RegisterType(Type type) => _schema.Value.RegisterType(type); 52 | public void RegisterTypeMapping(Type clrType, Type graphType) => _schema.Value.RegisterTypeMapping(clrType, graphType); 53 | public void RegisterVisitor(ISchemaNodeVisitor visitor) => _schema.Value.RegisterVisitor(visitor); 54 | public void RegisterVisitor(Type type) => _schema.Value.RegisterVisitor(type); 55 | 56 | public void InvalidateSchema() 57 | { 58 | if (!_schema.IsValueCreated) return; 59 | _schema = new Lazy(BuildSchema); 60 | } 61 | 62 | private ISchema BuildSchema() => _builder.Build(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Builders/ISchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using System; 3 | 4 | namespace Our.Umbraco.GraphQL.Builders 5 | { 6 | public interface ISchemaBuilder 7 | { 8 | ISchema Build(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Builders/SchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using GraphQL.Types; 4 | using Our.Umbraco.GraphQL.Adapters; 5 | using Our.Umbraco.GraphQL.Adapters.Visitors; 6 | using Our.Umbraco.GraphQL.Types; 7 | 8 | namespace Our.Umbraco.GraphQL.Builders 9 | { 10 | public class SchemaBuilder : ISchemaBuilder 11 | { 12 | private readonly IGraphTypeAdapter _graphTypeAdapter; 13 | private readonly IGraphVisitor _visitor; 14 | private readonly IServiceProvider _serviceProvider; 15 | 16 | public SchemaBuilder(IGraphTypeAdapter graphTypeAdapter, IServiceProvider serviceProvider, IGraphVisitor visitor) 17 | { 18 | _graphTypeAdapter = graphTypeAdapter ?? throw new ArgumentNullException(nameof(graphTypeAdapter)); 19 | _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); 20 | _visitor = visitor; 21 | } 22 | 23 | public virtual Type SchemaType => typeof(Schema); 24 | 25 | public virtual ISchema Build() 26 | { 27 | var schema = new Schema(_serviceProvider); 28 | 29 | AddProperties(schema); 30 | _visitor?.Visit(schema); 31 | 32 | return schema; 33 | } 34 | 35 | protected virtual void AddProperties(Schema schema) 36 | { 37 | schema.Query = GenerateFromProperty("Query", true); 38 | } 39 | 40 | protected virtual IObjectGraphType GenerateFromProperty(string propertyName, bool throwError) 41 | { 42 | var queryPropertyInfo = SchemaType.GetProperty(propertyName); 43 | if (queryPropertyInfo == null) 44 | { 45 | if (throwError) throw new ArgumentException($"Could not find property '{propertyName}' on {SchemaType.FullName}."); 46 | return null; 47 | } 48 | 49 | if (queryPropertyInfo.CanRead == false) 50 | { 51 | if (throwError) throw new ArgumentException($"Could not find getter for '{propertyName}' on {SchemaType.FullName}."); 52 | return null; 53 | } 54 | 55 | return (IObjectGraphType)_graphTypeAdapter.Adapt(queryPropertyInfo.GetMethod.ReturnType.GetTypeInfo()); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Compose/GraphQLComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Examine; 3 | using Newtonsoft.Json.Linq; 4 | using Our.Umbraco.GraphQL.Adapters.Examine.Types; 5 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 6 | using Our.Umbraco.GraphQL.Adapters.Types; 7 | using Our.Umbraco.GraphQL.Adapters.Types.Resolution; 8 | using Our.Umbraco.GraphQL.Types; 9 | using Our.Umbraco.GraphQL.Types.PublishedContent; 10 | using Umbraco.Cms.Core; 11 | using Umbraco.Cms.Core.Composing; 12 | using Umbraco.Cms.Core.Models.PublishedContent; 13 | using Umbraco.Cms.Core.Models; 14 | 15 | namespace Our.Umbraco.GraphQL.Compose 16 | { 17 | public class GraphQLComponent : IComponent 18 | { 19 | private readonly ITypeRegistry _typeRegistry; 20 | 21 | public GraphQLComponent(ITypeRegistry typeRegistry) 22 | { 23 | _typeRegistry = typeRegistry ?? throw new ArgumentNullException(nameof(typeRegistry)); 24 | } 25 | 26 | public void Initialize() 27 | { 28 | _typeRegistry.Add(); 29 | _typeRegistry.Add(); 30 | _typeRegistry.Add(); 31 | _typeRegistry.Add(); 32 | _typeRegistry.Add(); 33 | _typeRegistry.Add(); 34 | _typeRegistry.Add(); 35 | _typeRegistry.Add(); 36 | _typeRegistry.Add(); 37 | _typeRegistry.Add(); 38 | _typeRegistry.Add(); 39 | 40 | _typeRegistry.Extend(); 41 | _typeRegistry.Extend(); 42 | _typeRegistry.Extend(); 43 | } 44 | 45 | public void Terminate() 46 | { 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Compose/GraphQLPipelineFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Our.Umbraco.GraphQL.Web; 3 | using Our.Umbraco.GraphQL.Web.Middleware; 4 | using Umbraco.Cms.Web.Common.ApplicationBuilder; 5 | 6 | namespace Our.Umbraco.GraphQL.Compose 7 | { 8 | public class GraphQLPipelineFilter : UmbracoPipelineFilter 9 | { 10 | public GraphQLPipelineFilter(GraphQLServerOptions options) : base(nameof(GraphQLPipelineFilter)) 11 | { 12 | PostPipeline = a => 13 | { 14 | if (options.EnableCors && !string.IsNullOrWhiteSpace(options.CorsPolicyName)) 15 | { 16 | a.UseCors(options.CorsPolicyName); 17 | } 18 | a.UseMiddleware(); 19 | }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Compose/GraphQLUmbracoOptionsSetup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using Our.Umbraco.GraphQL.Web; 3 | using Umbraco.Cms.Web.Common.ApplicationBuilder; 4 | 5 | namespace Our.Umbraco.GraphQL.Compose 6 | { 7 | public class GraphQLUmbracoOptionsSetup : IConfigureOptions 8 | { 9 | private readonly IOptions _options; 10 | 11 | public GraphQLUmbracoOptionsSetup(IOptions options) 12 | { 13 | _options = options; 14 | } 15 | 16 | public void Configure(UmbracoPipelineOptions options) 17 | { 18 | options.AddFilter(new GraphQLPipelineFilter(_options.Value)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Composing/CompositionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Umbraco.Cms.Core.DependencyInjection; 2 | 3 | namespace Our.Umbraco.GraphQL.Composing 4 | { 5 | public static class CompositionExtensions 6 | { 7 | public static GraphVisitorCollectionBuilder GraphVisitors(this IUmbracoBuilder builder) => 8 | builder.WithCollectionBuilder(); 9 | 10 | public static FieldMiddlewareCollectionBuilder FieldMiddlewares(this IUmbracoBuilder builder) => 11 | builder.WithCollectionBuilder(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Composing/FieldMiddlewareCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Our.Umbraco.GraphQL.Middleware; 4 | using Umbraco.Cms.Core.Composing; 5 | 6 | namespace Our.Umbraco.GraphQL.Composing 7 | { 8 | public class FieldMiddlewareCollection : BuilderCollectionBase 9 | { 10 | public FieldMiddlewareCollection(Func> items) 11 | : base(items) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Composing/FieldMiddlewareCollectionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Our.Umbraco.GraphQL.Middleware; 3 | using Umbraco.Cms.Core.Composing; 4 | 5 | namespace Our.Umbraco.GraphQL.Composing 6 | { 7 | public class FieldMiddlewareCollectionBuilder : OrderedCollectionBuilderBase 8 | { 9 | protected override ServiceLifetime CollectionLifetime => ServiceLifetime.Scoped; 10 | 11 | protected override FieldMiddlewareCollectionBuilder This => this; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Composing/GraphVisitorCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Our.Umbraco.GraphQL.Adapters.Visitors; 4 | using Umbraco.Cms.Core.Composing; 5 | 6 | namespace Our.Umbraco.GraphQL.Composing 7 | { 8 | public class GraphVisitorCollection : BuilderCollectionBase 9 | { 10 | public GraphVisitorCollection(Func> items) 11 | : base(items) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Composing/GraphVisitorCollectionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Our.Umbraco.GraphQL.Adapters.Visitors; 3 | using Umbraco.Cms.Core.Composing; 4 | 5 | namespace Our.Umbraco.GraphQL.Composing 6 | { 7 | public class GraphVisitorCollectionBuilder : OrderedCollectionBuilderBase 8 | { 9 | public GraphVisitorCollectionBuilder() 10 | { 11 | } 12 | 13 | protected override ServiceLifetime CollectionLifetime => ServiceLifetime.Singleton; 14 | 15 | protected override GraphVisitorCollectionBuilder This => this; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL 2 | { 3 | public class Constants 4 | { 5 | public const string ProductName = "Our.Umbraco.GraphQL"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/FieldMiddleware/IFieldMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using GraphQL; 3 | using GraphQL.Instrumentation; 4 | 5 | namespace Our.Umbraco.GraphQL.Middleware 6 | { 7 | public interface IFieldMiddleware 8 | { 9 | Task Resolve(ResolveFieldContext context, FieldMiddlewareDelegate next); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/AndFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Our.Umbraco.GraphQL.Filters 6 | { 7 | public class AndFilter : IFilter 8 | { 9 | private readonly IEnumerable _subFilters; 10 | 11 | public AndFilter(IEnumerable subFilters) 12 | { 13 | _subFilters = subFilters ?? throw new ArgumentNullException(nameof(subFilters)); 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | foreach (var filter in _subFilters) 19 | { 20 | if (false == filter.IsSatisfiedBy(input)) return false; 21 | } 22 | 23 | return true; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/ContainsFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Filters 4 | { 5 | public class ContainsFilter : IFilter 6 | { 7 | private readonly string _value; 8 | private readonly Func _valueResolver; 9 | 10 | public ContainsFilter(Func valueResolver, object value) 11 | { 12 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 13 | _value = Convert.ToString(value); 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | var value = _valueResolver(input); 19 | if (value == null || _value == null) 20 | { 21 | return false; 22 | } 23 | return Convert.ToString(value).IndexOf(_value, StringComparison.InvariantCultureIgnoreCase) > -1; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/EndsWithFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Filters 4 | { 5 | public class EndsWithFilter : IFilter 6 | { 7 | private readonly string _value; 8 | private readonly Func _valueResolver; 9 | 10 | public EndsWithFilter(Func valueResolver, object value) 11 | { 12 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 13 | _value = Convert.ToString(value); 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | var value = _valueResolver(input); 19 | if (value == null || _value == null) 20 | { 21 | return false; 22 | } 23 | return Convert.ToString(value).EndsWith(_value, StringComparison.InvariantCultureIgnoreCase); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/EqFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Filters 4 | { 5 | public class EqFilter : IFilter 6 | { 7 | private readonly object _value; 8 | private readonly Func _valueResolver; 9 | 10 | public EqFilter(Func valueResolver, object value) 11 | { 12 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 13 | _value = value; 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | var value = _valueResolver(input); 19 | return Equals(value, _value); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/GtFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Our.Umbraco.GraphQL.Filters 5 | { 6 | public class GtFilter : IFilter 7 | { 8 | private readonly object _value; 9 | private readonly Func _valueResolver; 10 | 11 | public GtFilter(Func valueResolver, object value) 12 | { 13 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 14 | _value = value; 15 | } 16 | 17 | public bool IsSatisfiedBy(object input) 18 | { 19 | var value = _valueResolver(input); 20 | return Comparer.DefaultInvariant.Compare(value, _value) > 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/GteFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Our.Umbraco.GraphQL.Filters 5 | { 6 | public class GteFilter : IFilter 7 | { 8 | private readonly object _value; 9 | private readonly Func _valueResolver; 10 | 11 | public GteFilter(Func valueResolver, object value) 12 | { 13 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 14 | _value = value; 15 | } 16 | 17 | public bool IsSatisfiedBy(object input) 18 | { 19 | var value = _valueResolver(input); 20 | return Comparer.DefaultInvariant.Compare(value, _value) >= 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/IFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Filters 2 | { 3 | public interface IFilter 4 | { 5 | bool IsSatisfiedBy(object input); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/InFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Filters 4 | { 5 | public class InFilter : IFilter 6 | { 7 | private readonly object[] _value; 8 | private readonly Func _valueResolver; 9 | 10 | public InFilter(Func valueResolver, object value) 11 | { 12 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 13 | _value = (object[]) value; 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | var value = _valueResolver(input); 19 | return Array.Exists(_value, x => Equals(x, value)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/LtFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Our.Umbraco.GraphQL.Filters 5 | { 6 | public class LtFilter : IFilter 7 | { 8 | private readonly object _value; 9 | private readonly Func _valueResolver; 10 | 11 | public LtFilter(Func valueResolver, object value) 12 | { 13 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 14 | _value = value; 15 | } 16 | 17 | public bool IsSatisfiedBy(object input) 18 | { 19 | var value = _valueResolver(input); 20 | return Comparer.DefaultInvariant.Compare(value, _value) < 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/LteFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Our.Umbraco.GraphQL.Filters 5 | { 6 | public class LteFilter : IFilter 7 | { 8 | private readonly object _value; 9 | private readonly Func _valueResolver; 10 | 11 | public LteFilter(Func valueResolver, object value) 12 | { 13 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 14 | _value = value; 15 | } 16 | 17 | public bool IsSatisfiedBy(object input) 18 | { 19 | var value = _valueResolver(input); 20 | return Comparer.DefaultInvariant.Compare(value, _value) <= 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/NotFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Filters 4 | { 5 | public class NotFilter : IFilter 6 | { 7 | private readonly IFilter _filter; 8 | 9 | public NotFilter(IFilter filter) 10 | { 11 | _filter = filter ?? throw new ArgumentNullException(nameof(filter)); 12 | } 13 | 14 | public bool IsSatisfiedBy(object input) 15 | { 16 | return false == _filter.IsSatisfiedBy(input); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/OrFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Our.Umbraco.GraphQL.Filters 6 | { 7 | public class OrFilter : IFilter 8 | { 9 | private readonly IEnumerable _subFilters; 10 | 11 | public OrFilter(IEnumerable subFilters) 12 | { 13 | _subFilters = subFilters ?? throw new ArgumentNullException(nameof(subFilters)); 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | foreach (var filter in _subFilters) 19 | { 20 | if (filter.IsSatisfiedBy(input)) return true; 21 | } 22 | 23 | return false; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Filters/StartsWithFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Filters 4 | { 5 | public class StartsWithFilter : IFilter 6 | { 7 | private readonly string _value; 8 | private readonly Func _valueResolver; 9 | 10 | public StartsWithFilter(Func valueResolver, object value) 11 | { 12 | _valueResolver = valueResolver ?? throw new ArgumentNullException(nameof(valueResolver)); 13 | _value = Convert.ToString(value); 14 | } 15 | 16 | public bool IsSatisfiedBy(object input) 17 | { 18 | var value = _valueResolver(input); 19 | if (value == null || _value == null) 20 | { 21 | return false; 22 | } 23 | return Convert.ToString(value).StartsWith(_value, StringComparison.InvariantCultureIgnoreCase); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/IUserContext.cs: -------------------------------------------------------------------------------- 1 | using Umbraco.Cms.Web.Common.UmbracoContext; 2 | 3 | namespace Our.Umbraco.GraphQL 4 | { 5 | public interface IUserContext 6 | { 7 | UmbracoContext UmbracoContext { get; } 8 | } 9 | 10 | internal class UserContext 11 | { 12 | public UserContext(UmbracoContext umbracoContext) 13 | { 14 | UmbracoContext = umbracoContext ?? throw new System.ArgumentNullException(nameof(umbracoContext)); 15 | } 16 | 17 | public UmbracoContext UmbracoContext { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Json/Converters/InputsConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using GraphQL; 4 | using Newtonsoft.Json; 5 | 6 | namespace Our.Umbraco.GraphQL.Json.Converters 7 | { 8 | internal class InputsConverter : JsonConverter 9 | { 10 | public override bool CanConvert(Type objectType) => objectType == typeof(Inputs); 11 | 12 | public override bool CanWrite => false; 13 | 14 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 15 | JsonSerializer serializer) 16 | { 17 | var dict = serializer.Deserialize>(reader); 18 | return new Inputs(dict ?? new Dictionary()); 19 | } 20 | 21 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => 22 | throw new NotSupportedException(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Our.Umbraco.GraphQL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | GraphQL for Umbraco 4 | Umbraco Community 5 | umbraco-community 6 | A GraphQL server for Umbraco 7 | LICENSE 8 | https://github.com/umbraco-community/umbraco-graphql 9 | umbraco umbracocms graphql 10 | Copyright © 2023 Rasmus John Pedersen, Andrew McKaskill 11 | false 12 | 11.0.0-alpha001 13 | 14 | 15 | 16 | net7.0 17 | Our.Umbraco.GraphQL 18 | Our.Umbraco.GraphQL 19 | Web\UI\ 20 | $(DefaultItemExcludes);$(UIRoot)node_modules\**;$(UIRoot).sass-cache\** 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Our.Umbraco.GraphQL.Tests")] 4 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Reflection/TypeInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Threading.Tasks; 6 | using GraphQL.Types; 7 | 8 | namespace Our.Umbraco.GraphQL.Reflection 9 | { 10 | internal static class TypeInfoExtensions 11 | { 12 | public static bool IsNullable(this TypeInfo type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 13 | 14 | public static TypeInfo GetReturnType(this MemberInfo memberInfo) 15 | { 16 | switch (memberInfo) 17 | { 18 | case FieldInfo fieldInfo: 19 | return fieldInfo.FieldType.GetTypeInfo(); 20 | case MethodInfo methodInfo: 21 | return methodInfo.ReturnType.GetTypeInfo(); 22 | case PropertyInfo propertyInfo: 23 | return propertyInfo.GetMethod.ReturnType.GetTypeInfo(); 24 | default: 25 | throw new ArgumentOutOfRangeException(nameof(memberInfo)); 26 | } 27 | } 28 | 29 | public static TypeInfo Unwrap(this TypeInfo typeInfo) 30 | { 31 | if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Task<>)) 32 | typeInfo = typeInfo.GenericTypeArguments[0].GetTypeInfo(); 33 | 34 | var isNullable = typeInfo.IsNullable(); 35 | if (isNullable) 36 | return typeInfo.GenericTypeArguments[0].GetTypeInfo(); 37 | 38 | var enumerableArgument = GetEnumerableArgument(typeInfo); 39 | return enumerableArgument != null ? enumerableArgument.GetTypeInfo() : typeInfo; 40 | } 41 | 42 | public static TypeInfo Wrap(this TypeInfo graphType, TypeInfo typeInfo, bool isNonNull, bool isNonNullItem) 43 | => Wrap(graphType, typeInfo, isNonNull, isNonNullItem, true, out _); 44 | 45 | public static TypeInfo Wrap(this TypeInfo graphType, TypeInfo typeInfo, bool isNonNull, bool isNonNullItem, bool checkEnumerable, out bool isEnumerable) 46 | { 47 | isEnumerable = false; 48 | 49 | if (graphType == null) 50 | return null; 51 | 52 | var enumerableArgument = checkEnumerable ? GetEnumerableArgument(typeInfo) : null; 53 | isEnumerable = enumerableArgument != null; 54 | 55 | if (typeInfo.IsValueType && typeInfo.IsNullable() == false || enumerableArgument != null && 56 | (enumerableArgument.IsValueType && enumerableArgument.IsNullable() == false || isNonNullItem)) 57 | graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType).GetTypeInfo(); 58 | 59 | if (isEnumerable) 60 | graphType = typeof(ListGraphType<>).MakeGenericType(graphType).GetTypeInfo(); 61 | 62 | if (isNonNull && typeof(NonNullGraphType).IsAssignableFrom(graphType) == false) 63 | graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType).GetTypeInfo(); 64 | 65 | return graphType; 66 | } 67 | 68 | public static TypeInfo GetEnumerableArgument(this TypeInfo typeInfo) 69 | { 70 | if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Task<>)) 71 | typeInfo = typeInfo.GenericTypeArguments[0].GetTypeInfo(); 72 | 73 | if (typeInfo == typeof(string)) 74 | return null; 75 | 76 | if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>)) 77 | return typeInfo.GenericTypeArguments[0].GetTypeInfo(); 78 | 79 | var enumerableInterface = typeInfo.ImplementedInterfaces.FirstOrDefault(x => 80 | x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)); 81 | 82 | return enumerableInterface?.GenericTypeArguments[0].GetTypeInfo(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Resources/playground.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | GraphQL Playground 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 48 | 49 |
50 | Loading 51 | GraphQL Playground 52 |
53 |
54 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/ConnectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Our.Umbraco.GraphQL.Types.Relay; 6 | 7 | namespace Our.Umbraco.GraphQL.Types 8 | { 9 | public static class ConnectionExtensions 10 | { 11 | public static Connection ToConnection(this IEnumerable source, Func idSelector, 12 | int? first = null, string after = null, int? last = null, string before = null, long? totalCount = null) 13 | { 14 | if(first < 0) 15 | throw new ArgumentException($"{nameof(first)} cannot be less than 0.", nameof(first)); 16 | 17 | if(last < 0) 18 | throw new ArgumentException($"{nameof(last)} cannot be less than 0.", nameof(last)); 19 | 20 | var sourceList = source.ToList(); 21 | 22 | var edges = sourceList.Select(x => new Edge 23 | { 24 | Cursor = IdToCursor(idSelector(x)), 25 | Node = x 26 | }); 27 | 28 | if (after != null) edges = edges.SkipWhile(x => x.Cursor != after).Skip(1); 29 | if (before != null) edges = edges.TakeWhile(x => x.Cursor != before); 30 | if (first.HasValue) edges = edges.Take(first.Value); 31 | if (last.HasValue) edges = edges.Reverse().Take(last.Value).Reverse(); 32 | 33 | var edgeList = edges.ToList(); 34 | var endCursor = edgeList.LastOrDefault()?.Cursor; 35 | var startCursor = edgeList.FirstOrDefault()?.Cursor; 36 | 37 | var firstCursor = sourceList.Count > 0 ? IdToCursor(idSelector(sourceList.First())) : null; 38 | var lastCursor = sourceList.Count > 0 ? IdToCursor(idSelector(sourceList.Last())) : null; 39 | 40 | return new Connection 41 | { 42 | Edges = edgeList, 43 | TotalCount = totalCount, 44 | PageInfo = new PageInfo 45 | { 46 | EndCursor = endCursor, 47 | HasNextPage = endCursor != lastCursor, 48 | HasPreviousPage = startCursor != firstCursor, 49 | StartCursor = startCursor 50 | } 51 | }; 52 | } 53 | 54 | private static string IdToCursor(object id) => IdToCursor(new Id(id.ToString())); 55 | 56 | private static string IdToCursor(Id id) => Convert.ToBase64String(Encoding.UTF8.GetBytes($"connection:{id}")); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Id.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace Our.Umbraco.GraphQL.Types 5 | { 6 | public struct Id 7 | { 8 | public Id(string value) 9 | { 10 | if (value == null) throw new ArgumentNullException(nameof(value)); 11 | if (value == string.Empty) 12 | throw new ArgumentException($"{nameof(value)} cannot be empty.", nameof(value)); 13 | 14 | Value = value; 15 | } 16 | 17 | public string Value { get; } 18 | 19 | public T As() 20 | { 21 | var converter = TypeDescriptor.GetConverter(typeof(T)); 22 | if (converter.CanConvertFrom(typeof(string))) 23 | return (T)converter.ConvertFrom(Value); 24 | 25 | return (T)Convert.ChangeType(Value, typeof(T)); 26 | } 27 | 28 | public bool Equals(Id other) => Value == other.Value; 29 | 30 | public override bool Equals(object obj) => obj is Id other && Equals(other); 31 | 32 | public override int GetHashCode() => Value.GetHashCode(); 33 | 34 | public override string ToString() => Value; 35 | 36 | public static bool operator ==(Id left, Id right) => left.Equals(right); 37 | 38 | public static bool operator !=(Id left, Id right) => left.Equals(right) == false; 39 | 40 | public static implicit operator Id(string value) => new Id(value); 41 | 42 | public static implicit operator string(Id id) => id.Value; 43 | 44 | public static implicit operator Id?(string value) => string.IsNullOrEmpty(value) ? null : (Id?)new Id(value); 45 | 46 | public static implicit operator string(Id? id) => id?.Value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Mutation.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types 2 | { 3 | public class Mutation 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/OrderBy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL; 3 | 4 | namespace Our.Umbraco.GraphQL.Types 5 | { 6 | public class OrderBy 7 | { 8 | private readonly Func _resolver; 9 | 10 | internal OrderBy(string field, SortOrder direction, Func resolver) 11 | { 12 | _resolver = resolver ?? throw new ArgumentNullException(nameof(resolver)); 13 | Field = field ?? throw new ArgumentNullException(nameof(field)); 14 | Direction = direction; 15 | } 16 | 17 | public string Field { get; } 18 | public SortOrder Direction { get; } 19 | 20 | public object Resolve(TSource source) 21 | { 22 | return _resolver(new ResolveFieldContext 23 | { 24 | Source = source, 25 | }); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/OrderByExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Our.Umbraco.GraphQL.Types 5 | { 6 | public static class OrderByExtensions 7 | { 8 | public static IEnumerable OrderBy(this IEnumerable source, IEnumerable orderBy) 9 | { 10 | if (orderBy == null) return source; 11 | 12 | foreach (var order in orderBy) 13 | { 14 | if (source is IOrderedEnumerable ordered) 15 | { 16 | source = order.Direction == SortOrder.Ascending 17 | ? ordered.ThenBy(order.Resolve) 18 | : ordered.ThenByDescending(order.Resolve); 19 | } 20 | else 21 | { 22 | source = order.Direction == SortOrder.Ascending 23 | ? source.OrderBy(order.Resolve) 24 | : source.OrderByDescending(order.Resolve); 25 | } 26 | } 27 | 28 | return source; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/PublishedContent/PublishedContentAtRootQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Our.Umbraco.GraphQL.Attributes; 5 | using Our.Umbraco.GraphQL.Types.Relay; 6 | using Umbraco.Cms.Core.Models.PublishedContent; 7 | using Umbraco.Cms.Core.PublishedCache; 8 | 9 | namespace Our.Umbraco.GraphQL.Types.PublishedContent 10 | { 11 | public class PublishedContentAtRootQuery 12 | { 13 | private readonly IPublishedSnapshotAccessor _snapshotAccessor; 14 | 15 | public PublishedContentAtRootQuery(IPublishedSnapshotAccessor snapshotAccessor) 16 | { 17 | _snapshotAccessor = snapshotAccessor ?? throw new ArgumentNullException(nameof(snapshotAccessor)); 18 | } 19 | 20 | 21 | [NonNull, NonNullItem] 22 | public Connection All(string culture = null, int? first = null, string after = null, 23 | int? last = null, string before = null, IEnumerable orderBy = null) 24 | { 25 | if (!_snapshotAccessor.TryGetPublishedSnapshot(out var publishedSnapshot)) 26 | { 27 | throw new InvalidOperationException("Wasn't possible to a get a valid Snapshot"); 28 | } 29 | 30 | var rootContent = publishedSnapshot.Content.GetAtRoot(culture).OrderBy(orderBy).ToList(); 31 | return rootContent.ToConnection(x => x.Key, first, after, last, before, rootContent.Count); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/PublishedContent/PublishedContentByTypeQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types.PublishedContent 2 | { 3 | public class PublishedContentByTypeQuery 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/PublishedContent/PublishedContentQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Our.Umbraco.GraphQL.Attributes; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | using Umbraco.Cms.Core.PublishedCache; 5 | using Umbraco.Extensions; 6 | 7 | namespace Our.Umbraco.GraphQL.Types.PublishedContent 8 | { 9 | public class PublishedContentQuery 10 | { 11 | [NonNull] 12 | public PublishedContentAtRootQuery AtRoot([Inject] PublishedContentAtRootQuery query) => query; 13 | 14 | public IPublishedContent ById([Inject] IPublishedSnapshotAccessor snapshotAccessor, Id id, string culture = null) => 15 | GetInternal(snapshotAccessor, x => x.GetById(id.As()), culture); 16 | 17 | [NonNull] 18 | public PublishedContentByTypeQuery ByType([Inject] PublishedContentByTypeQuery query) => query; 19 | 20 | public IPublishedContent ByUrl([Inject] IPublishedSnapshotAccessor snapshotAccessor, string url, string culture = null) => 21 | GetInternal(snapshotAccessor, x => x.GetByRoute(url, culture: culture), culture); 22 | 23 | private static IPublishedContent GetInternal(IPublishedSnapshotAccessor snapshotAccessor, Func fetch, string culture) 24 | { 25 | if (!snapshotAccessor.TryGetPublishedSnapshot(out var publishedSnapshot)) 26 | { 27 | throw new InvalidOperationException("Wasn't possible to a get a valid Snapshot"); 28 | } 29 | 30 | var content = fetch(publishedSnapshot.Content); 31 | if (culture == null || content != null && content.IsInvariantOrHasCulture(culture)) 32 | return content; 33 | 34 | return null; 35 | } 36 | } 37 | 38 | public class ExtendUmbracoQueryWithPublishedContentQuery 39 | { 40 | [NonNull] 41 | [Description("Published content in Umbraco")] 42 | public PublishedContentQuery Content([Inject] PublishedContentQuery query) => query; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/PublishedContent/UmbracoQuery.cs: -------------------------------------------------------------------------------- 1 | using Our.Umbraco.GraphQL.Attributes; 2 | 3 | namespace Our.Umbraco.GraphQL.Types.PublishedContent 4 | { 5 | public class UmbracoQuery 6 | { 7 | } 8 | 9 | public class ExtendQueryWithUmbracoQuery 10 | { 11 | [NonNull] 12 | [Description("Query various types of data from the Umbraco CMS")] 13 | public UmbracoQuery Umbraco([Inject] UmbracoQuery query) => query; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Query.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types 2 | { 3 | public class Query 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Relay/Connection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Our.Umbraco.GraphQL.Types.Relay 5 | { 6 | public class Connection 7 | { 8 | public long? TotalCount { get; set; } 9 | public PageInfo PageInfo { get; set; } 10 | public List> Edges { get; set; } 11 | public List Items => Edges?.Select(x => x.Node).ToList(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Relay/Edge.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types.Relay 2 | { 3 | public class Edge 4 | { 5 | public string Cursor { get; set; } 6 | public T Node { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Relay/PageInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types.Relay 2 | { 3 | public class PageInfo 4 | { 5 | public string EndCursor { get; set; } 6 | public bool HasNextPage { get; set; } 7 | public bool HasPreviousPage { get; set; } 8 | public string StartCursor { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/Schema.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types 2 | { 3 | public class Schema 4 | { 5 | public TQuery Query { get; set; } 6 | } 7 | public class Schema 8 | { 9 | public TQuery Query { get; set; } 10 | public TMutation Mutation { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Types/SortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace Our.Umbraco.GraphQL.Types 2 | { 3 | public enum SortOrder 4 | { 5 | Ascending, 6 | Descending 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/Web/GraphQLServerOptions.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Server.Ui.Playground; 2 | using GraphQL.Validation.Complexity; 3 | 4 | namespace Our.Umbraco.GraphQL.Web 5 | { 6 | public class GraphQLServerOptions 7 | { 8 | public string Path { get; set; } = "/umbraco/graphql"; 9 | public bool EnableMetrics { get; set; } 10 | public bool EnablePlayground { get; set; } 11 | public bool EnableCors { get; set; } 12 | public string CorsPolicyName { get; set; } 13 | public string[] CorsAllowedOrigins { get; set; } 14 | public string[] CorsAllowedExposedHeaders { get; set; } 15 | public string[] CorsAllowedHeaders { get; set; } 16 | public string[] CorsAllowedMethods { get; set; } 17 | public ComplexityConfiguration Complexity { get; set; } = new ComplexityConfiguration(); 18 | public PlaygroundOptions Playground { get; set; } = new PlaygroundOptions { GraphQLEndPoint = "/umbraco/graphql", SubscriptionsEndPoint = "/umbraco/graphql" }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/content/App_Start/GraphQLComponent.cs.pp: -------------------------------------------------------------------------------- 1 | using System.Web.Hosting; 2 | using Our.Umbraco.GraphQL.Web; 3 | using Owin; 4 | using Umbraco.Cms.Core.Configuration; 5 | using Umbraco.Cms.Core.Composing; 6 | using Umbraco.Cms.Core; 7 | using Umbraco.Web; 8 | 9 | namespace $rootnamespace$ 10 | { 11 | [RuntimeLevel(MinLevel = RuntimeLevel.Run)] 12 | public class GraphQLComposer : ComponentComposer, IUserComposer 13 | { 14 | } 15 | 16 | public class GraphQLComponent : IComponent 17 | { 18 | private readonly IGlobalSettings _globalSettings; 19 | private readonly IFactory _factory; 20 | 21 | public GraphQLComponent(IGlobalSettings globalSettings, IFactory factory) 22 | { 23 | _globalSettings = globalSettings ?? throw new System.ArgumentNullException(nameof(globalSettings)); 24 | _factory = factory ?? throw new System.ArgumentNullException(nameof(factory)); 25 | } 26 | 27 | public void Initialize() 28 | { 29 | UmbracoDefaultOwinStartup.MiddlewareConfigured += UmbracoDefaultOwinStartup_MiddlewareConfigured; 30 | } 31 | 32 | private void UmbracoDefaultOwinStartup_MiddlewareConfigured(object sender, OwinMiddlewareConfiguredEventArgs e) => 33 | Configure(e.AppBuilder); 34 | 35 | private void Configure(IAppBuilder app) 36 | { 37 | var path = $"/{_globalSettings.GetUmbracoMvcArea()}/graphql"; 38 | 39 | app.UseUmbracoGraphQL(path, _factory, opts => 40 | { 41 | opts.Debug = HostingEnvironment.IsDevelopmentEnvironment; 42 | opts.EnableMetrics = true; 43 | opts.EnableMiniProfiler = false; 44 | opts.EnablePlayground = true; 45 | }); 46 | } 47 | 48 | public void Terminate() 49 | { 50 | UmbracoDefaultOwinStartup.MiddlewareConfigured -= UmbracoDefaultOwinStartup_MiddlewareConfigured; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Our.Umbraco.GraphQL/readme.txt: -------------------------------------------------------------------------------- 1 | _____ _ _____ _ __ _ _ _ 2 | | __ \ | | | _ | | / _| | | | | | | 3 | | | \/_ __ __ _ _ __ | |__ | | | | | | |_ ___ _ __ | | | |_ __ ___ | |__ _ __ __ _ ___ ___ 4 | | | __| '__/ _` | '_ \| '_ \| | | | | | _/ _ \| '__| | | | | '_ ` _ \| '_ \| '__/ _` |/ __/ _ \ 5 | | |_\ \ | | (_| | |_) | | | \ \/' / |____ | || (_) | | | |_| | | | | | | |_) | | | (_| | (_| (_) | 6 | \____/_| \__,_| .__/|_| |_|\_/\_\_____/ |_| \___/|_| \___/|_| |_| |_|_.__/|_| \__,_|\___\___/ 7 | | | 8 | |_| 9 | 10 | This package currently is intended for development-level testing right now. 11 | 12 | All doctypes and media properties are accessible via the GraphQL endpoint by anyone, as permissions and other security features aren't yet present. 13 | Don't use this on any data you need to keep protected 14 | 15 | For more info visit https://github.com/rasmusjp/umbraco-graphql/ -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/PublishedContent/Types/LinkTypeGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 3 | using Umbraco.Cms.Core.Models; 4 | using Xunit; 5 | 6 | namespace Our.Umbraco.GraphQL.Tests.Adapters.PublishedContent.Types 7 | { 8 | public class ContentVariationTests 9 | { 10 | [Fact] 11 | public void Ctor_SetsName() 12 | { 13 | var sut = new ContentVariationGraphType(); 14 | 15 | sut.Name.Should().Be("ContentVariation"); 16 | } 17 | [Fact] 18 | public void Ctor_SetsDescription() 19 | { 20 | var sut = new ContentVariationGraphType(); 21 | 22 | sut.Description.Should().Be("Indicates how values can vary."); 23 | } 24 | 25 | [Theory] 26 | [InlineData("NOTHING", ContentVariation.Nothing)] 27 | [InlineData("CULTURE", ContentVariation.Culture)] 28 | [InlineData("SEGMENT", ContentVariation.Segment)] 29 | [InlineData("CULTURE_AND_SEGMENT", ContentVariation.CultureAndSegment)] 30 | public void Ctor_AddsFields(string field, ContentVariation value) 31 | { 32 | var sut = new ContentVariationGraphType(); 33 | 34 | sut.Values.Should().Contain(x => x.Name == field) 35 | .Which.Value.Should().Be(value); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/PublishedContent/Types/PublishedContentCompositionGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using GraphQL.Types; 4 | using GraphQL.Types.Relay; 5 | using Microsoft.AspNetCore.Http; 6 | using NSubstitute; 7 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 8 | using Our.Umbraco.GraphQL.Adapters.Types.Resolution; 9 | using Umbraco.Cms.Core.Models; 10 | using Umbraco.Cms.Core.Models.PublishedContent; 11 | using Umbraco.Cms.Core.Routing; 12 | using Umbraco.Cms.Core.Web; 13 | using Xunit; 14 | using IdGraphType = Our.Umbraco.GraphQL.Adapters.Types.IdGraphType; 15 | 16 | namespace Our.Umbraco.GraphQL.Tests.Adapters.PublishedContent.Types 17 | { 18 | public class PublishedContentCompositionGraphTypeTests 19 | { 20 | private PublishedContentCompositionGraphType CreateSUT(IContentTypeComposition contentType = null) 21 | { 22 | return new PublishedContentCompositionGraphType(contentType ?? Substitute.For(), 23 | Substitute.For(), new TypeRegistry(), Substitute.For(), Substitute.For(), Substitute.For()); 24 | } 25 | 26 | [Theory] 27 | [InlineData(typeof(IContentType), "person", "PersonPublishedContent")] 28 | [InlineData(typeof(IMediaType), "image", "ImagePublishedMedia")] 29 | [InlineData(typeof(IContentType), "feature", "FeaturePublishedElement", true)] 30 | public void Ctor_SetsName(Type type, string alias, string expectedName, bool isElement = false) 31 | { 32 | var contentType = (IContentTypeComposition) Substitute.For(new[] {type}, null); 33 | contentType.Alias.Returns(alias); 34 | contentType.IsElement.Returns(isElement); 35 | 36 | var graphType = CreateSUT(contentType); 37 | 38 | graphType.Name.Should().Be(expectedName); 39 | } 40 | 41 | [Theory] 42 | [InlineData("_ancestors", typeof(ConnectionType))] 43 | [InlineData("_children", typeof(ConnectionType))] 44 | [InlineData("_createDate", typeof(NonNullGraphType))] 45 | [InlineData("_creatorName", typeof(NonNullGraphType))] 46 | [InlineData("_contentType", typeof(NonNullGraphType))] 47 | [InlineData("_id", typeof(NonNullGraphType))] 48 | [InlineData("_level", typeof(NonNullGraphType))] 49 | [InlineData("_name", typeof(StringGraphType))] 50 | [InlineData("_parent", typeof(PublishedContentInterfaceGraphType))] 51 | [InlineData("_sortOrder", typeof(NonNullGraphType))] 52 | [InlineData("_url", typeof(StringGraphType))] 53 | [InlineData("_updateDate", typeof(DateTimeGraphType))] 54 | [InlineData("_writerName", typeof(NonNullGraphType))] 55 | public void Fields_Type_ShouldBeOfExpectedType(string field, Type type) 56 | { 57 | var graphType = CreateSUT(); 58 | 59 | graphType.Fields.Should().Contain(x => x.Name == field) 60 | .Which.Type.Should().Be(type); 61 | } 62 | 63 | [Theory] 64 | [InlineData("_name", "culture", typeof(StringGraphType))] 65 | [InlineData("_url", "culture", typeof(StringGraphType))] 66 | [InlineData("_url", "mode", typeof(UrlModeGraphType))] 67 | [InlineData("_updateDate", "culture", typeof(StringGraphType))] 68 | public void Fields_Arguments_ShouldBeOfExpectedType(string field, string argument, Type type) 69 | { 70 | var graphType = CreateSUT(); 71 | 72 | graphType.Fields.Should().Contain(x => x.Name == field) 73 | .Which.Arguments.Should().Contain(x => x.Name == argument) 74 | .Which.Type.Should().Be(type); 75 | } 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/PublishedContent/Types/PublishedContentInterfaceGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using GraphQL.Types; 4 | using GraphQL.Types.Relay; 5 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 6 | using Xunit; 7 | using IdGraphType = Our.Umbraco.GraphQL.Adapters.Types.IdGraphType; 8 | 9 | namespace Our.Umbraco.GraphQL.Tests.Adapters.PublishedContent.Types 10 | { 11 | public class PublishedContentInterfaceGraphTypeTests 12 | { 13 | [Fact] 14 | public void Ctor_SetsName() 15 | { 16 | var graphType = new PublishedContentInterfaceGraphType(); 17 | 18 | graphType.Name.Should().Be("PublishedContent"); 19 | } 20 | 21 | [Theory] 22 | [InlineData("_ancestors", typeof(ConnectionType))] 23 | [InlineData("_children", typeof(ConnectionType))] 24 | [InlineData("_createDate", typeof(NonNullGraphType))] 25 | [InlineData("_creatorName", typeof(NonNullGraphType))] 26 | [InlineData("_contentType", typeof(NonNullGraphType))] 27 | [InlineData("_id", typeof(NonNullGraphType))] 28 | [InlineData("_level", typeof(NonNullGraphType))] 29 | [InlineData("_name", typeof(StringGraphType))] 30 | [InlineData("_parent", typeof(PublishedContentInterfaceGraphType))] 31 | [InlineData("_sortOrder", typeof(NonNullGraphType))] 32 | [InlineData("_url", typeof(StringGraphType))] 33 | [InlineData("_updateDate", typeof(DateTimeGraphType))] 34 | [InlineData("_writerName", typeof(NonNullGraphType))] 35 | public void Fields_Type_ShouldBeOfExpectedType(string field, Type type) 36 | { 37 | var graphType = new PublishedContentInterfaceGraphType(); 38 | 39 | graphType.Fields.Should().Contain(x => x.Name == field) 40 | .Which.Type.Should().Be(type); 41 | } 42 | 43 | [Theory] 44 | [InlineData("_name", "culture", typeof(StringGraphType))] 45 | [InlineData("_url", "culture", typeof(StringGraphType))] 46 | [InlineData("_url", "mode", typeof(UrlModeGraphType))] 47 | [InlineData("_updateDate", "culture", typeof(StringGraphType))] 48 | public void Fields_Arguments_ShouldBeOfExpectedType(string field, string argument, Type type) 49 | { 50 | var graphType = new PublishedContentInterfaceGraphType(); 51 | 52 | graphType.Fields.Should().Contain(x => x.Name == field) 53 | .Which.Arguments.Should().Contain(x => x.Name == argument) 54 | .Which.Type.Should().Be(type); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/PublishedContent/Types/PublishedElementInterfaceGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using GraphQL.Types; 3 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 4 | using Xunit; 5 | using IdGraphType = Our.Umbraco.GraphQL.Adapters.Types.IdGraphType; 6 | 7 | namespace Our.Umbraco.GraphQL.Tests.Adapters.PublishedContent.Types 8 | { 9 | public class PublishedElementInterfaceGraphTypeTests 10 | { 11 | [Fact] 12 | public void Ctor_SetsName() 13 | { 14 | var graphType = new PublishedElementInterfaceGraphType(); 15 | 16 | graphType.Name.Should().Be("PublishedElement"); 17 | } 18 | 19 | [Fact] 20 | public void Ctor_AddsFields() 21 | { 22 | var graphType = new PublishedElementInterfaceGraphType(); 23 | 24 | graphType.Fields.Should().Contain(x => x.Name == "_contentType") 25 | .And.Contain(x => x.Name == "_id"); 26 | } 27 | 28 | [Fact] 29 | public void ContentTypeField_Type_ShouldBePublishedContentGraphType() 30 | { 31 | var graphType = new PublishedElementInterfaceGraphType(); 32 | 33 | graphType.Fields.Should().Contain(x => x.Name == "_contentType") 34 | .Which.Type.Should().Be>(); 35 | } 36 | 37 | [Fact] 38 | public void IdField_Type_ShouldBeIdGraphType() 39 | { 40 | var graphType = new PublishedElementInterfaceGraphType(); 41 | 42 | graphType.Fields.Should().Contain(x => x.Name == "_id") 43 | .Which.Type.Should().Be>(); 44 | } 45 | 46 | // [Fact] 47 | // public void ContentTypeFieldResolver_WhenCalled_ReturnsAlias() 48 | // { 49 | // var graphType = new PublishedElementInterfaceGraphType(); 50 | // 51 | // var contentType = Substitute.For(); 52 | // var element = Substitute.For(); 53 | // element.ContentType.Returns(contentType); 54 | // 55 | // graphType.Fields.Should().Contain(x => x.Name == "_contentType") 56 | // .Which.Resolver.Resolve(new ResolveFieldContext{ Source = element }) 57 | // .Should().Be(contentType); 58 | // } 59 | // 60 | // [Fact] 61 | // public void CompositionAliasesFieldResolver_WhenCalled_ReturnsAlias() 62 | // { 63 | // var graphType = new PublishedElementInterfaceGraphType(); 64 | // 65 | // var element = Substitute.For(); 66 | // element.Key.Returns(new Guid("F14EA3D9-E40A-4A14-B860-492125D6877B")); 67 | // 68 | // graphType.Fields.Should().Contain(x => x.Name == "_id") 69 | // .Which.Resolver.Resolve(new ResolveFieldContext{ Source = element }) 70 | // .Should().Be(element.Key); 71 | // } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/PublishedContent/Types/PublishedItemTypeGraphType.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | using Xunit; 5 | 6 | namespace Our.Umbraco.GraphQL.Tests.Adapters.PublishedContent.Types 7 | { 8 | public class PublishedItemTypeGraphTypeTests 9 | { 10 | [Fact] 11 | public void Ctor_SetsName() 12 | { 13 | var sut = new PublishedItemTypeGraphType(); 14 | 15 | sut.Name.Should().Be("PublishedItemType"); 16 | } 17 | [Fact] 18 | public void Ctor_SetsDescription() 19 | { 20 | var sut = new PublishedItemTypeGraphType(); 21 | 22 | sut.Description.Should().Be("The type of published element."); 23 | } 24 | 25 | [Theory] 26 | [InlineData("CONTENT", PublishedItemType.Content)] 27 | [InlineData("ELEMENT", PublishedItemType.Element)] 28 | [InlineData("MEDIA", PublishedItemType.Media)] 29 | [InlineData("MEMBER", PublishedItemType.Member)] 30 | [InlineData("UNKNOWN", PublishedItemType.Unknown)] 31 | public void Ctor_AddsFields(string field, PublishedItemType value) 32 | { 33 | var sut = new PublishedItemTypeGraphType(); 34 | 35 | sut.Values.Should().Contain(x => x.Name == field) 36 | .Which.Value.Should().Be(value); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/PublishedContent/Types/UrlModeGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.PublishedContent.Types; 3 | using Umbraco.Cms.Core.Models.PublishedContent; 4 | using Xunit; 5 | 6 | namespace Our.Umbraco.GraphQL.Tests.Adapters.PublishedContent.Types 7 | { 8 | public class UrlModeGraphTypeTests 9 | { 10 | [Fact] 11 | public void Ctor_SetsName() 12 | { 13 | var sut = new UrlModeGraphType(); 14 | 15 | sut.Name.Should().Be("UrlMode"); 16 | } 17 | [Fact] 18 | public void Ctor_SetsDescription() 19 | { 20 | var sut = new UrlModeGraphType(); 21 | 22 | sut.Description.Should() 23 | .Be("Specifies the type of urls that the url provider should produce, Auto is the default."); 24 | } 25 | 26 | [Theory] 27 | [InlineData("ABSOLUTE", UrlMode.Absolute)] 28 | [InlineData("AUTO", UrlMode.Auto)] 29 | [InlineData("DEFAULT", UrlMode.Default)] 30 | [InlineData("RELATIVE", UrlMode.Relative)] 31 | public void Ctor_AddsFields(string field, UrlMode value) 32 | { 33 | var sut = new UrlModeGraphType(); 34 | 35 | sut.Values.Should().Contain(x => x.Name == field) 36 | .Which.Value.Should().Be(value); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/GuidGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.Types; 3 | using Xunit; 4 | 5 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 6 | { 7 | public class GuidGraphTypeTests 8 | { 9 | [Fact] 10 | public void Ctor_SetsName() 11 | { 12 | var graphType = new GuidGraphType(); 13 | graphType.Name.Should().Be("Guid"); 14 | } 15 | 16 | [Fact] 17 | public void Ctor_SetsDescription() 18 | { 19 | var graphType = new GuidGraphType(); 20 | graphType.Description.Should().Be("Globally Unique Identifier."); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/HtmlGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using FluentAssertions; 3 | using GraphQL.Language.AST; 4 | using Microsoft.AspNetCore.Html; 5 | using Our.Umbraco.GraphQL.Adapters.Types; 6 | using Xunit; 7 | 8 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 9 | { 10 | public class HtmlGraphTypeTests 11 | { 12 | [Fact] 13 | public void Ctor_SetsName() 14 | { 15 | var htmlGraphType = new HtmlGraphType(); 16 | 17 | htmlGraphType.Name.Should().Be("HTML"); 18 | } 19 | 20 | [Fact] 21 | public void Ctor_SetsDescription() 22 | { 23 | var htmlGraphType = new HtmlGraphType(); 24 | 25 | htmlGraphType.Description.Should().Be("A string containing HTML code."); 26 | } 27 | 28 | [Fact] 29 | public void Serialize_WithHtmlString_ReturnsValueToString() 30 | { 31 | var htmlGraphType = new HtmlGraphType(); 32 | var value = "
Some HTML
"; 33 | var htmlString = new HtmlString(value); 34 | 35 | var serialized = htmlGraphType.Serialize(htmlString); 36 | 37 | serialized.Should().BeOfType().Which.Should().Be(value); 38 | } 39 | 40 | [Fact] 41 | public void Serialize_WithNull_ReturnsNull() 42 | { 43 | var htmlGraphType = new HtmlGraphType(); 44 | 45 | var serialized = htmlGraphType.Serialize(null); 46 | 47 | serialized.Should().BeNull(); 48 | } 49 | 50 | [Fact] 51 | public void ParseValue_WithValue_ReturnsHtmlString() 52 | { 53 | var htmlGraphType = new HtmlGraphType(); 54 | var value = "
Some HTML
"; 55 | 56 | var parsed = htmlGraphType.ParseValue(value); 57 | 58 | parsed.Should().BeAssignableTo().Which.ToString().Should().Be(value); 59 | } 60 | 61 | [Fact] 62 | public void ParseValue_WithNull_ReturnsNull() 63 | { 64 | var htmlGraphType = new HtmlGraphType(); 65 | 66 | var parsed = htmlGraphType.ParseValue(null); 67 | 68 | parsed.Should().BeNull(); 69 | } 70 | 71 | [Fact] 72 | public void ParseLiteral_WithStringValue_ReturnsHtmlString() 73 | { 74 | var htmlGraphType = new HtmlGraphType(); 75 | var value = "
Some HTML
"; 76 | 77 | var parsed = htmlGraphType.ParseLiteral(new StringValue(value)); 78 | 79 | parsed.Should().BeAssignableTo().Which.ToString().Should().Be(value); 80 | } 81 | 82 | [Fact] 83 | public void ParseLiteral_WithNull_ReturnsNull() 84 | { 85 | var htmlGraphType = new HtmlGraphType(); 86 | 87 | var parsed = htmlGraphType.ParseLiteral(null); 88 | 89 | parsed.Should().BeNull(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/IdGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using GraphQL.Language.AST; 4 | using Our.Umbraco.GraphQL.Adapters.Types; 5 | using Our.Umbraco.GraphQL.Types; 6 | using Xunit; 7 | 8 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 9 | { 10 | public class IdGraphTypeTests 11 | { 12 | [Fact] 13 | public void Serialize_WithId_ReturnsValueToString() 14 | { 15 | var idGraphType = new IdGraphType(); 16 | var value = "05087385-5095-448D-AA90-4B9AD3C1CA3F"; 17 | var id = new Id(value); 18 | 19 | var serialized = idGraphType.Serialize(id); 20 | 21 | serialized.Should().BeOfType().Which.Should().Be(value); 22 | } 23 | 24 | [Fact] 25 | public void Serialize_WithNull_ReturnsNull() 26 | { 27 | var idGraphType = new IdGraphType(); 28 | 29 | var serialized = idGraphType.Serialize(null); 30 | 31 | serialized.Should().BeNull(); 32 | } 33 | 34 | [Fact] 35 | public void ParseValue_WithValue_ReturnsId() 36 | { 37 | var idGraphType = new IdGraphType(); 38 | var value = "BF389740-3EEE-4F2E-BBB1-1C4758B5CE11"; 39 | 40 | var parsed = idGraphType.ParseValue(value); 41 | 42 | parsed.Should().BeOfType().Which.Value.Should().Be(value); 43 | } 44 | 45 | [Fact] 46 | public void ParseValue_WithNull_ReturnsNull() 47 | { 48 | var idGraphType = new IdGraphType(); 49 | 50 | var parsed = idGraphType.ParseValue(null); 51 | 52 | parsed.Should().BeNull(); 53 | } 54 | 55 | [Fact] 56 | public void ParseLiteral_WithStringValue_ReturnsId() 57 | { 58 | var idGraphType = new IdGraphType(); 59 | var value = "BDCE0B55-8D23-47A4-9C66-C94F7D6D8C2A"; 60 | 61 | var parsed = idGraphType.ParseLiteral(new StringValue(value)); 62 | 63 | parsed.Should().BeOfType().Which.Value.Should().Be(value); 64 | } 65 | 66 | [Fact] 67 | public void ParseLiteral_WithIntValue_ReturnsId() 68 | { 69 | var idGraphType = new IdGraphType(); 70 | var value = 43; 71 | 72 | var parsed = idGraphType.ParseLiteral(new IntValue(value)); 73 | 74 | parsed.Should().BeOfType().Which.Value.Should().Be(value.ToString()); 75 | } 76 | 77 | [Fact] 78 | public void ParseLiteral_WithNull_ReturnsNull() 79 | { 80 | var idGraphType = new IdGraphType(); 81 | 82 | var parsed = idGraphType.ParseLiteral(null); 83 | 84 | parsed.Should().BeNull(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/JsonGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.Types; 3 | using Xunit; 4 | 5 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 6 | { 7 | public class JsonGraphTypeTests 8 | { 9 | [Fact] 10 | public void Ctor_SetsName() 11 | { 12 | var jsonGraphType = new JsonGraphType(); 13 | 14 | jsonGraphType.Name.Should().Be("JSON"); 15 | } 16 | 17 | [Fact] 18 | public void Ctor_SetsDescription() 19 | { 20 | var jsonGraphType = new JsonGraphType(); 21 | 22 | jsonGraphType.Description.Should().Be("The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)."); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/LinkGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using GraphQL.Types; 4 | using Our.Umbraco.GraphQL.Adapters.Types; 5 | using Xunit; 6 | 7 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 8 | { 9 | public class LinkGraphTypeTests 10 | { 11 | [Fact] 12 | public void Ctor_SetsName() 13 | { 14 | var sut = new LinkGraphType(); 15 | 16 | sut.Name.Should().Be("Link"); 17 | } 18 | 19 | [Theory] 20 | [InlineData("name", typeof(NonNullGraphType))] 21 | [InlineData("target", typeof(StringGraphType))] 22 | [InlineData("type", typeof(NonNullGraphType))] 23 | [InlineData("url", typeof(NonNullGraphType))] 24 | [InlineData("udi", typeof(UdiGraphType))] 25 | public void Ctor_AddsFields(string field, Type type) 26 | { 27 | var sut = new LinkGraphType(); 28 | 29 | sut.Fields.Should().Contain(x => x.Name == field) 30 | .Which.Type.Should().BeAssignableTo(type); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/LinkTypeGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.Types; 3 | using Umbraco.Cms.Core.Models; 4 | using Xunit; 5 | 6 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 7 | { 8 | public class LinkTypeGraphTypeTests 9 | { 10 | [Fact] 11 | public void Ctor_SetsName() 12 | { 13 | var sut = new LinkTypeGraphType(); 14 | 15 | sut.Name.Should().Be("LinkType"); 16 | } 17 | 18 | [Theory] 19 | [InlineData("CONTENT", LinkType.Content)] 20 | [InlineData("EXTERNAL", LinkType.External)] 21 | [InlineData("MEDIA", LinkType.Media)] 22 | public void Ctor_AddsFields(string field, LinkType value) 23 | { 24 | var sut = new LinkTypeGraphType(); 25 | 26 | sut.Values.Should().Contain(x => x.Name == field) 27 | .Which.Value.Should().Be(value); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/Relay/ConnectionGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using GraphQL.Types; 3 | using Lucene.Net.Documents; 4 | using Our.Umbraco.GraphQL.Adapters.Types.Relay; 5 | using Xunit; 6 | 7 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types.Relay 8 | { 9 | public class ConnectionGraphTypeTests 10 | { 11 | 12 | [Fact] 13 | public void Ctor_WithInstance_SetsName() 14 | { 15 | var graphType = new ConnectionGraphType(new ItemGraphType()); 16 | 17 | graphType.Name.Should().Be("ItemConnection"); 18 | } 19 | 20 | [Fact] 21 | public void Ctor_GenericClassWithType_SetsName() 22 | { 23 | var graphType = new ConnectionGraphType(); 24 | 25 | graphType.Name.Should().Be("ItemConnection"); 26 | } 27 | 28 | [Fact] 29 | public void Ctor_WithInstance_SetsResolvedType() 30 | { 31 | var itemGraphType = new ItemGraphType(); 32 | var graphType = new ConnectionGraphType(itemGraphType); 33 | 34 | graphType.ResolvedType.Should().Be(itemGraphType); 35 | } 36 | 37 | [Fact] 38 | public void Ctor_WithInstance_AddsFields() 39 | { 40 | var graphType = new ConnectionGraphType(new ItemGraphType()); 41 | 42 | graphType.Fields.Should().Contain(field => field.Name == "edges") 43 | .And.Contain(field => field.Name == "items") 44 | .And.Contain(field => field.Name == "pageInfo") 45 | .And.Contain(field => field.Name == "totalCount"); 46 | } 47 | 48 | [Fact] 49 | public void Ctor_WithInstance_SetsEdgesResolvedType() 50 | { 51 | var graphType = new ConnectionGraphType(new ItemGraphType()); 52 | 53 | graphType.Fields.Should().Contain(x => x.Name == "edges") 54 | .Which.ResolvedType.Should().BeAssignableTo() 55 | .Which.ResolvedType.Should().BeAssignableTo() 56 | .Which.ResolvedType.Should().BeAssignableTo(); 57 | } 58 | 59 | [Fact] 60 | public void Ctor_WithInstance_SetsItemsResolvedType() 61 | { 62 | var graphType = new ConnectionGraphType(new ItemGraphType()); 63 | 64 | graphType.Fields.Should().Contain(x => x.Name == "items") 65 | .Which.ResolvedType.Should().BeAssignableTo() 66 | .Which.ResolvedType.Should().BeAssignableTo(); 67 | } 68 | 69 | private class ItemGraphType : ObjectGraphType 70 | { 71 | public ItemGraphType() 72 | { 73 | Name = "Item"; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/Relay/EdgeGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using GraphQL.Types; 3 | using Our.Umbraco.GraphQL.Adapters.Types.Relay; 4 | using Xunit; 5 | 6 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types.Relay 7 | { 8 | public class EdgeGraphTypeTests 9 | { 10 | [Fact] 11 | public void Ctor_WithInstance_SetsName() 12 | { 13 | var graphType = new EdgeGraphType(new ItemGraphType()); 14 | 15 | graphType.Name.Should().Be("ItemEdge"); 16 | } 17 | 18 | [Fact] 19 | public void Ctor_GenericClassWithType_SetsName() 20 | { 21 | var graphType = new EdgeGraphType(); 22 | 23 | graphType.Name.Should().Be("ItemEdge"); 24 | } 25 | 26 | [Fact] 27 | public void Ctor_WithInstance_SetsResolvedType() 28 | { 29 | var itemGraphType = new ItemGraphType(); 30 | var graphType = new EdgeGraphType(itemGraphType); 31 | 32 | graphType.ResolvedType.Should().Be(itemGraphType); 33 | } 34 | 35 | [Fact] 36 | public void Ctor_WithInstance_AddsFields() 37 | { 38 | var graphType = new EdgeGraphType(new ItemGraphType()); 39 | 40 | graphType.Fields.Should().SatisfyRespectively( 41 | field => field.Name.Should().Be("cursor"), 42 | field => field.Name.Should().Be("node") 43 | ); 44 | } 45 | 46 | [Fact] 47 | public void Ctor_WithInstance_SetsNodeResolvedType() 48 | { 49 | var graphType = new EdgeGraphType(new ItemGraphType()); 50 | 51 | graphType.Fields.Should().Contain(x => x.Name == "node") 52 | .Which.ResolvedType.Should().BeAssignableTo(); 53 | } 54 | 55 | private class ItemGraphType : ObjectGraphType 56 | { 57 | public ItemGraphType() 58 | { 59 | Name = "Item"; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/Relay/PageInfoGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Adapters.Types.Relay; 3 | using Xunit; 4 | 5 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types.Relay 6 | { 7 | public class PageInfoGraphTypeTests 8 | { 9 | [Fact] 10 | public void Ctor_SetsName() 11 | { 12 | var graphType = new PageInfoGraphType(); 13 | 14 | graphType.Name.Should().Be("PageInfo"); 15 | } 16 | 17 | [Fact] 18 | public void Ctor_AddsFields() 19 | { 20 | var graphType = new PageInfoGraphType(); 21 | 22 | graphType.Fields.Should().Contain(field => field.Name == "endCursor") 23 | .And.Contain(field => field.Name == "hasNextPage") 24 | .And.Contain(field => field.Name == "hasPreviousPage") 25 | .And.Contain(field => field.Name == "startCursor"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Types/UdiGraphTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using FluentAssertions; 4 | using GraphQL.Language.AST; 5 | using Our.Umbraco.GraphQL.Adapters.Types; 6 | using Umbraco.Cms.Core; 7 | using Xunit; 8 | 9 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Types 10 | { 11 | public class UdiGraphTypeTests 12 | { 13 | public UdiGraphTypeTests() 14 | { 15 | // we need to set _scanned to true to avoid Udi to try scanning for types 16 | // which uses the static `Current` service locator. 17 | typeof(Udi).GetField("_scanned", BindingFlags.NonPublic | BindingFlags.Static) 18 | .SetValue(null, true); 19 | } 20 | 21 | [Fact] 22 | public void Ctor_SetsName() 23 | { 24 | var udiGraphType = new UdiGraphType(); 25 | 26 | udiGraphType.Name.Should().Be("UDI"); 27 | } 28 | 29 | [Fact] 30 | public void Ctor_SetsDescription() 31 | { 32 | var udiGraphType = new UdiGraphType(); 33 | 34 | udiGraphType.Description.Should().Be("Represents an entity identifier."); 35 | } 36 | 37 | [Fact] 38 | public void Serialize_WithUdi_ReturnsValueToString() 39 | { 40 | var udiGraphType = new UdiGraphType(); 41 | var value = new Guid("{A2A6F423-7C73-4B82-A86B-D8078F81E258}"); 42 | var udi = new GuidUdi("document", value); 43 | 44 | var serialized = udiGraphType.Serialize(udi); 45 | 46 | serialized.Should().BeOfType().Which.Should().Be(udi.ToString()); 47 | } 48 | 49 | [Fact] 50 | public void Serialize_WithNull_ReturnsNull() 51 | { 52 | var udiGraphType = new UdiGraphType(); 53 | 54 | var serialized = udiGraphType.Serialize(null); 55 | 56 | serialized.Should().BeNull(); 57 | } 58 | 59 | [Fact] 60 | public void ParseValue_WithUdiValue_ReturnsUdi() 61 | { 62 | var udiGraphType = new UdiGraphType(); 63 | var value = "umb://document/c0126770c4dd4c80b48464c5f38658c1"; 64 | 65 | var parsed = udiGraphType.ParseValue(value); 66 | 67 | parsed.Should().BeAssignableTo().Which.ToString().Should().Be(value); 68 | } 69 | 70 | [Fact] 71 | public void ParseValue_WithNonUdiValue_ReturnsNull() 72 | { 73 | var udiGraphType = new UdiGraphType(); 74 | var value = "not an udi"; 75 | 76 | var parsed = udiGraphType.ParseValue(value); 77 | 78 | parsed.Should().BeNull(); 79 | } 80 | 81 | [Fact] 82 | public void ParseValue_WithNull_ReturnsNull() 83 | { 84 | var udiGraphType = new UdiGraphType(); 85 | 86 | var parsed = udiGraphType.ParseValue(null); 87 | 88 | parsed.Should().BeNull(); 89 | } 90 | 91 | [Fact] 92 | public void ParseLiteral_WithUdiValue_ReturnsUdi() 93 | { 94 | var udiGraphType = new UdiGraphType(); 95 | var value = "umb://document/c0126770c4dd4c80b48464c5f38658c1"; 96 | 97 | var parsed = udiGraphType.ParseLiteral(new StringValue(value)); 98 | 99 | parsed.Should().BeAssignableTo().Which.ToString().Should().Be(value); 100 | } 101 | 102 | [Fact] 103 | public void ParseLiteral_WithNonUdiValue_ReturnsNull() 104 | { 105 | var udiGraphType = new UdiGraphType(); 106 | var value = 4; 107 | 108 | var parsed = udiGraphType.ParseLiteral(new IntValue(value)); 109 | 110 | parsed.Should().BeNull(); 111 | } 112 | 113 | [Fact] 114 | public void ParseLiteral_WithNull_ReturnsNull() 115 | { 116 | var udiGraphType = new UdiGraphType(); 117 | 118 | var parsed = udiGraphType.ParseLiteral(null); 119 | 120 | parsed.Should().BeNull(); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Adapters/Visitors/CompositeGraphVisitorTests.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using NSubstitute; 3 | using Our.Umbraco.GraphQL.Adapters.Visitors; 4 | using Xunit; 5 | 6 | namespace Our.Umbraco.GraphQL.Tests.Adapters.Visitors 7 | { 8 | public class CompositeGraphVisitorTests 9 | { 10 | private CompositeGraphVisitor CreateSUT(params GraphVisitor[] visitors) => new CompositeGraphVisitor(visitors); 11 | 12 | [Fact] 13 | public void Visit_WithInputObjectGraphType_CallsAllVisitors() 14 | { 15 | var visitor1 = Substitute.For(); 16 | var visitor2 = Substitute.For(); 17 | var visitor = CreateSUT(visitor1, visitor2); 18 | var graphType = new InputObjectGraphType(); 19 | 20 | visitor.Visit(graphType); 21 | 22 | visitor1.Received(1).Visit(Arg.Is(graphType)); 23 | visitor2.Received(1).Visit(Arg.Is(graphType)); 24 | } 25 | 26 | [Fact] 27 | public void Visit_WithInterfaceGraphType_CallsAllVisitors() 28 | { 29 | var visitor1 = Substitute.For(); 30 | var visitor2 = Substitute.For(); 31 | var visitor = CreateSUT(visitor1, visitor2); 32 | var graphType = new InterfaceGraphType(); 33 | 34 | visitor.Visit(graphType); 35 | 36 | visitor1.Received(1).Visit(Arg.Is(graphType)); 37 | visitor2.Received(1).Visit(Arg.Is(graphType)); 38 | } 39 | 40 | [Fact] 41 | public void Visit_WithObjectGraphType_CallsAllVisitors() 42 | { 43 | var visitor1 = Substitute.For(); 44 | var visitor2 = Substitute.For(); 45 | var visitor = CreateSUT(visitor1, visitor2); 46 | var graphType = new ObjectGraphType(); 47 | 48 | visitor.Visit(graphType); 49 | 50 | visitor1.Received(1).Visit(Arg.Is(graphType)); 51 | visitor2.Received(1).Visit(Arg.Is(graphType)); 52 | } 53 | 54 | [Fact] 55 | public void Visit_WithSchema_CallsAllVisitors() 56 | { 57 | var visitor1 = Substitute.For(); 58 | var visitor2 = Substitute.For(); 59 | var visitor = CreateSUT(visitor1, visitor2); 60 | var schema = new Schema(); 61 | 62 | visitor.Visit(schema); 63 | 64 | visitor1.Received(1).Visit(Arg.Is(schema)); 65 | visitor2.Received(1).Visit(Arg.Is(schema)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Builders/SchemaBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using FluentAssertions; 4 | using GraphQL; 5 | using GraphQL.Types; 6 | using NSubstitute; 7 | using NSubstitute.ReceivedExtensions; 8 | using Our.Umbraco.GraphQL.Adapters; 9 | using Our.Umbraco.GraphQL.Adapters.Visitors; 10 | using Our.Umbraco.GraphQL.Builders; 11 | using Xunit; 12 | 13 | namespace Our.Umbraco.GraphQL.Tests.Builders 14 | { 15 | public class SchemaBuilderTests 16 | { 17 | private SchemaBuilder CreateSUT(IGraphTypeAdapter graphTypeAdapter = null, GraphVisitor visitor = null) 18 | { 19 | if (graphTypeAdapter == null) 20 | { 21 | var queryObjectGraphType = new ObjectGraphType(); 22 | graphTypeAdapter = Substitute.For(); 23 | graphTypeAdapter.Adapt(Arg.Is(typeof(Query).GetTypeInfo())).Returns(queryObjectGraphType); 24 | } 25 | 26 | return new SchemaBuilderWithQuery(graphTypeAdapter, new FuncServiceProvider(Activator.CreateInstance), visitor); 27 | } 28 | 29 | [Fact] 30 | public void Build_SchemaWithQueryProperty_ReturnsSchemaWithQuery() 31 | { 32 | var graphTypeAdapter = Substitute.For(); 33 | var queryObjectGraphType = new ObjectGraphType(); 34 | graphTypeAdapter.Adapt(Arg.Is(typeof(Query).GetTypeInfo())).Returns(queryObjectGraphType); 35 | var schemaBuilder = CreateSUT(graphTypeAdapter); 36 | 37 | var schema = schemaBuilder.Build(); 38 | 39 | schema.Query.Should().Be(queryObjectGraphType); 40 | } 41 | 42 | [Fact] 43 | public void Build_WithVisitor_CallsVisitWithSchema() 44 | { 45 | var visitor = Substitute.For(); 46 | var schemaBuilder = CreateSUT(visitor: visitor); 47 | 48 | var schema = schemaBuilder.Build(); 49 | 50 | visitor.Received(1).Visit(Arg.Is(schema)); 51 | } 52 | 53 | private class EmptySchema 54 | { 55 | } 56 | 57 | private class SchemaWithSetOnlyQuery 58 | { 59 | private Query _query; 60 | public Query Query 61 | { 62 | set => _query = value; 63 | } 64 | } 65 | 66 | private class SchemaBuilderWithQuery : SchemaBuilder 67 | { 68 | public SchemaBuilderWithQuery(IGraphTypeAdapter graphTypeAdapter, IServiceProvider serviceProvider, IGraphVisitor visitor) : base(graphTypeAdapter, serviceProvider, visitor) 69 | { 70 | } 71 | 72 | public override Type SchemaType => typeof(SchemaWithQuery); 73 | } 74 | 75 | private class SchemaWithQuery 76 | { 77 | public Query Query { get; set; } 78 | } 79 | 80 | private class Query 81 | { 82 | } 83 | 84 | private class MyType 85 | { 86 | public string Name { get; set; } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Filters/EqFilterTests.cs: -------------------------------------------------------------------------------- 1 | using Our.Umbraco.GraphQL.Filters; 2 | using Xunit; 3 | 4 | namespace Our.Umbraco.GraphQL.Tests.Filters 5 | { 6 | public class EqFilterTests : FilterTest 7 | { 8 | [Fact] 9 | public void IsSatisfiedBy_WithEqualStringValue_ReturnsTrue() 10 | { 11 | var value = "test"; 12 | var filter = new EqFilter(ValueResolver, value); 13 | 14 | var result = filter.IsSatisfiedBy(value); 15 | 16 | Assert.True(result); 17 | } 18 | 19 | [Fact] 20 | public void IsSatisfiedBy_WithEqualIntValue_ReturnsTrue() 21 | { 22 | var value = 1; 23 | var filter = new EqFilter(ValueResolver, value); 24 | 25 | var result = filter.IsSatisfiedBy(value); 26 | 27 | Assert.True(result); 28 | } 29 | 30 | [Fact] 31 | public void IsSatisfiedBy_WithEqualObjectValue_ReturnsTrue() 32 | { 33 | var value = new object(); 34 | var filter = new EqFilter(ValueResolver, value); 35 | 36 | var result = filter.IsSatisfiedBy(value); 37 | 38 | Assert.True(result); 39 | } 40 | 41 | [Fact] 42 | public void IsSatisfiedBy_WithNonEqualValue_ReturnsFalse() 43 | { 44 | var value = 1; 45 | var input = "test"; 46 | var filter = new EqFilter(ValueResolver, value); 47 | 48 | var result = filter.IsSatisfiedBy(input); 49 | 50 | Assert.False(result); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Filters/FilterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Our.Umbraco.GraphQL.Tests.Filters 4 | { 5 | public abstract class FilterTest 6 | { 7 | protected Func ValueResolver = input => input; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Json/InputConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FluentAssertions; 4 | using GraphQL; 5 | using Newtonsoft.Json; 6 | using Our.Umbraco.GraphQL.Json.Converters; 7 | using Xunit; 8 | 9 | namespace Our.Umbraco.GraphQL.Tests.Json 10 | { 11 | public class InputConverterTests 12 | { 13 | [Fact] 14 | public void CanConvert_WithInputsType_ReturnsTrue() 15 | { 16 | var converter = new InputsConverter(); 17 | 18 | converter.CanConvert(typeof(Inputs)) 19 | .Should().BeTrue(); 20 | } 21 | 22 | [Fact] 23 | public void CanConvert_WithNonInputsType_ReturnsFalse() 24 | { 25 | var converter = new InputsConverter(); 26 | 27 | converter.CanConvert(typeof(string)) 28 | .Should().BeFalse(); 29 | } 30 | 31 | [Fact] 32 | public void CanWrite_ReturnsFalse() 33 | { 34 | var converter = new InputsConverter(); 35 | 36 | converter.CanWrite.Should().BeFalse(); 37 | } 38 | 39 | [Fact] 40 | public void WriteJson_ThrowsNotSupportedException() 41 | { 42 | var converter = new InputsConverter(); 43 | 44 | Action action = () => converter.WriteJson(null, null, null); 45 | action.Should().Throw(); 46 | } 47 | 48 | [Fact] 49 | public void ReadJson_WithData_ReturnsInputs() 50 | { 51 | var converter = new InputsConverter(); 52 | 53 | var data = converter.ReadJson(new JsonTextReader(new StringReader("{\"name\": \"test\"}")), typeof(Inputs), 54 | null, new JsonSerializer()); 55 | 56 | data.Should().BeOfType() 57 | .Which.Should().ContainKey("name") 58 | .WhichValue.Should().Be("test"); 59 | } 60 | 61 | [Fact] 62 | public void ReadJson_WithNoData_ReturnsEmptyInputs() 63 | { 64 | var converter = new InputsConverter(); 65 | 66 | var data = converter.ReadJson(new JsonTextReader(new StringReader("null")), typeof(Inputs), 67 | null, new JsonSerializer()); 68 | 69 | data.Should().BeOfType() 70 | .Which.Should().BeEmpty(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Our.Umbraco.GraphQL.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | 2 5 | 6 | 7 | 2 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Types/OrderByExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Types; 3 | using Xunit; 4 | 5 | namespace Our.Umbraco.GraphQL.Tests.Types 6 | { 7 | public class OrderByExtensionsTests 8 | { 9 | [Fact] 10 | public void OrderBy_WithSingleOrderByAsc_OrdersByAsc() 11 | { 12 | var collection = new[] 13 | { 14 | new Item { Name = "c"}, 15 | new Item { Name = "b" }, 16 | new Item { Name = "n"}, 17 | new Item { Name = "a"}, 18 | }; 19 | 20 | var result = collection.OrderBy(new[] 21 | { 22 | new OrderBy("Name", SortOrder.Ascending, x => ((Item) x.Source).Name) 23 | }); 24 | 25 | result.Should().BeInAscendingOrder(x => x.Name); 26 | } 27 | 28 | [Fact] 29 | public void OrderBy_WithSingleOrderByDesc_OrdersByDesc() 30 | { 31 | var collection = new[] 32 | { 33 | new Item { Name = "c"}, 34 | new Item { Name = "b" }, 35 | new Item { Name = "n"}, 36 | new Item { Name = "a"}, 37 | }; 38 | 39 | var result = collection.OrderBy(new[] 40 | { 41 | new OrderBy("Name", SortOrder.Descending, x => ((Item) x.Source).Name) 42 | }); 43 | 44 | result.Should().BeInDescendingOrder(x => x.Name); 45 | } 46 | 47 | [Fact] 48 | public void OrderBy_WithMultipleOrderByAscending_OrdersByFirstThenNext() 49 | { 50 | var collection = new[] 51 | { 52 | new Item { Id = new Id("1"), Name = "c", Stock = 1 }, 53 | new Item { Id = new Id("2"), Name = "b", Stock = 2 }, 54 | new Item { Id = new Id("3"), Name = "n", Stock = 1 }, 55 | new Item { Id = new Id("4"), Name = "a", Stock = 4 }, 56 | }; 57 | 58 | var result = collection.OrderBy(new[] 59 | { 60 | new OrderBy("Stock", SortOrder.Ascending, x => ((Item) x.Source).Stock), 61 | new OrderBy("Name", SortOrder.Ascending, x => ((Item) x.Source).Name), 62 | }); 63 | 64 | result.Should().SatisfyRespectively( 65 | x => x.Name.Should().Be("c"), 66 | x => x.Name.Should().Be("n"), 67 | x => x.Name.Should().Be("b"), 68 | x => x.Name.Should().Be("a")); 69 | } 70 | 71 | [Fact] 72 | public void OrderBy_WithMultipleOrderByAscendingThenDescending_OrdersByFirstThenNext() 73 | { 74 | var collection = new[] 75 | { 76 | new Item { Id = new Id("1"), Name = "c", Stock = 1 }, 77 | new Item { Id = new Id("2"), Name = "b", Stock = 2 }, 78 | new Item { Id = new Id("3"), Name = "n", Stock = 1 }, 79 | new Item { Id = new Id("4"), Name = "a", Stock = 4 }, 80 | }; 81 | 82 | var result = collection.OrderBy(new[] 83 | { 84 | new OrderBy("Stock", SortOrder.Ascending, x => ((Item) x.Source).Stock), 85 | new OrderBy("Name", SortOrder.Descending, x => ((Item) x.Source).Name), 86 | }); 87 | 88 | result.Should().SatisfyRespectively( 89 | x => x.Name.Should().Be("n"), 90 | x => x.Name.Should().Be("c"), 91 | x => x.Name.Should().Be("b"), 92 | x => x.Name.Should().Be("a")); 93 | } 94 | 95 | private class Item 96 | { 97 | public Id Id { get; set; } 98 | public string Name { get; set; } 99 | public int Stock { get; set; } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Types/PublishedContent/PublishedContentAtRootQueryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using NSubstitute; 3 | using Our.Umbraco.GraphQL.Types.PublishedContent; 4 | using System; 5 | using Umbraco.Cms.Core.Models.PublishedContent; 6 | using Umbraco.Cms.Core.PublishedCache; 7 | using Xunit; 8 | 9 | namespace Our.Umbraco.GraphQL.Tests.Types.PublishedContent 10 | { 11 | public class PublishedContentAtRootQueryTests 12 | { 13 | private PublishedContentAtRootQuery CreateSUT(IPublishedSnapshotAccessor snapshotAccessor) 14 | { 15 | return new PublishedContentAtRootQuery(snapshotAccessor); 16 | } 17 | 18 | [Fact] 19 | public void All_WhenCalled_ReturnsRootContent() 20 | { 21 | var items = new[] 22 | { 23 | Substitute.For(), 24 | Substitute.For(), 25 | Substitute.For(), 26 | }; 27 | 28 | var snapshotAccessor = Substitute.For(); 29 | if (!snapshotAccessor.TryGetPublishedSnapshot(out var publishedSnapshot)) 30 | { 31 | throw new InvalidOperationException("Wasn't possible to a get a valid Snapshot"); 32 | } 33 | publishedSnapshot.Content.GetAtRoot() 34 | .Returns(items); 35 | var query = CreateSUT(snapshotAccessor); 36 | 37 | var results = query.All(); 38 | 39 | results.Items.Should().Equal(items); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/Our.Umbraco.GraphQL.Tests/Types/PublishedContent/UmbracoQueryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Our.Umbraco.GraphQL.Types.PublishedContent; 3 | using Xunit; 4 | 5 | namespace Our.Umbraco.GraphQL.Tests.Types.PublishedContent 6 | { 7 | public class ExtendQueryWithUmbracoQueryTests 8 | { 9 | [Fact] 10 | public void ExtendQueryWithUmbracoQuery_Umbraco_WhenCalled_ReturnsUmbracoQuery() 11 | { 12 | var extendQuery = new ExtendQueryWithUmbracoQuery(); 13 | 14 | var expected = new UmbracoQuery(); 15 | var actual = extendQuery.Umbraco(expected); 16 | 17 | actual.Should().Be(expected); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- 1 | .fake/ 2 | .nuget/ -------------------------------------------------------------------------------- /tools/build.fsx: -------------------------------------------------------------------------------- 1 | // include Fake lib 2 | #r "paket: 3 | source nuget/dotnetcore 4 | source https://api.nuget.org/v3/index.json 5 | nuget FSharp.Core ~> 4.1 6 | nuget Fake.Core.Target 7 | nuget Fake.DotNet.Cli 8 | nuget Fake.IO.FileSystem //" 9 | #load "./.fake/build.fsx/intellisense.fsx" 10 | 11 | open System 12 | open System.IO 13 | open Fake.Core 14 | open Fake.DotNet 15 | open Fake.IO 16 | 17 | // Properties 18 | let currentDirectory = Directory.GetCurrentDirectory() 19 | let solutionFile = Directory.findFirstMatchingFile "*.sln" currentDirectory 20 | let artifactsDir = Path.getFullName "./artifacts/" 21 | 22 | let buildVersion = if not BuildServer.isLocalBuild then Some BuildServer.buildVersion else None 23 | 24 | // Targets 25 | Target.create "Clean" (fun _ -> 26 | Shell.cleanDirs [artifactsDir] 27 | ) 28 | 29 | Target.create "Build" (fun _ -> 30 | let setMSBuildParams (defaults:MSBuild.CliArguments) = 31 | { defaults with 32 | Properties = 33 | [ 34 | "Version_Suffix", if buildVersion.IsSome then buildVersion.Value else "" 35 | ] 36 | } 37 | 38 | DotNet.build (fun c -> 39 | { c with 40 | Configuration = DotNet.BuildConfiguration.Release 41 | MSBuildParams = setMSBuildParams c.MSBuildParams 42 | }) solutionFile 43 | ) 44 | 45 | Target.create "Test" (fun _ -> 46 | DotNet.test (fun c -> 47 | { c with 48 | NoBuild = true 49 | Configuration = DotNet.BuildConfiguration.Release 50 | }) solutionFile 51 | ) 52 | 53 | Target.create "Package" (fun _ -> 54 | DotNet.pack (fun c -> 55 | { c with 56 | NoBuild = true 57 | Configuration = DotNet.BuildConfiguration.Release 58 | OutputPath = Some artifactsDir 59 | VersionSuffix = buildVersion 60 | }) solutionFile 61 | ) 62 | 63 | Target.create "Default" ignore 64 | 65 | open Fake.Core.TargetOperators 66 | 67 | "Clean" 68 | ==> "Build" 69 | ==> "Test" 70 | ==> "Default" 71 | 72 | "Clean" 73 | ==> "Build" 74 | ==> "Test" 75 | ==> "Package" 76 | 77 | // start build 78 | Target.runOrDefault "Default" 79 | --------------------------------------------------------------------------------