Mock data generation for storybook - relayjs

I am trying to generate mock data using relay for storybook.
My query is
const QUERY_LIST = graphql`
query modelControllerAllUsersQuery #relay_test_operation {
allUsers {
pageInfo {
hasNextPage
}
edges {
node {
id
firstName
lastName
}
}
}
}
`
and provided RelayEnvironmentProvider as a decorator to the story. I'm trying to return some default values to my query using custom mock resolvers.
const customMockResolvers = {
...mockResolvers,
allUsers:() => ({
pageInfo:{
hasNextPage:false,
},
edges:[
{
node:{
id :'id',
firstName:'fname',
lastName :'lname',
},
},
],
}),
};
and calling it as
(operation) => MockPayloadGenerator.generate(operation, customMockResolvers)
I don't seem to be able to get the default values returned.
Currently, it is returning
{"allUsers":{"pageInfo":{"hasNextPage":false},"edges":[{"node":{"id":"<UserNode-mock-id-1>","firstName":"<mock-value-for-field-\"firstName\">","lastName":"<mock-value-for-field-\"lastName\">"}}]}}
What am I doing wrong?

When using the #relay-test-operation, the keys within your customMockResolvers object must match the type name of the fields, which can be different from the field names themselves.
For example, you could have the following in your schema:
type Foo {
id: ID!
name: String!
}
and the following query:
query FooQuery #relay_test_operation {
foo {
id
name
}
}
Then the customMockResolvers object would look like this:
const customMockResolvers = {
Foo: () => ({
id: "fooId",
name: "fooName"
})
}
Notice that I'm passing in Foo as the key instead of foo.
You can check your schema and see what the the type name of allUsers is. I suspect it would be something like AllUsers or allUsersConnection, or something similar.
Also, if you're interested in creating Storybook stories for Relay components, I created a NPM package just for that: https://www.npmjs.com/package/use-relay-mock-environment
It doesn't require adding the #relay-test-operation directive to your query, and instead relies only on resolving the String type (which is the default for all scalar properties). You can of course still add the #relay-test-operation directive and also extend the resolvers by providing customResolvers in the config.
You can also extend the the String resolver as well, by providing extendStringResolver in the config.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!

Related

Modifying the DTO name appearing in OpenAPI (Swagger) schemas in NestJS

I am facing a problem where my DTO types are named one thing, but I want them to appear with a different name in the OpenAPI doc page.
For example, I have a UserDto class that I use in my controller, but wanted it to appear as simply "User" in the schemas section (and everywhere else this applies). Is that possible? Is there any decorator I can use?
I know I can simply modify the class name, but there is already a different user class used elsewhere.
I have searched everywhere with no avail.
BTW, I am using typescript and nestjs.
Every help will be appreciated, thanks!
Out of the box, Nest.js doesn't yet offer a ready-made solution. There is an open pull request (as mentioned earlier) https://github.com/nestjs/swagger/pull/983, but when it will be merged is unknown.
You can change the DTO name in schemas using one of the following approaches:
Add a static name property to your DTO.
class UserDto {
static name = 'User'; // <- here
#ApiProperty()
firstName: string;
// ...
}
But in strict mode, TypeScript will show an error like:
Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'UserDto'.
Write a decorator with an interface as suggested in the pull request and use it until the desired functionality appears in Nest.js.
The decorator adds the name property with the needed value to the wrapper class for the DTO.
type Constructor<T = object> = new(...args: any[]) => T;
type Wrapper<T = object> = { new(): (T & any), prototype: T };
type DecoratorOptions = { name: string };
type ApiSchemaDecorator = <T extends Constructor>(options: DecoratorOptions) => (constructor: T) => Wrapper<T>;
const ApiSchema: ApiSchemaDecorator = ({ name }) => {
return (constructor) => {
const wrapper = class extends constructor { };
Object.defineProperty(wrapper, 'name', {
value: name,
writable: false,
});
return wrapper;
}
}
Use as suggested in the proposal:
#ApiSchema({ name: 'User' }) // <- here
class UserDto {
#ApiProperty()
firstName: string;
// ...
}
And don't forget that in TypeScript 5 the decorator API will change to something close to the implementation in JavaScript 😉
I solved in my case using #ApiModel
like this
#ApiModel(value="MeuLindoDto")
public class NameOriginalClassResponseDto ...

