Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Replace samples #208

Open
rstoyanchev opened this issue Dec 2, 2021 · 8 comments
Open

Replace samples #208

rstoyanchev opened this issue Dec 2, 2021 · 8 comments
Labels
type: enhancement A general enhancement
Milestone

Comments

@rstoyanchev
Copy link
Contributor

The current samples in the repository depend on the Boot starter which is about to move to the Spring Boot repository, which also means that the samples need to move too. The plan is to turn them into tests spring-graphql and/or spring-graphql-test.

In addition we'll also create a getting started guide and a more advanced external sample.

rstoyanchev added a commit that referenced this issue Nov 24, 2022
We no longer build the samples in the main branch due to dependency
issues between Boot starter dependencies and the project sources.
The samples really need to move out to an independent repository.
For now, this commit removes them from the main branch.

See gh-208
@eduanb
Copy link

eduanb commented Dec 5, 2022

This is probably part of this issue, but the current link to samples in the documentation is broken. The link here https://docs.spring.io/spring-graphql/docs/current/reference/html/#samples points to https://github.com/spring-projects/spring-graphql/tree/main/samples (broken)

@rstoyanchev
Copy link
Contributor Author

Thanks, @eduanb. This was addressed in 0baceda already.

@etienne-sf
Copy link

Thanks, @eduanb. This was addressed in 0baceda already.

Hello,

The link is actually corrected in the 1.1.3 version. It would be nice if it is also corrected in the 1.0.4 doc.

Etienne

@codesnippe
Copy link

Is there still a chance to get this documented Provide guidance on how to set up multiple GraphQL endpoints #439?

@rstoyanchev
Copy link
Contributor Author

Yes, it's the plan, however it hasn't been done. In the mean time, start with the Boot auto configuration, which shows how to set up a single endpoint, and go from there. If you want to make a sample repository somewhere, and you get stuck, we can have a look and help you.

@codesnippe
Copy link

Thanks @rstoyanchev for the reply and guidance. I actually made this work so I created a small project to showcase how I did it https://github.com/codesnippe/multiple-graphql-endpoints-demo. If you guys have time to have a look at it and give some feedback to see if this fine, or if they are any 'nice-to-know' or 'nice-to-have' things that could be done as well, I would really appreciate it. Thanks!

@rstoyanchev rstoyanchev modified the milestones: 1.2 Backlog, 1.x Backlog Apr 14, 2023
rstoyanchev added a commit that referenced this issue May 23, 2024
rstoyanchev added a commit that referenced this issue May 23, 2024
rstoyanchev added a commit that referenced this issue May 23, 2024
rstoyanchev added a commit that referenced this issue May 23, 2024
@bostandyksoft
Copy link

bostandyksoft commented May 31, 2024

Hi. I've just finished my own attepmt to implement multiple graphql endpoints and now found this topic. I've notices that example does not provide ability to setup different packages to process external and internal schemas by different ways.

For example:
for external consumers i dont'want to fetch not processed books (e.g. checked for restricted content), but internal consumers should recive it. So in common case there is need different controllers for external/internal schemas, so in my example (Java) I've added PackagedAnnotatedControllerConfigurer, that overrides "detectHandlerMethods" and adds filters for loaded beans. But this way forced me to put this class in "org.springframework.graphql.data.method.annotation.support" package (beacuse there is several package-private fields/methods, that i could'nt use)

public class PackagedAnnotatedControllerConfigurer extends AnnotatedControllerConfigurer {
	private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";

	private String activePackage = "";

	public void setActivePackage(String activePackage) {
		this.activePackage = activePackage;
	}

	@Override
	protected Set<DataFetcherMappingInfo> detectHandlerMethods() {
		Set<DataFetcherMappingInfo> results = new LinkedHashSet<>();
		ApplicationContext context = obtainApplicationContext();
		for (String beanName : context.getBeanNamesForType(Object.class)) {
			if (beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				continue;
			}
			Class<?> beanType = null;
			try {
				beanType = context.getType(beanName);
			}
			catch (Throwable ex) {
				// An unresolvable bean type, probably from a lazy bean - let's ignore it.
				if (this.logger.isTraceEnabled()) {
					this.logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
				}
			}
			if (beanType == null || !AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || !isPackageMatched(beanType)) {
				continue;
			}
			Class<?> beanClass = context.getType(beanName);
			findHandlerMethods(beanName, beanClass).forEach((info) -> addHandlerMethod(info, results));
		}

		return results;
	}

	/**
	 * Actually added only this check. All of code is copy from source class
	 */
	private boolean isPackageMatched(Class<?> beanType) {
		return StringUtils.isEmpty(activePackage) || activePackage.contains(beanType.getPackageName());
	}

	private Collection<DataFetcherMappingInfo> findHandlerMethods(Object handler, @Nullable Class<?> handlerClass) {
		if (handlerClass == null) {
			return Collections.emptyList();
		}

		Class<?> userClass = ClassUtils.getUserClass(handlerClass);
		Map<Method, DataFetcherMappingInfo> map = MethodIntrospector.selectMethods(
				userClass, (Method method) -> getMappingInfo(method, handler, userClass));

		return map.values();
	}

	private void addHandlerMethod(DataFetcherMappingInfo info, Set<DataFetcherMappingInfo> results) {
		HandlerMethod handlerMethod = getHandlerMethod(info);
		DataFetcherMappingInfo existing = results.stream().filter((o) -> o.equals(info)).findFirst().orElse(null);
		if (existing != null && !getHandlerMethod(existing).equals(handlerMethod)) {
			throw new IllegalStateException(
					"Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
							handlerMethod + "\n" + ": There is already '" +
							getHandlerMethod(existing).getBean() + "' bean method\n" + existing + " mapped.");
		}
		results.add(info);
		((AnnotatedControllerExceptionResolver)this.getExceptionResolver()).registerController(handlerMethod.getBeanType());
	}
}

and then add wiringConfigurer customization

	wiringConfigurers.orderedStream().forEach(customizer -> {
			if (customizer instanceof PackagedAnnotatedControllerConfigurer packaged) {
				packaged.setActivePackage(activePackage);
			}
			builder.configureRuntimeWiring(customizer);
		});

I suggest to partially change code of AnnotatedControllerConfigurer to build more clean way to implement ability of different packages for different endpoints

Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants