What do I want:
I want to be able to generate swagger documentation that passes a key/value into the URL. This so that I can use generic arguments controller to handle my requests like Dictionary.
If swagger can't generate it, is there a way to generate the documentation by using reflection on my objects? This so that I can still use generic methods
If not, what would be the best way to let everyone know what the correct approach would be.
Why do I want it
I'm developing a new API and I'm using swagger to create the documentation. In this API I want to work with some generic methods to prevent hardcoding things. For example on the PATCH method I use a Dictionary<string, string> to get the property/value combination and in the GET I use a custom object as the argument. In both cases, swagger can't generate the correct parameter fields, because it takes the argument as url key.
Example action & form - incorrect
public async Task<IActionResult> Patch(int id, Dictionary<string, string> viewModel)
{
return await ConnectionWrapper(() => connector.Patch(id, viewModel));
}
This uses the body, not the query
Other examples - incorrect
In the GET I have a model with a custom modelbinder to handle all the rest URL arguments. The problem is because the model is defined it sees the filter as a property.
Then it is in the URL, but it will look like http://example.com/controller/method/id?sort=prop_asc&filter=propTwo%3D=value, instead of http://example.com/controller/method/id?sort=prop_asc&propTwo=value
Desired output
I've modified the HTML to simulate what I would like in the picture above. The URL that would be called would be http://example.com/controller/method/id?propertyName=propertyValue.
I don't mind if there would be only one option to add a generic key/value pair because with it I can demonstrate what I want.
Expected solution
I think the solution lies in the MapType startup method of swagger or in an implementation of the IOperationFilter, but I haven't been able to figure it out.
Related
I’ve a server application with Vapor 3, I set up three models Book, Author, Publisher, relative routers and controllers with CRUD and relationships (both Author and Publisher have a Sibling relationship with book).
All would be fine was not for the incoming data: the Json I receive has a different data structure, where the publishers and authors are stored in arrays inside the book.
I cannot decode on Book.self because I would lose the data relevant to publishers and authors, so I created BookTotal, which has no representation on the database (no migration) so I could
req.content.decode(BookTotal.self).flatMap
At this point I would like to call the save or update functions on the Publisher and Author controllers passing the BookTotal.Publisher and BookTotal.Author array.
But, while I can reach the methods either with something like
`PublisherController.Save()`
Or
let client = req.make(Client.self)
let response = client(“path/for/the/router”)
I don’t know how to pass them the data. The CRUD methods inside the controllers have Request as parameter but I don’t know how to build one.
let newReq = Request()
newReq.content = BookTotal.Author
Would be my guess, but Request wants a Container and I don’t know how I should go about that.
I searched around but couldn't find any example/tutorial on such issue.
Am I even on the right path with this?
So you have a couple of options to achieve what you want to do. One option is to rewrite your request handlers in your controllers to accept that data using one of the helper functions. So
func savePublisher(_ req: Request, data: Publisher) throws -> Future<Publisher>
You can then change router.post("your", "url", use: savePublisher) to router.post(Publisher.self, at: "your", "url", use: savePublisher). Then if your code you can call that method and pass the data through as needed.
A (potentially) better way would be to extract the code that saves a publisher out into a separate function which you can then call from your savePublisher route and your bookTotal route. For basic routes this is likely to be the same as the first option, but for most complex routes this is the way to go.
Usually an AEM component is retrieving its data from a JCR node, but I was wondering whether it's possible to pass data to it in HTL. Sure, there's data-sly-resource, but as far as I know this way you can only pass a JCR node.
So in an actual case I've got data in a model that's retrieved from elsewhere. Yet I'd like to use existing components. I'm aware that the data must at least match the component-types' model.
But what if the component I'd like to use is using an model that got its data injected like
#Inject
#Optional
String[] itemList;
So in my stubborn thoughts it should be possible to somehow pass a string array like
<div data-sly-resource="${myModel.aStringArray # resourceType='my/component' }"></div>
But like mentioned above this seems to be meant for passing nodes only.
Is there any way to accomplish passing data directly to a component (other than creating a template)?
You can pass additional information in the form of request attributes or selectors
Selectors
Selectors are the most straight forward of passing simple information. This is an array of strings that can be passed. This is quite useful to data that can act as flags ex:
Variant/Mode of the component
index of the component in a list if it is being included in a loop.
ID of the parent when building things like accordion, tabs
This approach is an abuse of selectors, but IMHO as long as you know what you are doing this shouldn't be a major concern.
<article data-sly-resource="${'path/to/resource' # selectors=['s1', 's2']}"></article>
You can add, replace or remove selectors while including the component. Checkout the documentation for the syntax. https://docs.adobe.com/content/help/en/experience-manager-htl/using/htl/block-statements.html#resource
Request Attributes
This option allows you to add custom request attributes to the component request. This can be used to pass objects as parameters to the component while including them.
These are standard http request attributes with convince of scoping them to a particular instance of script/resource inclusion. To use this you will end up needing a model class or use-js as there is little support to compose the data to be passed along in sightly.
<sly data-sly-use.settings="com.adobe.examples.htl.core.hashmap.Settings"
data-sly-include="${ 'productdetails.html' # requestAttributes=settings.settings}" />
https://docs.adobe.com/content/help/en/experience-manager-htl/using/htl/block-statements.html#request-attributes
There is another way. You can pass additional parameters to the Sling Model on initialization using data-sly-use. For example:
<div data-sly-use.model="${'com.model.Teaser' # test='abc'}"
You can read then the variable "test" in model from request:
#PostConstruct
private void initModel() {
String value = request.getAttribute("test");
// value is 'abc'
}
In order this to work correctly you need to make sure your Sling Model is adaptable from request #Model(adaptables = SlingHttpServletRequest.class}
I am developing a rest api on .Net core 2.2 following MVC pattern.
I have a controller with a post method like this...
// POST: api/Todo
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(string param, [FromBody] TodoItem item)
{
// some work...
return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
}
And it works fine.
The customer asked to have an api on the same route, but the Json body could have 2 different structures, bearing the same data on different schemas.
I considered using
PostTodoItem(string param, [FromBody] Object item)
{
// TryCast item to one of the possible POCO classes then work with the correct one.
}
Do you know a better way, maybe with some advanced routing and filtering option?
This is not really possible nor desirable. Pretty much the core tenant of REST is a URI uniquely represents a particular resource. If you've got a URI like POST /todo, then the post body should be a "todo" and it should create a new "todo" based on that. Here, that is a TodoItem, so that is all that should ever be posted.
REST aside, this just won't work. When your action is activated, the modelbinder attempts to bind the post body to the param(s) that the action accepts. It basically just news up whatever type the param is, and then attempts to find something from the post body to bind to the various properties on that type. This is an intentionally simplistic description of what's happening; the important part is that the type of the param informs how the post body is bound. If you bind to an object (which has no members) or even a base type, then the only members of the post body that will be bound are those that are present on that type, not derived types thereof. Anything that cannot be bound is discarded.
Long and short, you need a unique route for each type of thing you're working with. Under the hood, you can share or otherwise reuse code by factoring out common functionality into private methods, employing inheritance, etc., but you need a distinct action and route to handle each case.
I am building a sample project with Angular2/Typescript in order to use it as a new frontend for an existing backend code.
I really like the ability to create types and consider to use some of the JSON objects served by the backend as typed classes like this:
module DomainObjects {
export class SomeDomainObject {
constructor(attr1:string, attr2:number) {...}
...
}
}
Some of the JSON code that is returned by the backend is very large, so I don't want to work with a huge amount of parameters in the constructor. At best i just pass a JSON object as a single parameter to the constructor which does some checks (or not). On the other hand I'd like to access the JSON object directly. Is something like this possible:
myobject:SomeDomainObject;
...
this.myobject = new SomeDomainObject({id:10,color:'green'});
and access myobject in a template like this
{{myobject.color}}
without having another reference like {{myobject.json.color}}
You can model your JSON data with interfaces (instead of classes). Then you can simply assign the received JSON to it (instead of calling a constructor).
For validation you will most likely need to build a validation function that checks if the received JSONs structure really equals the type that you described in your interface. You could also build a validate and assign function with a signature like
function toTypedData(input: any):SomeDomainObject { ... }
You can also use JSON schema and a suitable validation library to perform that step.
I want to append the query parameters list of a received UriInfo in a Rest service. The query comes from the client with some parameters and I need to add some more in server side.
I tried with:
uriInfo.getQueryParameters().add("Param", "value");
but when I display the URI, it doesn't has the new parameter.
On the other hand, if I do it like this:
URI uri = uriInfo.getRequestUriBuilder().queryParam("Param", "value").build();
when I display the URI, it contains the new parameter. The problem in this second case is to reconstruct a UriInfo object to give to the next functions, they require it.
I've seen that it cannot be instantiated, it has no constructors, it has to be added with #Context, its value can be updated by another UriInfo... but how to create this UriInfo with the URI I modified?
It is not possible to modify a UriInfo, there are no methods defined for that. The only option is to recreate it using one implementation of the interface. The only implementation available is org.jboss.resteasy.spi.ResteasyUriInfo.
The problem is that when deployed, and the function using it is called, it throws a ClassDefNotFound exception; even with a dependency in the manifest pointing to resteasy-jaxrs-2.3.2.Final.jar
So, the only option is to make our own implementation of the interface.