Can't count the occurences of the entity with a field of particular value inside a nested property using Spring Data ElasticSearch Repository

I have the Article entity and inside it there is a nested property, let's say Metadata.
I need to count all articles, which have a particular field inside this nested property, let's say indexed, assigned to e.g. 1.
Java Document Snippet:
#Document(indexName = "article", type = "article", useServerConfiguration = true, createIndex = false)
#Setting(settingPath = "/mappings/settings.json")
#Mapping(mappingPath = "/mappings/articles.json")
public class Article {
// getters and setters, empty constructor are omitted for brevity
#Id
private String id;
private Metadata metadata;
// remainder of the body is omitted
}
Metadata.class snippet
public class Metadata {
// getters and setters, empty constructor are omitted for brevity
private Integer indexed;
// remainder of the body is omitted
}
The query I use to retrieve articles, which satisfy the given criteria and which I put as a value of #org.springframework.data.elasticsearch.annotations.Query on top of the custom method:
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "metadata",
"query": {
"bool": {
"must": [
{
"match": {
"metadata.indexed": 1
}
}
]
}
}
}
}
]
}
}
}
My custom Spring Data ElasticSearch repository snippet with a custom method:
public CustomSpringDataElasticsearchRepository extends ElasticsearchRepository<Article, String> {
#Query("The query from above")
Long countByMetadata_Indexed(int value);
}
When I use the repository method shown above , I get java.lang.IllegalArgumentException: Expected 1 but found n results.
Custom Spring Data Elasticsearch Repository method(without #Query) returns 0(version without underscore returns 0 as well) though it should return everything correctly.
How do I get the correct results using Spring Data ElasticSearch Repository? Why does the custom method without #Query doesn't work as well?
UPD: The version of spring-data-elasticsearch used is 3.1.1.RELEASE.
Repository query methods currently(3.2.4.RELEASE) don't support the count by the fields inside nested fields.
As was mentioned previously, #Query annotation doesn't support custom count queries as of the latest version(3.2.4.RELEASE).
In other words, currently, the only way to do this query through Spring Data ElasticSearch is to use ElasticsearchTemplate bean or ElasticsearchOperations bean.
Credit: P.J.Meisch

How to make makeExecutableSchema ignore directives?

Is there a way to tell makeExecutableSchema from graphql-tools to ignore certain directives?
I want to query my neo4j database with graphql. I also want to be able to specify subtypes in graphql. There is a library called graphql-s2s which adds subtypes to graphql. The library neo4j-graphql-js uses custom directives (#cyper and #relation) to build an augmented Schema. It takes typeDefs or an executableSchema from graphql-tools. qraphql-s2s lets me create an executableSchema out of the Schema containing subtypes. My hope was that I should be easy to just pipe the different schema outputs into each other like in a decorator pattern.
Unfortunately this is apparently not how it works as I get a lot of parser error messages, which are not really descriptive.
Unfortunately I haven't found any Grandstack documentation where it is shown how to use augmentSchema() on an executableSchema with relations and cypher in it.
Is there a way how to do this?
Below my naive approach:
const { transpileSchema } = require('graphql-s2s').graphqls2s;
const { augmentSchema } = require('neo4j-graphql-js');
const { makeExecutableSchema} = require('graphql-tools');
const { ApolloServer} = require('apollo-server');
const driver = require('./neo4j-setup');
/** The #relation and #cypher directives don't make any sense here they are
just for illustration of having directives that make sense to
'augmentSchema' and not to 'makeExecutableSchema' **/
const schema = `
type Node {
id: ID!
}
type Person inherits Node {
firstname: String
lastname: String #relation(name: "SOUNDS_LIKE", direction: "OUT")
}
type Student inherits Person {
nickname: String #cypher(
statement: """ MATCH (n:Color)...some weird cypher query"""
)
}
type Query {
students: [Student]
}
`
const resolver = {
Query: {
students(root, args, context) {
// Some dummy code
return [{ id: 1, firstname: "Carry", lastname: "Connor", nickname: "Cannie" }]
}
}
};
const executabledSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver
})
const schema = augmentSchema(executabledSchema)
const server = new ApolloServer({ schema, context: { driver } });
server.listen(3003, '0.0.0.0').then(({ url }) => {
console.log(`GraphQL API ready at ${url}`);
});

