├── .circleci └── config.yml ├── .gitignore ├── department-service ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── graphql │ │ └── departments │ │ │ ├── queries │ │ │ ├── departments.graphql │ │ │ └── departmentsByOrganization.graphql │ │ │ └── schema.json │ ├── java │ │ ├── META-INF │ │ │ └── MANIFEST.MF │ │ ├── pl │ │ │ └── piomin │ │ │ │ └── services │ │ │ │ └── department │ │ │ │ ├── DepartmentApplication.java │ │ │ │ ├── client │ │ │ │ ├── EmployeeClient.java │ │ │ │ └── EmployeesByDepartmentQuery.java │ │ │ │ ├── model │ │ │ │ ├── Department.java │ │ │ │ └── Employee.java │ │ │ │ ├── repository │ │ │ │ └── DepartmentRepository.java │ │ │ │ └── resolver │ │ │ │ ├── DepartmentMutations.java │ │ │ │ └── DepartmentQueries.java │ │ └── type │ │ │ └── CustomType.java │ └── resources │ │ ├── bootstrap.yml │ │ └── graphql │ │ └── department.graphqls │ └── test │ └── java │ └── pl │ └── piomin │ └── services │ └── department │ └── DepartmentAppTests.java ├── discovery-service ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ ├── META-INF │ │ └── MANIFEST.MF │ └── pl │ │ └── piomin │ │ └── services │ │ └── discovery │ │ └── DiscoveryApplication.java │ └── resources │ └── bootstrap.yml ├── employee-service ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── graphql │ │ └── employees │ │ │ ├── queries │ │ │ └── employees.graphql │ │ │ └── schema.json │ ├── java │ │ ├── META-INF │ │ │ └── MANIFEST.MF │ │ └── pl │ │ │ └── piomin │ │ │ └── services │ │ │ └── employee │ │ │ ├── EmployeeApplication.java │ │ │ ├── model │ │ │ ├── Employee.java │ │ │ └── EmployeesQuery.java │ │ │ ├── repository │ │ │ └── EmployeeRepository.java │ │ │ └── resolver │ │ │ ├── EmployeeMutations.java │ │ │ └── EmployeeQueries.java │ └── resources │ │ ├── bootstrap.yml │ │ └── graphql │ │ └── employee.graphqls │ └── test │ └── java │ └── pl │ └── piomin │ └── services │ └── employee │ └── EmployeeApiTest.java ├── organization-service ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ ├── META-INF │ │ └── MANIFEST.MF │ ├── pl │ │ └── piomin │ │ │ └── services │ │ │ └── organization │ │ │ ├── OrganizationApplication.java │ │ │ ├── client │ │ │ ├── DepartmentClient.java │ │ │ ├── EmployeeClient.java │ │ │ ├── EmployeesByDepartmentQuery.java │ │ │ └── EmployeesByOrganizationQuery.java │ │ │ ├── model │ │ │ ├── Department.java │ │ │ ├── Employee.java │ │ │ └── Organization.java │ │ │ ├── repository │ │ │ └── OrganizationRepository.java │ │ │ └── resolver │ │ │ ├── OrganizationMutations.java │ │ │ └── OrganizationQueries.java │ └── type │ │ └── CustomType.java │ └── resources │ ├── bootstrap.yml │ └── graphql │ └── organization.graphqls ├── pom.xml ├── readme.md └── renovate.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | analyze: 5 | docker: 6 | - image: 'cimg/openjdk:8.0' 7 | steps: 8 | - checkout 9 | - run: 10 | name: Analyze on SonarCloud 11 | command: mvn verify sonar:sonar 12 | 13 | executors: 14 | jdk: 15 | docker: 16 | - image: 'cimg/openjdk:8.0' 17 | 18 | orbs: 19 | maven: circleci/maven@1.4.0 20 | 21 | workflows: 22 | maven_test: 23 | jobs: 24 | - maven/test: 25 | executor: jdk 26 | - analyze: 27 | context: SonarCloud -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | /.settings/ 3 | -------------------------------------------------------------------------------- /department-service/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | /.settings/ 5 | -------------------------------------------------------------------------------- /department-service/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-graphql-microservices 7 | 1.0-SNAPSHOT 8 | 9 | department-service 10 | 1.0-SNAPSHOT 11 | 12 | 13 | ${project.artifactId} 14 | 15 | 16 | 17 | 18 | org.springframework.cloud 19 | spring-cloud-starter-netflix-eureka-client 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-openfeign 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-openfeign-core 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-actuator 36 | 37 | 38 | com.graphql-java 39 | graphql-spring-boot-starter 40 | 5.0.2 41 | 42 | 43 | com.graphql-java 44 | graphiql-spring-boot-starter 45 | 5.0.2 46 | 47 | 48 | com.graphql-java 49 | voyager-spring-boot-starter 50 | 5.0.2 51 | 52 | 53 | com.graphql-java 54 | graphql-java 55 | 9.2 56 | 57 | 58 | com.graphql-java 59 | graphql-java-tools 60 | 5.2.3 61 | 62 | 63 | org.springframework.cloud 64 | spring-cloud-starter-sleuth 65 | 66 | 67 | com.apollographql.apollo 68 | apollo-runtime 69 | 1.0.1 70 | 71 | 72 | com.apollographql.apollo3 73 | apollo-api-jvm 74 | 3.0.0 75 | 76 | 77 | com.apollographql.apollo3 78 | apollo-runtime-jvm 79 | 3.0.0 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-starter-test 84 | test 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-maven-plugin 93 | 94 | 95 | com.github.aoudiamoncef 96 | apollo-client-maven-plugin 97 | 5.0.0 98 | 99 | 100 | 101 | generate 102 | 103 | 104 | 105 | 106 | 107 | departments 108 | 109 | pl.piomin.services.department.client 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.codehaus.mojo 120 | build-helper-maven-plugin 121 | 122 | 123 | add-source 124 | generate-sources 125 | 126 | add-source 127 | 128 | 129 | 130 | target/generated-sources/apollo/departments 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /department-service/src/main/graphql/departments/queries/departments.graphql: -------------------------------------------------------------------------------- 1 | query departments { 2 | departments { 3 | id 4 | name 5 | organizationId 6 | } 7 | } -------------------------------------------------------------------------------- /department-service/src/main/graphql/departments/queries/departmentsByOrganization.graphql: -------------------------------------------------------------------------------- 1 | query departmentsByOrganization { 2 | departmentsByOrganization(organizationId: 1) { 3 | id 4 | name 5 | } 6 | } -------------------------------------------------------------------------------- /department-service/src/main/graphql/departments/schema.json: -------------------------------------------------------------------------------- 1 | {"data":{"__schema":{"queryType":{"name":"DepartmentQueries"},"mutationType":{"name":"DepartmentMutations"},"subscriptionType":null,"types":[{"kind":"OBJECT","name":"DepartmentQueries","description":"","fields":[{"name":"departments","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Department","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"department","description":"","args":[{"name":"id","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Department","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"departmentsByOrganization","description":"","args":[{"name":"organizationId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Department","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"departmentsByOrganizationWithEmployees","description":"","args":[{"name":"organizationId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Department","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"ID","description":"Built-in ID","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Int","description":"Built-in Int","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DepartmentMutations","description":"","fields":[{"name":"newDepartment","description":"","args":[{"name":"department","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DepartmentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Department","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteDepartment","description":"","args":[{"name":"id","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null}],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateDepartment","description":"","args":[{"name":"id","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"department","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DepartmentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Department","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Boolean","description":"Built-in Boolean","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Department","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizationId","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"employees","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Employee","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"String","description":"Built-in String","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Employee","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"position","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"salary","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DepartmentInput","description":"","fields":null,"inputFields":[{"name":"organizationId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"name","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Schema","description":"A GraphQL Introspection defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, the entry points for query, mutation, and subscription operations.","fields":[{"name":"types","description":"A list of all types supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"queryType","description":"The type that query operations will be rooted at.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mutationType","description":"If this server supports mutation, the type that mutation operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"directives","description":"'A list of all directives supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Directive"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"subscriptionType","description":"'If this server support subscription, the type that subscription operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Type","description":null,"fields":[{"name":"kind","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__TypeKind","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"fields","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Field","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"interfaces","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"possibleTypes","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"enumValues","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__EnumValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"inputFields","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"ofType","description":null,"args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__TypeKind","description":"An enum describing what kind of type a given __Type is","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SCALAR","description":"Indicates this type is a scalar.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Indicates this type is an object. `fields` and `interfaces` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Indicates this type is a union. `possibleTypes` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Indicates this type is an enum. `enumValues` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Indicates this type is an input object. `inputFields` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"LIST","description":"Indicates this type is a list. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"NON_NULL","description":"Indicates this type is a non-null. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"__Field","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__InputValue","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"defaultValue","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__EnumValue","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Directive","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locations","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__DirectiveLocation","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"onOperation","description":null,"args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onFragment","description":null,"args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onField","description":null,"args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":true,"deprecationReason":"Use `locations`."}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__DirectiveLocation","description":"An enum describing valid locations where a directive can be placed","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"QUERY","description":"Indicates the directive is valid on queries.","isDeprecated":false,"deprecationReason":null},{"name":"MUTATION","description":"Indicates the directive is valid on mutations.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD","description":"Indicates the directive is valid on fields.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_DEFINITION","description":"Indicates the directive is valid on fragment definitions.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_SPREAD","description":"Indicates the directive is valid on fragment spreads.","isDeprecated":false,"deprecationReason":null},{"name":"INLINE_FRAGMENT","description":"Indicates the directive is valid on inline fragments.","isDeprecated":false,"deprecationReason":null},{"name":"SCHEMA","description":"Indicates the directive is valid on a schema SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"SCALAR","description":"Indicates the directive is valid on a scalar SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Indicates the directive is valid on an object SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD_DEFINITION","description":"Indicates the directive is valid on a field SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"ARGUMENT_DEFINITION","description":"Indicates the directive is valid on a field argument SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Indicates the directive is valid on an interface SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Indicates the directive is valid on an union SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Indicates the directive is valid on an enum SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM_VALUE","description":"Indicates the directive is valid on an enum value SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Indicates the directive is valid on an input object SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_FIELD_DEFINITION","description":"Indicates the directive is valid on an input object field SDL definition.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null}],"directives":[{"name":"include","description":"Directs the executor to include this field or fragment only when the `if` argument is true","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Included when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"skip","description":"Directs the executor to skip this field or fragment when the `if`'argument is true.","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Skipped when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"defer","description":"This directive allows results to be deferred during execution","locations":["FIELD"],"args":[]}]}}} -------------------------------------------------------------------------------- /department-service/src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/DepartmentApplication.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.openfeign.EnableFeignClients; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | import pl.piomin.services.department.model.Department; 10 | import pl.piomin.services.department.repository.DepartmentRepository; 11 | 12 | @SpringBootApplication 13 | @EnableDiscoveryClient 14 | @EnableFeignClients 15 | public class DepartmentApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(DepartmentApplication.class, args); 19 | } 20 | 21 | @Bean 22 | DepartmentRepository repository() { 23 | DepartmentRepository repository = new DepartmentRepository(); 24 | repository.add(new Department(1L, "Development")); 25 | repository.add(new Department(1L, "Operations")); 26 | repository.add(new Department(2L, "Development")); 27 | repository.add(new Department(2L, "Operations")); 28 | return repository; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/client/EmployeeClient.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | import java.util.concurrent.CountDownLatch; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.stream.Collectors; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | 15 | import com.apollographql.apollo.ApolloCall.Callback; 16 | import com.apollographql.apollo.ApolloClient; 17 | import com.apollographql.apollo.api.Response; 18 | import com.apollographql.apollo.exception.ApolloException; 19 | import com.netflix.appinfo.InstanceInfo; 20 | import com.netflix.discovery.EurekaClient; 21 | import com.netflix.discovery.shared.Application; 22 | 23 | import pl.piomin.services.department.model.Employee; 24 | 25 | @Component 26 | public class EmployeeClient { 27 | 28 | private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeClient.class); 29 | private static final int TIMEOUT = 5000; 30 | private static final String SERVICE_NAME = "EMPLOYEE-SERVICE"; 31 | private static final String SERVER_URL = "http://localhost:%d/graphql"; 32 | 33 | Random r = new Random(); 34 | 35 | @Autowired 36 | private EurekaClient discoveryClient; 37 | 38 | public List findByDepartment(Long departmentId) throws InterruptedException { 39 | List employees = new ArrayList<>(); 40 | Application app = discoveryClient.getApplication(SERVICE_NAME); 41 | InstanceInfo ii = app.getInstances().get(r.nextInt(app.size())); 42 | ApolloClient client = ApolloClient.builder().serverUrl(String.format(SERVER_URL, ii.getPort())).build(); 43 | CountDownLatch lock = new CountDownLatch(1); 44 | client.query(EmployeesByDepartmentQuery.builder().departmentId(departmentId.intValue()).build()).enqueue(new Callback() { 45 | 46 | @Override 47 | public void onFailure(ApolloException ex) { 48 | LOGGER.info("Err: {}", ex); 49 | lock.countDown(); 50 | } 51 | 52 | @Override 53 | public void onResponse(Response res) { 54 | LOGGER.info("Res: {}", res); 55 | employees.addAll(res.data().employeesByDepartment().stream().map(emp -> new Employee(Long.valueOf(emp.id()), emp.name(), emp.position(), emp.salary())).collect(Collectors.toList())); 56 | lock.countDown(); 57 | } 58 | 59 | }); 60 | lock.await(TIMEOUT, TimeUnit.MILLISECONDS); 61 | return employees; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/client/EmployeesByDepartmentQuery.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.client; 2 | 3 | import com.apollographql.apollo.api.InputFieldMarshaller; 4 | import com.apollographql.apollo.api.InputFieldWriter; 5 | import com.apollographql.apollo.api.Operation; 6 | import com.apollographql.apollo.api.OperationName; 7 | import com.apollographql.apollo.api.Query; 8 | import com.apollographql.apollo.api.ResponseField; 9 | import com.apollographql.apollo.api.ResponseFieldMapper; 10 | import com.apollographql.apollo.api.ResponseFieldMarshaller; 11 | import com.apollographql.apollo.api.ResponseReader; 12 | import com.apollographql.apollo.api.ResponseWriter; 13 | import com.apollographql.apollo.api.internal.UnmodifiableMapBuilder; 14 | import com.apollographql.apollo.api.internal.Utils; 15 | import java.io.IOException; 16 | import java.lang.Object; 17 | import java.lang.Override; 18 | import java.lang.String; 19 | import java.util.Collections; 20 | import java.util.LinkedHashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import javax.annotation.Generated; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | import type.CustomType; 27 | 28 | @Generated("Apollo GraphQL") 29 | public final class EmployeesByDepartmentQuery implements Query { 30 | public static final String OPERATION_DEFINITION = "query EmployeesByDepartment($departmentId: Int!) {\n" 31 | + " employeesByDepartment(departmentId: $departmentId) {\n" 32 | + " __typename\n" 33 | + " id\n" 34 | + " name\n" 35 | + " position\n" 36 | + " salary\n" 37 | + " }\n" 38 | + "}"; 39 | 40 | public static final String OPERATION_ID = "14971a7245ab04844941749beef351913563d71c03d8a05a48ca6375230e90aa"; 41 | 42 | public static final String QUERY_DOCUMENT = OPERATION_DEFINITION; 43 | 44 | public static final OperationName OPERATION_NAME = new OperationName() { 45 | @Override 46 | public String name() { 47 | return "EmployeesByDepartment"; 48 | } 49 | }; 50 | 51 | private final EmployeesByDepartmentQuery.Variables variables; 52 | 53 | public EmployeesByDepartmentQuery(int departmentId) { 54 | variables = new EmployeesByDepartmentQuery.Variables(departmentId); 55 | } 56 | 57 | @Override 58 | public String operationId() { 59 | return OPERATION_ID; 60 | } 61 | 62 | @Override 63 | public String queryDocument() { 64 | return QUERY_DOCUMENT; 65 | } 66 | 67 | @Override 68 | public EmployeesByDepartmentQuery.Data wrapData(EmployeesByDepartmentQuery.Data data) { 69 | return data; 70 | } 71 | 72 | @Override 73 | public EmployeesByDepartmentQuery.Variables variables() { 74 | return variables; 75 | } 76 | 77 | @Override 78 | public ResponseFieldMapper responseFieldMapper() { 79 | return new Data.Mapper(); 80 | } 81 | 82 | public static Builder builder() { 83 | return new Builder(); 84 | } 85 | 86 | @Override 87 | public OperationName name() { 88 | return OPERATION_NAME; 89 | } 90 | 91 | public static final class Builder { 92 | private int departmentId; 93 | 94 | Builder() { 95 | } 96 | 97 | public Builder departmentId(int departmentId) { 98 | this.departmentId = departmentId; 99 | return this; 100 | } 101 | 102 | public EmployeesByDepartmentQuery build() { 103 | return new EmployeesByDepartmentQuery(departmentId); 104 | } 105 | } 106 | 107 | public static final class Variables extends Operation.Variables { 108 | private final int departmentId; 109 | 110 | private final transient Map valueMap = new LinkedHashMap<>(); 111 | 112 | Variables(int departmentId) { 113 | this.departmentId = departmentId; 114 | this.valueMap.put("departmentId", departmentId); 115 | } 116 | 117 | public int departmentId() { 118 | return departmentId; 119 | } 120 | 121 | @Override 122 | public Map valueMap() { 123 | return Collections.unmodifiableMap(valueMap); 124 | } 125 | 126 | @Override 127 | public InputFieldMarshaller marshaller() { 128 | return new InputFieldMarshaller() { 129 | @Override 130 | public void marshal(InputFieldWriter writer) throws IOException { 131 | writer.writeInt("departmentId", departmentId); 132 | } 133 | }; 134 | } 135 | } 136 | 137 | public static class Data implements Operation.Data { 138 | static final ResponseField[] $responseFields = { 139 | ResponseField.forList("employeesByDepartment", "employeesByDepartment", new UnmodifiableMapBuilder(1) 140 | .put("departmentId", new UnmodifiableMapBuilder(2) 141 | .put("kind", "Variable") 142 | .put("variableName", "departmentId") 143 | .build()) 144 | .build(), true, Collections.emptyList()) 145 | }; 146 | 147 | final @Nullable List employeesByDepartment; 148 | 149 | private volatile String $toString; 150 | 151 | private volatile int $hashCode; 152 | 153 | private volatile boolean $hashCodeMemoized; 154 | 155 | public Data(@Nullable List employeesByDepartment) { 156 | this.employeesByDepartment = employeesByDepartment; 157 | } 158 | 159 | public @Nullable List employeesByDepartment() { 160 | return this.employeesByDepartment; 161 | } 162 | 163 | public ResponseFieldMarshaller marshaller() { 164 | return new ResponseFieldMarshaller() { 165 | @Override 166 | public void marshal(ResponseWriter writer) { 167 | writer.writeList($responseFields[0], employeesByDepartment, new ResponseWriter.ListWriter() { 168 | @Override 169 | public void write(@Nullable List list, @NotNull ResponseWriter.ListItemWriter listItemWriter) { 170 | listItemWriter.writeList(list, this); 171 | } 172 | 173 | // @Override 174 | // public void write(Object value, ResponseWriter.ListItemWriter listItemWriter) { 175 | // listItemWriter.writeObject(((EmployeesByDepartment) value).marshaller()); 176 | // } 177 | }); 178 | } 179 | }; 180 | } 181 | 182 | @Override 183 | public String toString() { 184 | if ($toString == null) { 185 | $toString = "Data{" 186 | + "employeesByDepartment=" + employeesByDepartment 187 | + "}"; 188 | } 189 | return $toString; 190 | } 191 | 192 | @Override 193 | public boolean equals(Object o) { 194 | if (o == this) { 195 | return true; 196 | } 197 | if (o instanceof Data) { 198 | Data that = (Data) o; 199 | return ((this.employeesByDepartment == null) ? (that.employeesByDepartment == null) : this.employeesByDepartment.equals(that.employeesByDepartment)); 200 | } 201 | return false; 202 | } 203 | 204 | @Override 205 | public int hashCode() { 206 | if (!$hashCodeMemoized) { 207 | int h = 1; 208 | h *= 1000003; 209 | h ^= (employeesByDepartment == null) ? 0 : employeesByDepartment.hashCode(); 210 | $hashCode = h; 211 | $hashCodeMemoized = true; 212 | } 213 | return $hashCode; 214 | } 215 | 216 | public static final class Mapper implements ResponseFieldMapper { 217 | final EmployeesByDepartment.Mapper employeesByDepartmentFieldMapper = new EmployeesByDepartment.Mapper(); 218 | 219 | @Override 220 | public Data map(ResponseReader reader) { 221 | final List employeesByDepartment = reader.readList($responseFields[0], new ResponseReader.ListReader() { 222 | @Override 223 | public EmployeesByDepartment read(ResponseReader.ListItemReader listItemReader) { 224 | return listItemReader.readObject(new ResponseReader.ObjectReader() { 225 | @Override 226 | public EmployeesByDepartment read(ResponseReader reader) { 227 | return employeesByDepartmentFieldMapper.map(reader); 228 | } 229 | }); 230 | } 231 | }); 232 | return new Data(employeesByDepartment); 233 | } 234 | } 235 | } 236 | 237 | public static class EmployeesByDepartment { 238 | static final ResponseField[] $responseFields = { 239 | ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), 240 | ResponseField.forCustomType("id", "id", null, false, CustomType.ID, Collections.emptyList()), 241 | ResponseField.forString("name", "name", null, false, Collections.emptyList()), 242 | ResponseField.forString("position", "position", null, false, Collections.emptyList()), 243 | ResponseField.forInt("salary", "salary", null, false, Collections.emptyList()) 244 | }; 245 | 246 | final @NotNull String __typename; 247 | 248 | final @NotNull String id; 249 | 250 | final @NotNull String name; 251 | 252 | final @NotNull String position; 253 | 254 | final int salary; 255 | 256 | private volatile String $toString; 257 | 258 | private volatile int $hashCode; 259 | 260 | private volatile boolean $hashCodeMemoized; 261 | 262 | public EmployeesByDepartment(@NotNull String __typename, @NotNull String id, 263 | @NotNull String name, @NotNull String position, int salary) { 264 | this.__typename = Utils.checkNotNull(__typename, "__typename == null"); 265 | this.id = Utils.checkNotNull(id, "id == null"); 266 | this.name = Utils.checkNotNull(name, "name == null"); 267 | this.position = Utils.checkNotNull(position, "position == null"); 268 | this.salary = salary; 269 | } 270 | 271 | public @NotNull String __typename() { 272 | return this.__typename; 273 | } 274 | 275 | public @NotNull String id() { 276 | return this.id; 277 | } 278 | 279 | public @NotNull String name() { 280 | return this.name; 281 | } 282 | 283 | public @NotNull String position() { 284 | return this.position; 285 | } 286 | 287 | public int salary() { 288 | return this.salary; 289 | } 290 | 291 | public ResponseFieldMarshaller marshaller() { 292 | return new ResponseFieldMarshaller() { 293 | @Override 294 | public void marshal(ResponseWriter writer) { 295 | writer.writeString($responseFields[0], __typename); 296 | writer.writeCustom((ResponseField.CustomTypeField) $responseFields[1], id); 297 | writer.writeString($responseFields[2], name); 298 | writer.writeString($responseFields[3], position); 299 | writer.writeInt($responseFields[4], salary); 300 | } 301 | }; 302 | } 303 | 304 | @Override 305 | public String toString() { 306 | if ($toString == null) { 307 | $toString = "EmployeesByDepartment{" 308 | + "__typename=" + __typename + ", " 309 | + "id=" + id + ", " 310 | + "name=" + name + ", " 311 | + "position=" + position + ", " 312 | + "salary=" + salary 313 | + "}"; 314 | } 315 | return $toString; 316 | } 317 | 318 | @Override 319 | public boolean equals(Object o) { 320 | if (o == this) { 321 | return true; 322 | } 323 | if (o instanceof EmployeesByDepartment) { 324 | EmployeesByDepartment that = (EmployeesByDepartment) o; 325 | return this.__typename.equals(that.__typename) 326 | && this.id.equals(that.id) 327 | && this.name.equals(that.name) 328 | && this.position.equals(that.position) 329 | && this.salary == that.salary; 330 | } 331 | return false; 332 | } 333 | 334 | @Override 335 | public int hashCode() { 336 | if (!$hashCodeMemoized) { 337 | int h = 1; 338 | h *= 1000003; 339 | h ^= __typename.hashCode(); 340 | h *= 1000003; 341 | h ^= id.hashCode(); 342 | h *= 1000003; 343 | h ^= name.hashCode(); 344 | h *= 1000003; 345 | h ^= position.hashCode(); 346 | h *= 1000003; 347 | h ^= salary; 348 | $hashCode = h; 349 | $hashCodeMemoized = true; 350 | } 351 | return $hashCode; 352 | } 353 | 354 | public static final class Mapper implements ResponseFieldMapper { 355 | @Override 356 | public EmployeesByDepartment map(ResponseReader reader) { 357 | final String __typename = reader.readString($responseFields[0]); 358 | final String id = reader.readCustomType((ResponseField.CustomTypeField) $responseFields[1]); 359 | final String name = reader.readString($responseFields[2]); 360 | final String position = reader.readString($responseFields[3]); 361 | final int salary = reader.readInt($responseFields[4]); 362 | return new EmployeesByDepartment(__typename, id, name, position, salary); 363 | } 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/model/Department.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Department { 7 | 8 | private Long id; 9 | private Long organizationId; 10 | private String name; 11 | private List employees = new ArrayList<>(); 12 | 13 | public Department() { 14 | 15 | } 16 | 17 | public Department(Long organizationId, String name) { 18 | super(); 19 | this.organizationId = organizationId; 20 | this.name = name; 21 | } 22 | 23 | public Long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(Long id) { 28 | this.id = id; 29 | } 30 | 31 | public Long getOrganizationId() { 32 | return organizationId; 33 | } 34 | 35 | public void setOrganizationId(Long organizationId) { 36 | this.organizationId = organizationId; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public void setName(String name) { 44 | this.name = name; 45 | } 46 | 47 | public List getEmployees() { 48 | return employees; 49 | } 50 | 51 | public void setEmployees(List employees) { 52 | this.employees = employees; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Department [id=" + id + ", organizationId=" + organizationId + ", name=" + name + "]"; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/model/Employee.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.model; 2 | 3 | public class Employee { 4 | 5 | private Long id; 6 | private String name; 7 | private String position; 8 | private int salary; 9 | 10 | public Employee() { 11 | 12 | } 13 | 14 | public Employee(Long id, String name, String position, int salary) { 15 | super(); 16 | this.id = id; 17 | this.name = name; 18 | this.position = position; 19 | this.salary = salary; 20 | } 21 | 22 | public Long getId() { 23 | return id; 24 | } 25 | 26 | public void setId(Long id) { 27 | this.id = id; 28 | } 29 | 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | 39 | public String getPosition() { 40 | return position; 41 | } 42 | 43 | public void setPosition(String position) { 44 | this.position = position; 45 | } 46 | 47 | public int getSalary() { 48 | return salary; 49 | } 50 | 51 | public void setSalary(int salary) { 52 | this.salary = salary; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Employee [id=" + id + ", name=" + name + ", position=" + position + "]"; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/repository/DepartmentRepository.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.repository; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.stream.Collectors; 7 | 8 | import pl.piomin.services.department.model.Department; 9 | 10 | public class DepartmentRepository { 11 | 12 | private List departments = new ArrayList<>(); 13 | 14 | public Department add(Department department) { 15 | department.setId((long) (departments.size()+1)); 16 | departments.add(department); 17 | return department; 18 | } 19 | 20 | public Department findById(Long id) { 21 | Optional department = departments.stream().filter(a -> a.getId().equals(id)).findFirst(); 22 | if (department.isPresent()) 23 | return department.get(); 24 | else 25 | return null; 26 | } 27 | 28 | public List findAll() { 29 | return departments; 30 | } 31 | 32 | public List findByOrganization(Long organizationId) { 33 | return departments.stream().filter(a -> a.getOrganizationId().equals(organizationId)).collect(Collectors.toList()); 34 | } 35 | 36 | public boolean delete(Long id) { 37 | return departments.removeIf(it -> it.getId() == id.longValue()); 38 | } 39 | 40 | public Department update(Long id, Department department) { 41 | department.setId(id); 42 | int index = departments.indexOf(department); 43 | departments.set(index, department); 44 | return department; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/resolver/DepartmentMutations.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.resolver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import com.coxautodev.graphql.tools.GraphQLMutationResolver; 9 | 10 | import pl.piomin.services.department.model.Department; 11 | import pl.piomin.services.department.repository.DepartmentRepository; 12 | 13 | @Component 14 | public class DepartmentMutations implements GraphQLMutationResolver { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(DepartmentQueries.class); 17 | 18 | @Autowired 19 | DepartmentRepository repository; 20 | 21 | public Department newDepartment(Department department) { 22 | LOGGER.info("Department add: department={}", department); 23 | return repository.add(department); 24 | } 25 | 26 | public boolean deleteDepartment(Long id) { 27 | LOGGER.info("Department delete: id={}", id); 28 | return repository.delete(id); 29 | } 30 | 31 | public Department updateDepartment(Long id, Department department) { 32 | LOGGER.info("Department update: id={}, department={}", id, department); 33 | return repository.update(id, department); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /department-service/src/main/java/pl/piomin/services/department/resolver/DepartmentQueries.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department.resolver; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.coxautodev.graphql.tools.GraphQLQueryResolver; 11 | 12 | import pl.piomin.services.department.client.EmployeeClient; 13 | import pl.piomin.services.department.model.Department; 14 | import pl.piomin.services.department.repository.DepartmentRepository; 15 | 16 | @Component 17 | public class DepartmentQueries implements GraphQLQueryResolver { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(DepartmentQueries.class); 20 | 21 | @Autowired 22 | EmployeeClient employeeClient; 23 | @Autowired 24 | DepartmentRepository repository; 25 | 26 | public List departments() { 27 | LOGGER.info("Departments find"); 28 | return repository.findAll(); 29 | } 30 | 31 | public List departmentsByOrganization(Long organizationId) { 32 | LOGGER.info("Departments find: organizationId={}", organizationId); 33 | return repository.findByOrganization(organizationId); 34 | } 35 | 36 | public List departmentsByOrganizationWithEmployees(Long organizationId) { 37 | LOGGER.info("Departments find: organizationId={}", organizationId); 38 | List departments = repository.findByOrganization(organizationId); 39 | for (int i = 0; i < departments.size(); i++) { 40 | try { 41 | departments.get(i).setEmployees(employeeClient.findByDepartment(departments.get(i).getId())); 42 | } catch (InterruptedException e) { 43 | LOGGER.error("Error calling employee-service", e); 44 | } 45 | } 46 | return departments; 47 | } 48 | 49 | public Department department(Long id) { 50 | LOGGER.info("Department find: id={}", id); 51 | return repository.findById(id); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /department-service/src/main/java/type/CustomType.java: -------------------------------------------------------------------------------- 1 | package type; 2 | 3 | import com.apollographql.apollo.api.ScalarType; 4 | import java.lang.Class; 5 | import java.lang.Override; 6 | import java.lang.String; 7 | import javax.annotation.Generated; 8 | 9 | @Generated("Apollo GraphQL") 10 | public enum CustomType implements ScalarType { 11 | ID { 12 | @Override 13 | public String typeName() { 14 | return "ID"; 15 | } 16 | 17 | @Override 18 | public Class javaType() { 19 | return String.class; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /department-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: department-service 4 | server: 5 | port: 8091 6 | 7 | eureka: 8 | client: 9 | serviceUrl: 10 | defaultZone: http://localhost:8061/eureka/ 11 | 12 | logging: 13 | pattern: 14 | console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n" -------------------------------------------------------------------------------- /department-service/src/main/resources/graphql/department.graphqls: -------------------------------------------------------------------------------- 1 | schema { 2 | query: DepartmentQueries 3 | mutation: DepartmentMutations 4 | } 5 | 6 | type DepartmentQueries { 7 | departments: [Department] 8 | department(id: ID!): Department! 9 | departmentsByOrganization(organizationId: Int!): [Department] 10 | departmentsByOrganizationWithEmployees(organizationId: Int!): [Department] 11 | } 12 | 13 | type DepartmentMutations { 14 | newDepartment(department: DepartmentInput!): Department 15 | deleteDepartment(id: ID!) : Boolean 16 | updateDepartment(id: ID!, department: DepartmentInput!): Department 17 | } 18 | 19 | input DepartmentInput { 20 | organizationId: Int! 21 | name: String! 22 | } 23 | 24 | type Department { 25 | id: ID! 26 | organizationId: Int! 27 | name: String! 28 | employees: [Employee] 29 | } 30 | 31 | type Employee { 32 | id: ID! 33 | name: String! 34 | position: String! 35 | salary: Int! 36 | } -------------------------------------------------------------------------------- /department-service/src/test/java/pl/piomin/services/department/DepartmentAppTests.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.department; 2 | 3 | import com.apollographql.apollo3.ApolloClient; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.context.TestConfiguration; 8 | import org.springframework.boot.web.server.LocalServerPort; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import pl.piomin.services.department.clientoperation.DepartmentsByOrganizationQuery; 12 | import pl.piomin.services.department.clientoperation.DepartmentsQuery; 13 | 14 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 15 | @RunWith(SpringRunner.class) 16 | public class DepartmentAppTests { 17 | 18 | @LocalServerPort 19 | int port; 20 | 21 | @Test 22 | public void departments() { 23 | ApolloClient client = new ApolloClient.Builder() 24 | .serverUrl("http://localhost:" + port + "/graphql") 25 | .build(); 26 | client.query(new DepartmentsQuery()); 27 | } 28 | 29 | @Test 30 | public void departmentsByOrganization() { 31 | ApolloClient client = new ApolloClient.Builder() 32 | .serverUrl("http://localhost:" + port + "/graphql") 33 | .build(); 34 | client.query(new DepartmentsByOrganizationQuery()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /discovery-service/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | /.classpath 4 | /.project 5 | -------------------------------------------------------------------------------- /discovery-service/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-graphql-microservices 7 | 1.0-SNAPSHOT 8 | 9 | discovery-service 10 | 1.0-SNAPSHOT 11 | 12 | 13 | ${project.artifactId} 14 | 15 | 16 | 17 | 18 | org.springframework.cloud 19 | spring-cloud-starter-netflix-eureka-server 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-config 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-maven-plugin 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /discovery-service/src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /discovery-service/src/main/java/pl/piomin/services/discovery/DiscoveryApplication.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.discovery; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | import org.springframework.boot.builder.SpringApplicationBuilder; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class DiscoveryApplication { 10 | 11 | public static void main(String[] args) { 12 | new SpringApplicationBuilder(DiscoveryApplication.class).run(args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /discovery-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: discovery-service 4 | 5 | server: 6 | port: 8061 7 | 8 | eureka: 9 | instance: 10 | hostname: localhost 11 | client: 12 | registerWithEureka: false 13 | fetchRegistry: false 14 | serviceUrl: 15 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ -------------------------------------------------------------------------------- /employee-service/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | /.settings/ 5 | -------------------------------------------------------------------------------- /employee-service/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-graphql-microservices 7 | 1.0-SNAPSHOT 8 | 9 | employee-service 10 | 1.0-SNAPSHOT 11 | 12 | 13 | ${project.artifactId} 14 | 15 | 16 | 17 | 18 | org.springframework.cloud 19 | spring-cloud-starter-netflix-eureka-client 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-actuator 28 | 29 | 30 | com.graphql-java 31 | graphql-spring-boot-starter 32 | 5.0.2 33 | 34 | 35 | com.graphql-java 36 | graphiql-spring-boot-starter 37 | 5.0.2 38 | 39 | 40 | com.graphql-java 41 | voyager-spring-boot-starter 42 | 5.0.2 43 | 44 | 45 | com.graphql-java 46 | graphql-java 47 | 9.2 48 | 49 | 50 | com.graphql-java 51 | graphql-java-tools 52 | 5.2.3 53 | 54 | 55 | com.apollographql.apollo 56 | apollo-runtime 57 | 1.0.1 58 | 59 | 60 | com.apollographql.apollo3 61 | apollo-api-jvm 62 | 3.0.0 63 | 64 | 65 | com.apollographql.apollo3 66 | apollo-runtime-jvm 67 | 3.0.0 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-starter-test 72 | test 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-maven-plugin 81 | 82 | 83 | com.github.aoudiamoncef 84 | apollo-client-maven-plugin 85 | 5.0.0 86 | 87 | 88 | 89 | generate 90 | 91 | 92 | 93 | 94 | 95 | employees 96 | 97 | pl.piomin.services.employee.client 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /employee-service/src/main/graphql/employees/queries/employees.graphql: -------------------------------------------------------------------------------- 1 | query employees { 2 | employees { 3 | id 4 | organizationId 5 | departmentId 6 | name 7 | age 8 | position 9 | } 10 | } -------------------------------------------------------------------------------- /employee-service/src/main/graphql/employees/schema.json: -------------------------------------------------------------------------------- 1 | {"data":{"__schema":{"queryType":{"name":"EmployeeQueries"},"mutationType":{"name":"EmployeeMutations"},"subscriptionType":null,"types":[{"kind":"OBJECT","name":"EmployeeQueries","description":"","fields":[{"name":"employees","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Employee","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"employee","description":"","args":[{"name":"id","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Employee","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"employeesByOrganization","description":"","args":[{"name":"organizationId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Employee","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"employeesByDepartment","description":"","args":[{"name":"departmentId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Employee","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"ID","description":"Built-in ID","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Int","description":"Built-in Int","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EmployeeMutations","description":"","fields":[{"name":"newEmployee","description":"","args":[{"name":"employee","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"EmployeeInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Employee","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteEmployee","description":"","args":[{"name":"id","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null}],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEmployee","description":"","args":[{"name":"id","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"employee","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"EmployeeInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Employee","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Boolean","description":"Built-in Boolean","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Employee","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizationId","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"departmentId","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"age","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"position","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"salary","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"String","description":"Built-in String","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EmployeeInput","description":"","fields":null,"inputFields":[{"name":"organizationId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"departmentId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"name","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"age","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"position","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"salary","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Schema","description":"A GraphQL Introspection defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, the entry points for query, mutation, and subscription operations.","fields":[{"name":"types","description":"A list of all types supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"queryType","description":"The type that query operations will be rooted at.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mutationType","description":"If this server supports mutation, the type that mutation operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"directives","description":"'A list of all directives supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Directive"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"subscriptionType","description":"'If this server support subscription, the type that subscription operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Type","description":null,"fields":[{"name":"kind","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__TypeKind","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"fields","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Field","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"interfaces","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"possibleTypes","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"enumValues","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__EnumValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"inputFields","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"ofType","description":null,"args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__TypeKind","description":"An enum describing what kind of type a given __Type is","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SCALAR","description":"Indicates this type is a scalar.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Indicates this type is an object. `fields` and `interfaces` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Indicates this type is a union. `possibleTypes` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Indicates this type is an enum. `enumValues` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Indicates this type is an input object. `inputFields` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"LIST","description":"Indicates this type is a list. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"NON_NULL","description":"Indicates this type is a non-null. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"__Field","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__InputValue","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"defaultValue","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__EnumValue","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Directive","description":null,"fields":[{"name":"name","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locations","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__DirectiveLocation","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue"}}}},"isDeprecated":false,"deprecationReason":null},{"name":"onOperation","description":null,"args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onFragment","description":null,"args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onField","description":null,"args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":true,"deprecationReason":"Use `locations`."}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__DirectiveLocation","description":"An enum describing valid locations where a directive can be placed","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"QUERY","description":"Indicates the directive is valid on queries.","isDeprecated":false,"deprecationReason":null},{"name":"MUTATION","description":"Indicates the directive is valid on mutations.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD","description":"Indicates the directive is valid on fields.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_DEFINITION","description":"Indicates the directive is valid on fragment definitions.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_SPREAD","description":"Indicates the directive is valid on fragment spreads.","isDeprecated":false,"deprecationReason":null},{"name":"INLINE_FRAGMENT","description":"Indicates the directive is valid on inline fragments.","isDeprecated":false,"deprecationReason":null},{"name":"SCHEMA","description":"Indicates the directive is valid on a schema SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"SCALAR","description":"Indicates the directive is valid on a scalar SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Indicates the directive is valid on an object SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD_DEFINITION","description":"Indicates the directive is valid on a field SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"ARGUMENT_DEFINITION","description":"Indicates the directive is valid on a field argument SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Indicates the directive is valid on an interface SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Indicates the directive is valid on an union SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Indicates the directive is valid on an enum SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM_VALUE","description":"Indicates the directive is valid on an enum value SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Indicates the directive is valid on an input object SDL definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_FIELD_DEFINITION","description":"Indicates the directive is valid on an input object field SDL definition.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null}],"directives":[{"name":"include","description":"Directs the executor to include this field or fragment only when the `if` argument is true","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Included when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"skip","description":"Directs the executor to skip this field or fragment when the `if`'argument is true.","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Skipped when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"defer","description":"This directive allows results to be deferred during execution","locations":["FIELD"],"args":[]}]}}} -------------------------------------------------------------------------------- /employee-service/src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /employee-service/src/main/java/pl/piomin/services/employee/EmployeeApplication.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.context.annotation.Bean; 7 | 8 | import pl.piomin.services.employee.model.Employee; 9 | import pl.piomin.services.employee.repository.EmployeeRepository; 10 | 11 | @SpringBootApplication 12 | @EnableDiscoveryClient 13 | public class EmployeeApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(EmployeeApplication.class, args); 17 | } 18 | 19 | @Bean 20 | EmployeeRepository repository() { 21 | EmployeeRepository repository = new EmployeeRepository(); 22 | repository.add(new Employee(1L, 1L, "John Smith", 34, "Analyst", 3000)); 23 | repository.add(new Employee(1L, 1L, "Darren Hamilton", 37, "Manager", 5000)); 24 | repository.add(new Employee(1L, 1L, "Tom Scott", 26, "Developer", 4500)); 25 | repository.add(new Employee(1L, 2L, "Anna London", 39, "Analyst", 2500)); 26 | repository.add(new Employee(1L, 2L, "Patrick Dempsey", 27, "Developer", 5000)); 27 | repository.add(new Employee(2L, 3L, "Kevin Price", 38, "Developer", 6000)); 28 | repository.add(new Employee(2L, 3L, "Ian Scott", 34, "Developer", 4500)); 29 | repository.add(new Employee(2L, 3L, "Andrew Campton", 30, "Manager", 8000)); 30 | repository.add(new Employee(2L, 4L, "Steve Franklin", 25, "Developer", 4000)); 31 | repository.add(new Employee(2L, 4L, "Elisabeth Smith", 30, "Developer", 4000)); 32 | return repository; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /employee-service/src/main/java/pl/piomin/services/employee/model/Employee.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee.model; 2 | 3 | public class Employee { 4 | 5 | private Long id; 6 | private Long organizationId; 7 | private Long departmentId; 8 | private String name; 9 | private int age; 10 | private String position; 11 | private int salary; 12 | 13 | public Employee() { 14 | 15 | } 16 | 17 | public Employee(Long organizationId, Long departmentId, String name, int age, String position, int salary) { 18 | this.organizationId = organizationId; 19 | this.departmentId = departmentId; 20 | this.name = name; 21 | this.age = age; 22 | this.position = position; 23 | this.salary = salary; 24 | } 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public Long getOrganizationId() { 35 | return organizationId; 36 | } 37 | 38 | public void setOrganizationId(Long organizationId) { 39 | this.organizationId = organizationId; 40 | } 41 | 42 | public Long getDepartmentId() { 43 | return departmentId; 44 | } 45 | 46 | public void setDepartmentId(Long departmentId) { 47 | this.departmentId = departmentId; 48 | } 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public void setName(String name) { 55 | this.name = name; 56 | } 57 | 58 | public int getAge() { 59 | return age; 60 | } 61 | 62 | public void setAge(int age) { 63 | this.age = age; 64 | } 65 | 66 | public String getPosition() { 67 | return position; 68 | } 69 | 70 | public void setPosition(String position) { 71 | this.position = position; 72 | } 73 | 74 | public int getSalary() { 75 | return salary; 76 | } 77 | 78 | public void setSalary(int salary) { 79 | this.salary = salary; 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | return "Employee [id=" + id + ", organizationId=" + organizationId + ", departmentId=" + departmentId 85 | + ", name=" + name + ", position=" + position + "]"; 86 | } 87 | 88 | @Override 89 | public int hashCode() { 90 | final int prime = 31; 91 | int result = 1; 92 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 93 | return result; 94 | } 95 | 96 | @Override 97 | public boolean equals(Object obj) { 98 | if (this == obj) 99 | return true; 100 | if (obj == null) 101 | return false; 102 | if (getClass() != obj.getClass()) 103 | return false; 104 | Employee other = (Employee) obj; 105 | if (id == null) { 106 | if (other.id != null) 107 | return false; 108 | } else if (!id.equals(other.id)) 109 | return false; 110 | return true; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /employee-service/src/main/java/pl/piomin/services/employee/model/EmployeesQuery.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee.model; 2 | 3 | import com.apollographql.apollo.api.Operation; 4 | import com.apollographql.apollo.api.OperationName; 5 | import com.apollographql.apollo.api.Query; 6 | import com.apollographql.apollo.api.ResponseField; 7 | import com.apollographql.apollo.api.ResponseFieldMapper; 8 | import com.apollographql.apollo.api.ResponseFieldMarshaller; 9 | import com.apollographql.apollo.api.ResponseReader; 10 | import com.apollographql.apollo.api.ResponseWriter; 11 | import com.apollographql.apollo.api.internal.Utils; 12 | import java.lang.Object; 13 | import java.lang.Override; 14 | import java.lang.String; 15 | import java.util.Collections; 16 | import java.util.List; 17 | import javax.annotation.Generated; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | @Generated("Apollo GraphQL") 22 | public final class EmployeesQuery implements Query { 23 | public static final String OPERATION_DEFINITION = "query EmployeesQuery {\n" 24 | + " employees {\n" 25 | + " __typename\n" 26 | + " name\n" 27 | + " age\n" 28 | + " }\n" 29 | + "}"; 30 | 31 | public static final String OPERATION_ID = "fb9a898c6d1af441e2a005a73850290ea980e0b0575b833cb06f17dd2331ba13"; 32 | 33 | public static final String QUERY_DOCUMENT = OPERATION_DEFINITION; 34 | 35 | public static final OperationName OPERATION_NAME = new OperationName() { 36 | @Override 37 | public String name() { 38 | return "EmployeesQuery"; 39 | } 40 | }; 41 | 42 | private final Operation.Variables variables; 43 | 44 | public EmployeesQuery() { 45 | this.variables = Operation.EMPTY_VARIABLES; 46 | } 47 | 48 | @Override 49 | public String operationId() { 50 | return OPERATION_ID; 51 | } 52 | 53 | @Override 54 | public String queryDocument() { 55 | return QUERY_DOCUMENT; 56 | } 57 | 58 | @Override 59 | public EmployeesQuery.Data wrapData(EmployeesQuery.Data data) { 60 | return data; 61 | } 62 | 63 | @Override 64 | public Operation.Variables variables() { 65 | return variables; 66 | } 67 | 68 | @Override 69 | public ResponseFieldMapper responseFieldMapper() { 70 | return new Data.Mapper(); 71 | } 72 | 73 | public static Builder builder() { 74 | return new Builder(); 75 | } 76 | 77 | @Override 78 | public OperationName name() { 79 | return OPERATION_NAME; 80 | } 81 | 82 | public static final class Builder { 83 | Builder() { 84 | } 85 | 86 | public EmployeesQuery build() { 87 | return new EmployeesQuery(); 88 | } 89 | } 90 | 91 | public static class Data implements Operation.Data { 92 | static final ResponseField[] $responseFields = { 93 | ResponseField.forList("employees", "employees", null, true, Collections.emptyList()) 94 | }; 95 | 96 | final @Nullable List employees; 97 | 98 | private volatile String $toString; 99 | 100 | private volatile int $hashCode; 101 | 102 | private volatile boolean $hashCodeMemoized; 103 | 104 | public Data(@Nullable List employees) { 105 | this.employees = employees; 106 | } 107 | 108 | public @Nullable List employees() { 109 | return this.employees; 110 | } 111 | 112 | public ResponseFieldMarshaller marshaller() { 113 | return new ResponseFieldMarshaller() { 114 | @Override 115 | public void marshal(ResponseWriter writer) { 116 | writer.writeList($responseFields[0], employees, new ResponseWriter.ListWriter() { 117 | 118 | @Override 119 | public void write(@Nullable List list, @NotNull ResponseWriter.ListItemWriter listItemWriter) { 120 | listItemWriter.writeList(list, this); 121 | } 122 | 123 | // public void write(Object value, ResponseWriter.ListItemWriter listItemWriter) { 124 | // listItemWriter.writeObject(((Employee) value).marshaller()); 125 | // } 126 | }); 127 | } 128 | }; 129 | } 130 | 131 | @Override 132 | public String toString() { 133 | if ($toString == null) { 134 | $toString = "Data{" 135 | + "employees=" + employees 136 | + "}"; 137 | } 138 | return $toString; 139 | } 140 | 141 | @Override 142 | public boolean equals(Object o) { 143 | if (o == this) { 144 | return true; 145 | } 146 | if (o instanceof Data) { 147 | Data that = (Data) o; 148 | return ((this.employees == null) ? (that.employees == null) : this.employees.equals(that.employees)); 149 | } 150 | return false; 151 | } 152 | 153 | @Override 154 | public int hashCode() { 155 | if (!$hashCodeMemoized) { 156 | int h = 1; 157 | h *= 1000003; 158 | h ^= (employees == null) ? 0 : employees.hashCode(); 159 | $hashCode = h; 160 | $hashCodeMemoized = true; 161 | } 162 | return $hashCode; 163 | } 164 | 165 | public static final class Mapper implements ResponseFieldMapper { 166 | final Employee.Mapper employeeFieldMapper = new Employee.Mapper(); 167 | 168 | @Override 169 | public Data map(ResponseReader reader) { 170 | final List employees = reader.readList($responseFields[0], new ResponseReader.ListReader() { 171 | @Override 172 | public Employee read(ResponseReader.ListItemReader listItemReader) { 173 | return listItemReader.readObject(new ResponseReader.ObjectReader() { 174 | @Override 175 | public Employee read(ResponseReader reader) { 176 | return employeeFieldMapper.map(reader); 177 | } 178 | }); 179 | } 180 | }); 181 | return new Data(employees); 182 | } 183 | } 184 | } 185 | 186 | public static class Employee { 187 | static final ResponseField[] $responseFields = { 188 | ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), 189 | ResponseField.forString("name", "name", null, false, Collections.emptyList()), 190 | ResponseField.forInt("age", "age", null, false, Collections.emptyList()) 191 | }; 192 | 193 | final @NotNull String __typename; 194 | 195 | final @NotNull String name; 196 | 197 | final int age; 198 | 199 | private volatile String $toString; 200 | 201 | private volatile int $hashCode; 202 | 203 | private volatile boolean $hashCodeMemoized; 204 | 205 | public Employee(@NotNull String __typename, @NotNull String name, int age) { 206 | this.__typename = Utils.checkNotNull(__typename, "__typename == null"); 207 | this.name = Utils.checkNotNull(name, "name == null"); 208 | this.age = age; 209 | } 210 | 211 | public @NotNull String __typename() { 212 | return this.__typename; 213 | } 214 | 215 | public @NotNull String name() { 216 | return this.name; 217 | } 218 | 219 | public int age() { 220 | return this.age; 221 | } 222 | 223 | public ResponseFieldMarshaller marshaller() { 224 | return new ResponseFieldMarshaller() { 225 | @Override 226 | public void marshal(ResponseWriter writer) { 227 | writer.writeString($responseFields[0], __typename); 228 | writer.writeString($responseFields[1], name); 229 | writer.writeInt($responseFields[2], age); 230 | } 231 | }; 232 | } 233 | 234 | @Override 235 | public String toString() { 236 | if ($toString == null) { 237 | $toString = "Employee{" 238 | + "__typename=" + __typename + ", " 239 | + "name=" + name + ", " 240 | + "age=" + age 241 | + "}"; 242 | } 243 | return $toString; 244 | } 245 | 246 | @Override 247 | public boolean equals(Object o) { 248 | if (o == this) { 249 | return true; 250 | } 251 | if (o instanceof Employee) { 252 | Employee that = (Employee) o; 253 | return this.__typename.equals(that.__typename) 254 | && this.name.equals(that.name) 255 | && this.age == that.age; 256 | } 257 | return false; 258 | } 259 | 260 | @Override 261 | public int hashCode() { 262 | if (!$hashCodeMemoized) { 263 | int h = 1; 264 | h *= 1000003; 265 | h ^= __typename.hashCode(); 266 | h *= 1000003; 267 | h ^= name.hashCode(); 268 | h *= 1000003; 269 | h ^= age; 270 | $hashCode = h; 271 | $hashCodeMemoized = true; 272 | } 273 | return $hashCode; 274 | } 275 | 276 | public static final class Mapper implements ResponseFieldMapper { 277 | @Override 278 | public Employee map(ResponseReader reader) { 279 | final String __typename = reader.readString($responseFields[0]); 280 | final String name = reader.readString($responseFields[1]); 281 | final int age = reader.readInt($responseFields[2]); 282 | return new Employee(__typename, name, age); 283 | } 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /employee-service/src/main/java/pl/piomin/services/employee/repository/EmployeeRepository.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee.repository; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.stream.Collectors; 7 | 8 | import pl.piomin.services.employee.model.Employee; 9 | 10 | public class EmployeeRepository { 11 | 12 | private List employees = new ArrayList<>(); 13 | 14 | public Employee add(Employee employee) { 15 | employee.setId((long) (employees.size()+1)); 16 | employees.add(employee); 17 | return employee; 18 | } 19 | 20 | public Employee findById(Long id) { 21 | Optional employee = employees.stream().filter(a -> a.getId().equals(id)).findFirst(); 22 | if (employee.isPresent()) 23 | return employee.get(); 24 | else 25 | return null; 26 | } 27 | 28 | public List findAll() { 29 | return employees; 30 | } 31 | 32 | public List findByDepartment(Long departmentId) { 33 | return employees.stream().filter(a -> a.getDepartmentId().equals(departmentId)).collect(Collectors.toList()); 34 | } 35 | 36 | public List findByOrganization(Long organizationId) { 37 | return employees.stream().filter(a -> a.getOrganizationId().equals(organizationId)).collect(Collectors.toList()); 38 | } 39 | 40 | public boolean delete(Long id) { 41 | return employees.removeIf(it -> it.getId() == id.longValue()); 42 | } 43 | 44 | public Employee update(Long id, Employee employee) { 45 | employee.setId(id); 46 | int index = employees.indexOf(employee); 47 | employees.set(index, employee); 48 | return employee; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /employee-service/src/main/java/pl/piomin/services/employee/resolver/EmployeeMutations.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee.resolver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import com.coxautodev.graphql.tools.GraphQLMutationResolver; 9 | 10 | import pl.piomin.services.employee.model.Employee; 11 | import pl.piomin.services.employee.repository.EmployeeRepository; 12 | 13 | @Component 14 | public class EmployeeMutations implements GraphQLMutationResolver { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeQueries.class); 17 | 18 | @Autowired 19 | EmployeeRepository repository; 20 | 21 | public Employee newEmployee(Employee employee) { 22 | LOGGER.info("Employee add: employee={}", employee); 23 | return repository.add(employee); 24 | } 25 | 26 | public boolean deleteEmployee(Long id) { 27 | LOGGER.info("Employee delete: id={}", id); 28 | return repository.delete(id); 29 | } 30 | 31 | public Employee updateEmployee(Long id, Employee employee) { 32 | LOGGER.info("Employee update: id={}, employee={}", id, employee); 33 | return repository.update(id, employee); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /employee-service/src/main/java/pl/piomin/services/employee/resolver/EmployeeQueries.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee.resolver; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.coxautodev.graphql.tools.GraphQLQueryResolver; 11 | 12 | import pl.piomin.services.employee.model.Employee; 13 | import pl.piomin.services.employee.repository.EmployeeRepository; 14 | 15 | @Component 16 | public class EmployeeQueries implements GraphQLQueryResolver { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeQueries.class); 19 | 20 | @Autowired 21 | EmployeeRepository repository; 22 | 23 | public List employees() { 24 | LOGGER.info("Employees find"); 25 | return repository.findAll(); 26 | } 27 | 28 | public List employeesByOrganization(Long organizationId) { 29 | LOGGER.info("Employees find: organizationId={}", organizationId); 30 | return repository.findByOrganization(organizationId); 31 | } 32 | 33 | public List employeesByDepartment(Long departmentId) { 34 | LOGGER.info("Employees find: departmentId={}", departmentId); 35 | return repository.findByDepartment(departmentId); 36 | } 37 | 38 | public Employee employee(Long id) { 39 | LOGGER.info("Employee find: id={}", id); 40 | return repository.findById(id); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /employee-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: employee-service 4 | 5 | server: 6 | port: 8090 7 | 8 | eureka: 9 | client: 10 | serviceUrl: 11 | defaultZone: http://localhost:8061/eureka/ 12 | 13 | logging: 14 | pattern: 15 | console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n" -------------------------------------------------------------------------------- /employee-service/src/main/resources/graphql/employee.graphqls: -------------------------------------------------------------------------------- 1 | schema { 2 | query: EmployeeQueries 3 | mutation: EmployeeMutations 4 | } 5 | 6 | type EmployeeQueries { 7 | employees: [Employee] 8 | employee(id: ID!): Employee! 9 | employeesByOrganization(organizationId: Int!): [Employee] 10 | employeesByDepartment(departmentId: Int!): [Employee] 11 | } 12 | 13 | type EmployeeMutations { 14 | newEmployee(employee: EmployeeInput!): Employee 15 | deleteEmployee(id: ID!) : Boolean 16 | updateEmployee(id: ID!, employee: EmployeeInput!): Employee 17 | } 18 | 19 | input EmployeeInput { 20 | organizationId: Int! 21 | departmentId: Int! 22 | name: String! 23 | age: Int! 24 | position: String! 25 | salary: Int! 26 | } 27 | 28 | type Employee { 29 | id: ID! 30 | organizationId: Int! 31 | departmentId: Int! 32 | name: String! 33 | age: Int! 34 | position: String! 35 | salary: Int! 36 | } -------------------------------------------------------------------------------- /employee-service/src/test/java/pl/piomin/services/employee/EmployeeApiTest.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.employee; 2 | 3 | import com.apollographql.apollo.ApolloCall.Callback; 4 | import com.apollographql.apollo.ApolloClient; 5 | import com.apollographql.apollo.api.Response; 6 | import com.apollographql.apollo.exception.ApolloException; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.boot.web.server.LocalServerPort; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | import pl.piomin.services.employee.model.EmployeesQuery; 15 | import pl.piomin.services.employee.model.EmployeesQuery.Data; 16 | 17 | import java.util.concurrent.CountDownLatch; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 21 | @RunWith(SpringRunner.class) 22 | public class EmployeeApiTest { 23 | 24 | @LocalServerPort 25 | int port; 26 | 27 | private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeApiTest.class); 28 | 29 | private CountDownLatch lock = new CountDownLatch(1); 30 | 31 | @Test 32 | public void testClient() throws InterruptedException { 33 | ApolloClient client = ApolloClient.builder().serverUrl("http://localhost:" + port + "/graphql").build(); 34 | client.query(EmployeesQuery.builder().build()).enqueue(new Callback() { 35 | 36 | @Override 37 | public void onFailure(ApolloException arg0) { 38 | LOGGER.error("Error", arg0); 39 | lock.countDown(); 40 | } 41 | 42 | @Override 43 | public void onResponse(Response res) { 44 | LOGGER.info("Res: {}", res.data().employees()); 45 | lock.countDown(); 46 | } 47 | }); 48 | lock.await(10000, TimeUnit.MILLISECONDS); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /organization-service/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | /.settings/ 5 | /build/ 6 | -------------------------------------------------------------------------------- /organization-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | pl.piomin.services 5 | sample-graphql-microservices 6 | 1.0-SNAPSHOT 7 | 8 | organization-service 9 | 1.0-SNAPSHOT 10 | 11 | 12 | ${project.artifactId} 13 | 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-netflix-eureka-client 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-config 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starter-openfeign 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-openfeign-core 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-actuator 39 | 40 | 41 | com.graphql-java 42 | graphql-spring-boot-starter 43 | 5.0.2 44 | 45 | 46 | com.graphql-java 47 | graphiql-spring-boot-starter 48 | 5.0.2 49 | 50 | 51 | com.graphql-java 52 | voyager-spring-boot-starter 53 | 5.0.2 54 | 55 | 56 | com.graphql-java 57 | graphql-java 58 | 9.2 59 | 60 | 61 | com.graphql-java 62 | graphql-java-tools 63 | 5.2.3 64 | 65 | 66 | org.springframework.cloud 67 | spring-cloud-starter-sleuth 68 | 69 | 70 | com.apollographql.apollo 71 | apollo-runtime 72 | 1.0.1 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-maven-plugin 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /organization-service/src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/OrganizationApplication.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.openfeign.EnableFeignClients; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | import pl.piomin.services.organization.model.Organization; 10 | import pl.piomin.services.organization.repository.OrganizationRepository; 11 | 12 | @SpringBootApplication 13 | @EnableDiscoveryClient 14 | @EnableFeignClients 15 | public class OrganizationApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(OrganizationApplication.class, args); 19 | } 20 | 21 | @Bean 22 | OrganizationRepository repository() { 23 | OrganizationRepository repository = new OrganizationRepository(); 24 | repository.add(new Organization("Microsoft", "Redmond, Washington, USA")); 25 | repository.add(new Organization("Oracle", "Redwood City, California, USA")); 26 | return repository; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/client/DepartmentClient.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.client; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.cloud.openfeign.FeignClient; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | 9 | import pl.piomin.services.organization.model.Department; 10 | 11 | @FeignClient(name = "department-service") 12 | public interface DepartmentClient { 13 | 14 | @GetMapping("/organization/{organizationId}") 15 | public List findByOrganization(@PathVariable("organizationId") Long organizationId); 16 | 17 | @GetMapping("/organization/{organizationId}/with-employees") 18 | public List findByOrganizationWithEmployees(@PathVariable("organizationId") Long organizationId); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/client/EmployeeClient.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | import java.util.concurrent.CountDownLatch; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.stream.Collectors; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | 15 | import com.apollographql.apollo.ApolloCall.Callback; 16 | import com.apollographql.apollo.ApolloClient; 17 | import com.apollographql.apollo.api.Response; 18 | import com.apollographql.apollo.exception.ApolloException; 19 | import com.netflix.appinfo.InstanceInfo; 20 | import com.netflix.discovery.EurekaClient; 21 | import com.netflix.discovery.shared.Application; 22 | 23 | import pl.piomin.services.organization.model.Employee; 24 | 25 | @Component 26 | public class EmployeeClient { 27 | 28 | private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeClient.class); 29 | private static final int TIMEOUT = 5000; 30 | private static final String SERVICE_NAME = "EMPLOYEE-SERVICE"; 31 | private static final String SERVER_URL = "http://localhost:%d/graphql"; 32 | 33 | Random r = new Random(); 34 | 35 | @Autowired 36 | private EurekaClient discoveryClient; 37 | 38 | public List findByDepartment(Long departmentId) throws InterruptedException { 39 | List employees = new ArrayList<>(); 40 | Application app = discoveryClient.getApplication(SERVICE_NAME); 41 | InstanceInfo ii = app.getInstances().get(r.nextInt(app.size())); 42 | ApolloClient client = ApolloClient.builder().serverUrl(String.format(SERVER_URL, ii.getPort())).build(); 43 | CountDownLatch lock = new CountDownLatch(1); 44 | client.query(EmployeesByDepartmentQuery.builder().departmentId(departmentId.intValue()).build()).enqueue(new Callback() { 45 | 46 | @Override 47 | public void onFailure(ApolloException ex) { 48 | LOGGER.info("Err: {}", ex); 49 | lock.countDown(); 50 | } 51 | 52 | @Override 53 | public void onResponse(Response res) { 54 | LOGGER.info("Res: {}", res); 55 | employees.addAll(res.data().employeesByDepartment().stream().map(emp -> new Employee(Long.valueOf(emp.id()), emp.name(), null)).collect(Collectors.toList())); 56 | lock.countDown(); 57 | } 58 | 59 | }); 60 | lock.await(TIMEOUT, TimeUnit.MILLISECONDS); 61 | return employees; 62 | } 63 | 64 | public List findByOrganization(Long organizationId) throws InterruptedException { 65 | List employees = new ArrayList<>(); 66 | Application app = discoveryClient.getApplication(SERVICE_NAME); 67 | InstanceInfo ii = app.getInstances().get(r.nextInt(app.size())); 68 | ApolloClient client = ApolloClient.builder().serverUrl(String.format(SERVER_URL, ii.getPort())).build(); 69 | CountDownLatch lock = new CountDownLatch(1); 70 | client.query(EmployeesByOrganizationQuery.builder().organizationId(organizationId.intValue()).build()).enqueue(new Callback() { 71 | 72 | @Override 73 | public void onFailure(ApolloException ex) { 74 | LOGGER.info("Err: {}", ex); 75 | lock.countDown(); 76 | } 77 | 78 | @Override 79 | public void onResponse(Response res) { 80 | LOGGER.info("Res: {}", res); 81 | employees.addAll(res.data().employeesByOrganization().stream().map(emp -> new Employee(Long.valueOf(emp.id()), emp.name(), null)).collect(Collectors.toList())); 82 | lock.countDown(); 83 | } 84 | 85 | }); 86 | lock.await(TIMEOUT, TimeUnit.MILLISECONDS); 87 | return employees; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/client/EmployeesByDepartmentQuery.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.client; 2 | 3 | import com.apollographql.apollo.api.InputFieldMarshaller; 4 | import com.apollographql.apollo.api.InputFieldWriter; 5 | import com.apollographql.apollo.api.Operation; 6 | import com.apollographql.apollo.api.OperationName; 7 | import com.apollographql.apollo.api.Query; 8 | import com.apollographql.apollo.api.ResponseField; 9 | import com.apollographql.apollo.api.ResponseFieldMapper; 10 | import com.apollographql.apollo.api.ResponseFieldMarshaller; 11 | import com.apollographql.apollo.api.ResponseReader; 12 | import com.apollographql.apollo.api.ResponseWriter; 13 | import com.apollographql.apollo.api.internal.UnmodifiableMapBuilder; 14 | import com.apollographql.apollo.api.internal.Utils; 15 | import java.io.IOException; 16 | import java.lang.Object; 17 | import java.lang.Override; 18 | import java.lang.String; 19 | import java.util.Collections; 20 | import java.util.LinkedHashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import javax.annotation.Generated; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | import type.CustomType; 27 | 28 | @Generated("Apollo GraphQL") 29 | public final class EmployeesByDepartmentQuery implements Query { 30 | public static final String OPERATION_DEFINITION = "query EmployeesByDepartment($departmentId: Int!) {\n" 31 | + " employeesByDepartment(departmentId: $departmentId) {\n" 32 | + " __typename\n" 33 | + " id\n" 34 | + " name\n" 35 | + " }\n" 36 | + "}"; 37 | 38 | public static final String OPERATION_ID = "aed2f7d05a2e1d2b5dc61fb2a9678bd08132b0cef2cc769c6d3c34e090f00da0"; 39 | 40 | public static final String QUERY_DOCUMENT = OPERATION_DEFINITION; 41 | 42 | public static final OperationName OPERATION_NAME = new OperationName() { 43 | @Override 44 | public String name() { 45 | return "EmployeesByDepartment"; 46 | } 47 | }; 48 | 49 | private final EmployeesByDepartmentQuery.Variables variables; 50 | 51 | public EmployeesByDepartmentQuery(int departmentId) { 52 | variables = new EmployeesByDepartmentQuery.Variables(departmentId); 53 | } 54 | 55 | @Override 56 | public String operationId() { 57 | return OPERATION_ID; 58 | } 59 | 60 | @Override 61 | public String queryDocument() { 62 | return QUERY_DOCUMENT; 63 | } 64 | 65 | @Override 66 | public EmployeesByDepartmentQuery.Data wrapData(EmployeesByDepartmentQuery.Data data) { 67 | return data; 68 | } 69 | 70 | @Override 71 | public EmployeesByDepartmentQuery.Variables variables() { 72 | return variables; 73 | } 74 | 75 | @Override 76 | public ResponseFieldMapper responseFieldMapper() { 77 | return new Data.Mapper(); 78 | } 79 | 80 | public static Builder builder() { 81 | return new Builder(); 82 | } 83 | 84 | @Override 85 | public OperationName name() { 86 | return OPERATION_NAME; 87 | } 88 | 89 | public static final class Builder { 90 | private int departmentId; 91 | 92 | Builder() { 93 | } 94 | 95 | public Builder departmentId(int departmentId) { 96 | this.departmentId = departmentId; 97 | return this; 98 | } 99 | 100 | public EmployeesByDepartmentQuery build() { 101 | return new EmployeesByDepartmentQuery(departmentId); 102 | } 103 | } 104 | 105 | public static final class Variables extends Operation.Variables { 106 | private final int departmentId; 107 | 108 | private final transient Map valueMap = new LinkedHashMap<>(); 109 | 110 | Variables(int departmentId) { 111 | this.departmentId = departmentId; 112 | this.valueMap.put("departmentId", departmentId); 113 | } 114 | 115 | public int departmentId() { 116 | return departmentId; 117 | } 118 | 119 | @Override 120 | public Map valueMap() { 121 | return Collections.unmodifiableMap(valueMap); 122 | } 123 | 124 | @Override 125 | public InputFieldMarshaller marshaller() { 126 | return new InputFieldMarshaller() { 127 | @Override 128 | public void marshal(InputFieldWriter writer) throws IOException { 129 | writer.writeInt("departmentId", departmentId); 130 | } 131 | }; 132 | } 133 | } 134 | 135 | public static class Data implements Operation.Data { 136 | static final ResponseField[] $responseFields = { 137 | ResponseField.forList("employeesByDepartment", "employeesByDepartment", new UnmodifiableMapBuilder(1) 138 | .put("departmentId", new UnmodifiableMapBuilder(2) 139 | .put("kind", "Variable") 140 | .put("variableName", "departmentId") 141 | .build()) 142 | .build(), true, Collections.emptyList()) 143 | }; 144 | 145 | final @Nullable List employeesByDepartment; 146 | 147 | private volatile String $toString; 148 | 149 | private volatile int $hashCode; 150 | 151 | private volatile boolean $hashCodeMemoized; 152 | 153 | public Data(@Nullable List employeesByDepartment) { 154 | this.employeesByDepartment = employeesByDepartment; 155 | } 156 | 157 | public @Nullable List employeesByDepartment() { 158 | return this.employeesByDepartment; 159 | } 160 | 161 | public ResponseFieldMarshaller marshaller() { 162 | return new ResponseFieldMarshaller() { 163 | @Override 164 | public void marshal(ResponseWriter writer) { 165 | writer.writeList($responseFields[0], employeesByDepartment, new ResponseWriter.ListWriter() { 166 | @Override 167 | public void write(@Nullable List list, @NotNull ResponseWriter.ListItemWriter listItemWriter) { 168 | listItemWriter.writeList(list, this); 169 | } 170 | 171 | // @Override 172 | // public void write(Object value, ResponseWriter.ListItemWriter listItemWriter) { 173 | // listItemWriter.writeObject(((EmployeesByDepartment) value).marshaller()); 174 | // } 175 | }); 176 | } 177 | }; 178 | } 179 | 180 | @Override 181 | public String toString() { 182 | if ($toString == null) { 183 | $toString = "Data{" 184 | + "employeesByDepartment=" + employeesByDepartment 185 | + "}"; 186 | } 187 | return $toString; 188 | } 189 | 190 | @Override 191 | public boolean equals(Object o) { 192 | if (o == this) { 193 | return true; 194 | } 195 | if (o instanceof Data) { 196 | Data that = (Data) o; 197 | return ((this.employeesByDepartment == null) ? (that.employeesByDepartment == null) : this.employeesByDepartment.equals(that.employeesByDepartment)); 198 | } 199 | return false; 200 | } 201 | 202 | @Override 203 | public int hashCode() { 204 | if (!$hashCodeMemoized) { 205 | int h = 1; 206 | h *= 1000003; 207 | h ^= (employeesByDepartment == null) ? 0 : employeesByDepartment.hashCode(); 208 | $hashCode = h; 209 | $hashCodeMemoized = true; 210 | } 211 | return $hashCode; 212 | } 213 | 214 | public static final class Mapper implements ResponseFieldMapper { 215 | final EmployeesByDepartment.Mapper employeesByDepartmentFieldMapper = new EmployeesByDepartment.Mapper(); 216 | 217 | @Override 218 | public Data map(ResponseReader reader) { 219 | final List employeesByDepartment = reader.readList($responseFields[0], new ResponseReader.ListReader() { 220 | @Override 221 | public EmployeesByDepartment read(ResponseReader.ListItemReader listItemReader) { 222 | return listItemReader.readObject(new ResponseReader.ObjectReader() { 223 | @Override 224 | public EmployeesByDepartment read(ResponseReader reader) { 225 | return employeesByDepartmentFieldMapper.map(reader); 226 | } 227 | }); 228 | } 229 | }); 230 | return new Data(employeesByDepartment); 231 | } 232 | } 233 | } 234 | 235 | public static class EmployeesByDepartment { 236 | static final ResponseField[] $responseFields = { 237 | ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), 238 | ResponseField.forCustomType("id", "id", null, false, CustomType.ID, Collections.emptyList()), 239 | ResponseField.forString("name", "name", null, false, Collections.emptyList()) 240 | }; 241 | 242 | final @NotNull String __typename; 243 | 244 | final @NotNull String id; 245 | 246 | final @NotNull String name; 247 | 248 | private volatile String $toString; 249 | 250 | private volatile int $hashCode; 251 | 252 | private volatile boolean $hashCodeMemoized; 253 | 254 | public EmployeesByDepartment(@NotNull String __typename, @NotNull String id, 255 | @NotNull String name) { 256 | this.__typename = Utils.checkNotNull(__typename, "__typename == null"); 257 | this.id = Utils.checkNotNull(id, "id == null"); 258 | this.name = Utils.checkNotNull(name, "name == null"); 259 | } 260 | 261 | public @NotNull String __typename() { 262 | return this.__typename; 263 | } 264 | 265 | public @NotNull String id() { 266 | return this.id; 267 | } 268 | 269 | public @NotNull String name() { 270 | return this.name; 271 | } 272 | 273 | public ResponseFieldMarshaller marshaller() { 274 | return new ResponseFieldMarshaller() { 275 | @Override 276 | public void marshal(ResponseWriter writer) { 277 | writer.writeString($responseFields[0], __typename); 278 | writer.writeCustom((ResponseField.CustomTypeField) $responseFields[1], id); 279 | writer.writeString($responseFields[2], name); 280 | } 281 | }; 282 | } 283 | 284 | @Override 285 | public String toString() { 286 | if ($toString == null) { 287 | $toString = "EmployeesByDepartment{" 288 | + "__typename=" + __typename + ", " 289 | + "id=" + id + ", " 290 | + "name=" + name 291 | + "}"; 292 | } 293 | return $toString; 294 | } 295 | 296 | @Override 297 | public boolean equals(Object o) { 298 | if (o == this) { 299 | return true; 300 | } 301 | if (o instanceof EmployeesByDepartment) { 302 | EmployeesByDepartment that = (EmployeesByDepartment) o; 303 | return this.__typename.equals(that.__typename) 304 | && this.id.equals(that.id) 305 | && this.name.equals(that.name); 306 | } 307 | return false; 308 | } 309 | 310 | @Override 311 | public int hashCode() { 312 | if (!$hashCodeMemoized) { 313 | int h = 1; 314 | h *= 1000003; 315 | h ^= __typename.hashCode(); 316 | h *= 1000003; 317 | h ^= id.hashCode(); 318 | h *= 1000003; 319 | h ^= name.hashCode(); 320 | $hashCode = h; 321 | $hashCodeMemoized = true; 322 | } 323 | return $hashCode; 324 | } 325 | 326 | public static final class Mapper implements ResponseFieldMapper { 327 | @Override 328 | public EmployeesByDepartment map(ResponseReader reader) { 329 | final String __typename = reader.readString($responseFields[0]); 330 | final String id = reader.readCustomType((ResponseField.CustomTypeField) $responseFields[1]); 331 | final String name = reader.readString($responseFields[2]); 332 | return new EmployeesByDepartment(__typename, id, name); 333 | } 334 | } 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/client/EmployeesByOrganizationQuery.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.client; 2 | 3 | import com.apollographql.apollo.api.InputFieldMarshaller; 4 | import com.apollographql.apollo.api.InputFieldWriter; 5 | import com.apollographql.apollo.api.Operation; 6 | import com.apollographql.apollo.api.OperationName; 7 | import com.apollographql.apollo.api.Query; 8 | import com.apollographql.apollo.api.ResponseField; 9 | import com.apollographql.apollo.api.ResponseFieldMapper; 10 | import com.apollographql.apollo.api.ResponseFieldMarshaller; 11 | import com.apollographql.apollo.api.ResponseReader; 12 | import com.apollographql.apollo.api.ResponseWriter; 13 | import com.apollographql.apollo.api.internal.UnmodifiableMapBuilder; 14 | import com.apollographql.apollo.api.internal.Utils; 15 | import java.io.IOException; 16 | import java.lang.Object; 17 | import java.lang.Override; 18 | import java.lang.String; 19 | import java.util.Collections; 20 | import java.util.LinkedHashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import javax.annotation.Generated; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | import type.CustomType; 27 | 28 | @Generated("Apollo GraphQL") 29 | public final class EmployeesByOrganizationQuery implements Query { 30 | public static final String OPERATION_DEFINITION = "query EmployeesByOrganization($organizationId: Int!) {\n" 31 | + " employeesByOrganization(organizationId: $organizationId) {\n" 32 | + " __typename\n" 33 | + " id\n" 34 | + " name\n" 35 | + " }\n" 36 | + "}"; 37 | 38 | public static final String OPERATION_ID = "e22dfe6efe53b9e0faa27de63eda414ee6fa8fa9a8d0cc4269ef7bce117705a9"; 39 | 40 | public static final String QUERY_DOCUMENT = OPERATION_DEFINITION; 41 | 42 | public static final OperationName OPERATION_NAME = new OperationName() { 43 | @Override 44 | public String name() { 45 | return "EmployeesByOrganization"; 46 | } 47 | }; 48 | 49 | private final EmployeesByOrganizationQuery.Variables variables; 50 | 51 | public EmployeesByOrganizationQuery(int organizationId) { 52 | variables = new EmployeesByOrganizationQuery.Variables(organizationId); 53 | } 54 | 55 | @Override 56 | public String operationId() { 57 | return OPERATION_ID; 58 | } 59 | 60 | @Override 61 | public String queryDocument() { 62 | return QUERY_DOCUMENT; 63 | } 64 | 65 | @Override 66 | public EmployeesByOrganizationQuery.Data wrapData(EmployeesByOrganizationQuery.Data data) { 67 | return data; 68 | } 69 | 70 | @Override 71 | public EmployeesByOrganizationQuery.Variables variables() { 72 | return variables; 73 | } 74 | 75 | @Override 76 | public ResponseFieldMapper responseFieldMapper() { 77 | return new Data.Mapper(); 78 | } 79 | 80 | public static Builder builder() { 81 | return new Builder(); 82 | } 83 | 84 | @Override 85 | public OperationName name() { 86 | return OPERATION_NAME; 87 | } 88 | 89 | public static final class Builder { 90 | private int organizationId; 91 | 92 | Builder() { 93 | } 94 | 95 | public Builder organizationId(int organizationId) { 96 | this.organizationId = organizationId; 97 | return this; 98 | } 99 | 100 | public EmployeesByOrganizationQuery build() { 101 | return new EmployeesByOrganizationQuery(organizationId); 102 | } 103 | } 104 | 105 | public static final class Variables extends Operation.Variables { 106 | private final int organizationId; 107 | 108 | private final transient Map valueMap = new LinkedHashMap<>(); 109 | 110 | Variables(int organizationId) { 111 | this.organizationId = organizationId; 112 | this.valueMap.put("organizationId", organizationId); 113 | } 114 | 115 | public int organizationId() { 116 | return organizationId; 117 | } 118 | 119 | @Override 120 | public Map valueMap() { 121 | return Collections.unmodifiableMap(valueMap); 122 | } 123 | 124 | @Override 125 | public InputFieldMarshaller marshaller() { 126 | return new InputFieldMarshaller() { 127 | @Override 128 | public void marshal(InputFieldWriter writer) throws IOException { 129 | writer.writeInt("organizationId", organizationId); 130 | } 131 | }; 132 | } 133 | } 134 | 135 | public static class Data implements Operation.Data { 136 | static final ResponseField[] $responseFields = { 137 | ResponseField.forList("employeesByOrganization", "employeesByOrganization", new UnmodifiableMapBuilder(1) 138 | .put("organizationId", new UnmodifiableMapBuilder(2) 139 | .put("kind", "Variable") 140 | .put("variableName", "organizationId") 141 | .build()) 142 | .build(), true, Collections.emptyList()) 143 | }; 144 | 145 | final @Nullable List employeesByOrganization; 146 | 147 | private volatile String $toString; 148 | 149 | private volatile int $hashCode; 150 | 151 | private volatile boolean $hashCodeMemoized; 152 | 153 | public Data(@Nullable List employeesByOrganization) { 154 | this.employeesByOrganization = employeesByOrganization; 155 | } 156 | 157 | public @Nullable List employeesByOrganization() { 158 | return this.employeesByOrganization; 159 | } 160 | 161 | public ResponseFieldMarshaller marshaller() { 162 | return new ResponseFieldMarshaller() { 163 | @Override 164 | public void marshal(ResponseWriter writer) { 165 | writer.writeList($responseFields[0], employeesByOrganization, new ResponseWriter.ListWriter() { 166 | @Override 167 | public void write(@Nullable List list, @NotNull ResponseWriter.ListItemWriter listItemWriter) { 168 | listItemWriter.writeList(list, this); 169 | } 170 | 171 | // @Override 172 | // public void write(Object value, ResponseWriter.ListItemWriter listItemWriter) { 173 | // listItemWriter.writeObject(((EmployeesByOrganization) value).marshaller()); 174 | // } 175 | }); 176 | } 177 | }; 178 | } 179 | 180 | @Override 181 | public String toString() { 182 | if ($toString == null) { 183 | $toString = "Data{" 184 | + "employeesByOrganization=" + employeesByOrganization 185 | + "}"; 186 | } 187 | return $toString; 188 | } 189 | 190 | @Override 191 | public boolean equals(Object o) { 192 | if (o == this) { 193 | return true; 194 | } 195 | if (o instanceof Data) { 196 | Data that = (Data) o; 197 | return ((this.employeesByOrganization == null) ? (that.employeesByOrganization == null) : this.employeesByOrganization.equals(that.employeesByOrganization)); 198 | } 199 | return false; 200 | } 201 | 202 | @Override 203 | public int hashCode() { 204 | if (!$hashCodeMemoized) { 205 | int h = 1; 206 | h *= 1000003; 207 | h ^= (employeesByOrganization == null) ? 0 : employeesByOrganization.hashCode(); 208 | $hashCode = h; 209 | $hashCodeMemoized = true; 210 | } 211 | return $hashCode; 212 | } 213 | 214 | public static final class Mapper implements ResponseFieldMapper { 215 | final EmployeesByOrganization.Mapper employeesByOrganizationFieldMapper = new EmployeesByOrganization.Mapper(); 216 | 217 | @Override 218 | public Data map(ResponseReader reader) { 219 | final List employeesByOrganization = reader.readList($responseFields[0], new ResponseReader.ListReader() { 220 | @Override 221 | public EmployeesByOrganization read(ResponseReader.ListItemReader listItemReader) { 222 | return listItemReader.readObject(new ResponseReader.ObjectReader() { 223 | @Override 224 | public EmployeesByOrganization read(ResponseReader reader) { 225 | return employeesByOrganizationFieldMapper.map(reader); 226 | } 227 | }); 228 | } 229 | }); 230 | return new Data(employeesByOrganization); 231 | } 232 | } 233 | } 234 | 235 | public static class EmployeesByOrganization { 236 | static final ResponseField[] $responseFields = { 237 | ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), 238 | ResponseField.forCustomType("id", "id", null, false, CustomType.ID, Collections.emptyList()), 239 | ResponseField.forString("name", "name", null, false, Collections.emptyList()) 240 | }; 241 | 242 | final @NotNull String __typename; 243 | 244 | final @NotNull String id; 245 | 246 | final @NotNull String name; 247 | 248 | private volatile String $toString; 249 | 250 | private volatile int $hashCode; 251 | 252 | private volatile boolean $hashCodeMemoized; 253 | 254 | public EmployeesByOrganization(@NotNull String __typename, @NotNull String id, 255 | @NotNull String name) { 256 | this.__typename = Utils.checkNotNull(__typename, "__typename == null"); 257 | this.id = Utils.checkNotNull(id, "id == null"); 258 | this.name = Utils.checkNotNull(name, "name == null"); 259 | } 260 | 261 | public @NotNull String __typename() { 262 | return this.__typename; 263 | } 264 | 265 | public @NotNull String id() { 266 | return this.id; 267 | } 268 | 269 | public @NotNull String name() { 270 | return this.name; 271 | } 272 | 273 | public ResponseFieldMarshaller marshaller() { 274 | return new ResponseFieldMarshaller() { 275 | @Override 276 | public void marshal(ResponseWriter writer) { 277 | writer.writeString($responseFields[0], __typename); 278 | writer.writeCustom((ResponseField.CustomTypeField) $responseFields[1], id); 279 | writer.writeString($responseFields[2], name); 280 | } 281 | }; 282 | } 283 | 284 | @Override 285 | public String toString() { 286 | if ($toString == null) { 287 | $toString = "EmployeesByOrganization{" 288 | + "__typename=" + __typename + ", " 289 | + "id=" + id + ", " 290 | + "name=" + name 291 | + "}"; 292 | } 293 | return $toString; 294 | } 295 | 296 | @Override 297 | public boolean equals(Object o) { 298 | if (o == this) { 299 | return true; 300 | } 301 | if (o instanceof EmployeesByOrganization) { 302 | EmployeesByOrganization that = (EmployeesByOrganization) o; 303 | return this.__typename.equals(that.__typename) 304 | && this.id.equals(that.id) 305 | && this.name.equals(that.name); 306 | } 307 | return false; 308 | } 309 | 310 | @Override 311 | public int hashCode() { 312 | if (!$hashCodeMemoized) { 313 | int h = 1; 314 | h *= 1000003; 315 | h ^= __typename.hashCode(); 316 | h *= 1000003; 317 | h ^= id.hashCode(); 318 | h *= 1000003; 319 | h ^= name.hashCode(); 320 | $hashCode = h; 321 | $hashCodeMemoized = true; 322 | } 323 | return $hashCode; 324 | } 325 | 326 | public static final class Mapper implements ResponseFieldMapper { 327 | @Override 328 | public EmployeesByOrganization map(ResponseReader reader) { 329 | final String __typename = reader.readString($responseFields[0]); 330 | final String id = reader.readCustomType((ResponseField.CustomTypeField) $responseFields[1]); 331 | final String name = reader.readString($responseFields[2]); 332 | return new EmployeesByOrganization(__typename, id, name); 333 | } 334 | } 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/model/Department.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Department { 7 | 8 | private Long id; 9 | private String name; 10 | private List employees = new ArrayList<>(); 11 | 12 | public Department() { 13 | 14 | } 15 | 16 | public Department(String name) { 17 | super(); 18 | this.name = name; 19 | } 20 | 21 | public Long getId() { 22 | return id; 23 | } 24 | 25 | public void setId(Long id) { 26 | this.id = id; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public List getEmployees() { 38 | return employees; 39 | } 40 | 41 | public void setEmployees(List employees) { 42 | this.employees = employees; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "Department [id=" + id + ", name=" + name + "]"; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/model/Employee.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.model; 2 | 3 | public class Employee { 4 | 5 | private Long id; 6 | private String name; 7 | private Long departmentId; 8 | 9 | public Employee() { 10 | 11 | } 12 | 13 | public Employee(Long id, String name, Long departmentId) { 14 | super(); 15 | this.id = id; 16 | this.name = name; 17 | this.departmentId = departmentId; 18 | } 19 | 20 | public Employee(String name, Long departmentId) { 21 | super(); 22 | this.name = name; 23 | this.departmentId = departmentId; 24 | } 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public Long getDepartmentId() { 43 | return departmentId; 44 | } 45 | 46 | public void setDepartmentId(Long departmentId) { 47 | this.departmentId = departmentId; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "Employee [id=" + id + ", name=" + name + "]"; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/model/Organization.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Organization { 7 | 8 | private Long id; 9 | private String name; 10 | private String address; 11 | private List departments = new ArrayList<>(); 12 | private List employees = new ArrayList<>(); 13 | 14 | public Organization() { 15 | 16 | } 17 | 18 | public Organization(String name, String address) { 19 | this.name = name; 20 | this.address = address; 21 | } 22 | 23 | public Long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(Long id) { 28 | this.id = id; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getAddress() { 40 | return address; 41 | } 42 | 43 | public void setAddress(String address) { 44 | this.address = address; 45 | } 46 | 47 | public List getDepartments() { 48 | return departments; 49 | } 50 | 51 | public void setDepartments(List departments) { 52 | this.departments = departments; 53 | } 54 | 55 | public List getEmployees() { 56 | return employees; 57 | } 58 | 59 | public void setEmployees(List employees) { 60 | this.employees = employees; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return "Organization [id=" + id + ", name=" + name + ", address=" + address + "]"; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/repository/OrganizationRepository.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.repository; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | import pl.piomin.services.organization.model.Organization; 8 | 9 | public class OrganizationRepository { 10 | 11 | private List organizations = new ArrayList<>(); 12 | 13 | public Organization add(Organization organization) { 14 | organization.setId((long) (organizations.size()+1)); 15 | organizations.add(organization); 16 | return organization; 17 | } 18 | 19 | public Organization findById(Long id) { 20 | Optional organization = organizations.stream().filter(a -> a.getId().equals(id)).findFirst(); 21 | if (organization.isPresent()) 22 | return organization.get(); 23 | else 24 | return null; 25 | } 26 | 27 | public List findAll() { 28 | return organizations; 29 | } 30 | 31 | public boolean delete(Long id) { 32 | return organizations.removeIf(it -> it.getId() == id.longValue()); 33 | } 34 | 35 | public Organization update(Long id, Organization organization) { 36 | organization.setId(id); 37 | int index = organizations.indexOf(organization); 38 | organizations.set(index, organization); 39 | return organization; 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/resolver/OrganizationMutations.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.resolver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import com.coxautodev.graphql.tools.GraphQLMutationResolver; 9 | 10 | import pl.piomin.services.organization.model.Organization; 11 | import pl.piomin.services.organization.repository.OrganizationRepository; 12 | 13 | 14 | @Component 15 | public class OrganizationMutations implements GraphQLMutationResolver { 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationMutations.class); 18 | 19 | @Autowired 20 | OrganizationRepository repository; 21 | 22 | public Organization newOrganization(Organization department) { 23 | LOGGER.info("Organization add: department={}", department); 24 | return repository.add(department); 25 | } 26 | 27 | public boolean deleteOrganization(Long id) { 28 | LOGGER.info("Organization delete: id={}", id); 29 | return repository.delete(id); 30 | } 31 | 32 | public Organization updateOrganization(Long id, Organization department) { 33 | LOGGER.info("Organization update: id={}, department={}", id, department); 34 | return repository.update(id, department); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /organization-service/src/main/java/pl/piomin/services/organization/resolver/OrganizationQueries.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.organization.resolver; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.coxautodev.graphql.tools.GraphQLQueryResolver; 11 | 12 | import pl.piomin.services.organization.client.EmployeeClient; 13 | import pl.piomin.services.organization.model.Organization; 14 | import pl.piomin.services.organization.repository.OrganizationRepository; 15 | 16 | @Component 17 | public class OrganizationQueries implements GraphQLQueryResolver { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationQueries.class); 20 | 21 | @Autowired 22 | EmployeeClient employeeClient; 23 | @Autowired 24 | OrganizationRepository repository; 25 | 26 | public List organizations() { 27 | LOGGER.info("Organization find"); 28 | return repository.findAll(); 29 | } 30 | 31 | public Organization organizationByIdWithEmployees(Long id) throws InterruptedException { 32 | LOGGER.info("Organizations find: id={}", id); 33 | Organization organization = repository.findById(id); 34 | organization.setEmployees(employeeClient.findByOrganization(id)); 35 | return organization; 36 | } 37 | 38 | public Organization organization(Long id) { 39 | LOGGER.info("Organization find: id={}", id); 40 | return repository.findById(id); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /organization-service/src/main/java/type/CustomType.java: -------------------------------------------------------------------------------- 1 | package type; 2 | 3 | import com.apollographql.apollo.api.ScalarType; 4 | import java.lang.Class; 5 | import java.lang.Override; 6 | import java.lang.String; 7 | import javax.annotation.Generated; 8 | 9 | @Generated("Apollo GraphQL") 10 | public enum CustomType implements ScalarType { 11 | ID { 12 | @Override 13 | public String typeName() { 14 | return "ID"; 15 | } 16 | 17 | @Override 18 | public Class javaType() { 19 | return String.class; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /organization-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: organization-service 4 | cloud: 5 | config: 6 | uri: http://localhost:8088 -------------------------------------------------------------------------------- /organization-service/src/main/resources/graphql/organization.graphqls: -------------------------------------------------------------------------------- 1 | schema { 2 | query: OrganizationQueries 3 | mutation: OrganizationMutations 4 | } 5 | 6 | type OrganizationQueries { 7 | organizations: [Organization] 8 | organization(id: ID!): Organization! 9 | organizationByIdWithEmployees(id: Int!): Organization 10 | organizationByIdWithDepartments(id: Int!): Organization 11 | organizationByIdWithDepartmentsAndEmployees(id: Int!): Organization 12 | } 13 | 14 | type OrganizationMutations { 15 | newOrganization(organization: OrganizationInput!): Organization 16 | deleteOrganization(id: ID!) : Boolean 17 | updateOrganization(id: ID!, organization: OrganizationInput!): Organization 18 | } 19 | 20 | input OrganizationInput { 21 | organizationId: Int! 22 | name: String! 23 | } 24 | 25 | type Organization { 26 | id: ID! 27 | name: String! 28 | address: String! 29 | employees: [Employee] 30 | departments: [Department] 31 | } 32 | 33 | type Department { 34 | id: ID! 35 | name: String! 36 | employees: [Employee] 37 | } 38 | 39 | type Employee { 40 | id: ID! 41 | name: String! 42 | position: String! 43 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.0.0.RELEASE 9 | 10 | 11 | 12 | pl.piomin.services 13 | sample-graphql-microservices 14 | 1.0-SNAPSHOT 15 | pom 16 | 17 | 18 | 1.8 19 | piomin_sample-graphql-microservices 20 | piomin 21 | https://sonarcloud.io 22 | 23 | 24 | 25 | discovery-service 26 | employee-service 27 | department-service 28 | organization-service 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-dependencies 36 | Finchley.SR1 37 | pom 38 | import 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## GraphQL - The Future of Microservices? [![Twitter](https://img.shields.io/twitter/follow/piotr_minkowski.svg?style=social&logo=twitter&label=Follow%20Me)](https://twitter.com/piotr_minkowski) 2 | 3 | Detailed description can be found here: [GraphQL - The Future of Microservices?](https://piotrminkowski.com/2018/08/16/graphql-the-future-of-microservices/) 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base",":dependencyDashboard" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 9 | "automerge": true 10 | } 11 | ], 12 | "prCreation": "not-pending" 13 | } --------------------------------------------------------------------------------