EXT JS 5: viewModel links variable id

I am using Ext JS 5.0.1 and I am trying to use links in the viewModel defined inside a view.
The example below works.
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
theIdToUse: 47,
viewModel: {
links: {
theProject: {
type 'mypackage.MyModelClassName'
id: 17 //This works. But not theIdToUse or this.theIdToUse.
//I would like to use a value provided from my view
}
}
}
});
I would like to use the value of 'theIdToUse' for the id property of 'theProject' defined in 'links'.
I have tried to simply put theIdToUse or this.theIdToUse but I always got the following error:
Cannot use bind config without a viewModel
Do you know how could I managed to use links with a variable id?
Thanks in advance!
Use linkTo, like:
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
theIdToUse: 47,
constructor: function(){
this.callParent(arguments);
this.linkTo('theProject',{
type 'mypackage.MyModelClassName',
id: this.theIdToUse
});
}
});
this.theIdToUse does not work because within the scope of the viewModel, this no longer refers to MyViewPackage.MyView, but to the viewModel itself.
Even if you could get a reference back to MyViewPackage.MyView, say using ComponentQuery, the component does not yet exist at the point that the viewModel is being initialized, so you will get an error Cannot read property 'theIdToUse' of undefined.
You would probably be better off using some sort of two way binding between the view and viewModel, but I would need to know more about what you're trying to achieve to say exactly how.
Previous answer were pretty correct, except that in your case this would refer to window, not viewModel. This is due to in Ext.define you are passing anonymous object without some scope, so window scope would be used by default.
Suppose you should use something like this:
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
bind: {theIdToUse: "{id}"}
viewModel: {
links: {
theProject: {
type 'mypackage.MyModelClassName'
id: 17 //This works. But not theIdToUse or this.theIdToUse.
//I would like to use a value provided from my view
}
}
}
});
Although the question is from long time ago, I leave a possible solution for further viewers.
Try:
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
config: {
theIdToUse: 47,
},
viewModel: {
links: {
theProject: {
type 'mypackage.MyModelClassName'
id: null // Will be copied from config in initConfig
}
}
},
initConfig: function (config) {
this.config.viewModel.links.theProject.id = config.theIdToUse ;
this.callParent([config]) ;
}
});
You will be able to instanciate your panel with different ids:
items: [
{
xtype: 'myview',
theIdToUse: 47
},{
xtype: 'myview',
theIdToUse: 32
}
],
Even with:
Ext.create('MyViewPackage.MyView', {theIdToUse: 12}) ;

Grails cast sqltype "number" to String

I have an abstract Event class which holds properties shared by all Event types and beneath it I have two subclasses with one table per class (MasterEvent and ParentEvent).
The problem is that in MasterEvent the id column is of type "number" and in the ParentEvent it is of type "varchar2".
This means that when I try to run the app I get :
Caused by HibernateException: Wrong column type in * for column event_id. Found: number, expected: varchar2(255 char).
Note that this is a legacy database, so there's no chance in changing the column types on the database level.
The sample code below should help in understanding better:
Event.groovy
package a
abstract class Event {
String id
static mapping = {
tablePerHierarchy "false"
id column: "id"
}
}
ParentEvent.groovy
package a
class ParentEvent extends Event {
static mapping = {
id column: "id"
}
}
MasterEvent.groovy
package a
class MasterEvent extends Event {
static mapping = {
id column: "id", type: "number"
}
}
I have tried putting type: number all sorts of combinations, but it's always giving me the same error.
Is there anyway to either cast the sqlType directly to String or have grails ignore this validation?
type: 'number' does not work because it is not a hibernate basic type
Try changing it to type: 'long' (or whatever fits your legacy DB)
Also see the docs: Link
Edit after your comment
I don't know what you are doing in your abstract class, but perhaps you can do something like that and use the getter in your abstract class (not tested!):
class MasterEvent extends Event {
Long longId
static mapping = {
longId column: "id"
}
#Override
String getId() {
longId?.toString()
}
}
Did you try to define id field in subclasses? "String id" in Parent event and "long id" in Master event?
You could try a custom Hibernate UserType which takes the number id column and converts it to a String. This would then go in the MasterEvent class:
static mapping = {
id column: "id", type: NumberToStringType
}

Resources