Using Postgraphile in nodeJS, how to enable aggregate max of date field? - node.js

I am using postgraphile in NodeJS for graphql API based on Postgresql database. I need to get max(date_field), but postgraphile does not provide that option by default.
How can I enable aggregation of max on a date field?
I want something as follows. But inspection_Date field is not available under max
query Query {
allRooms {
aggregates {
max {
inspection_date
}
}
}
}

Using a slightly modified version of the approach outlined in the defining your own aggregates section of the pg-aggregates readme, you can create a new graphile plugin that uses a hook to modify the existing aggregate specs for "min" and "max" to use a different isSuitableType function that includes temporal types as well as numeric types:
import type { Plugin } from "graphile-build";
import type { AggregateSpec } from "#graphile/pg-aggregates/dist/interfaces";
import type { PgType } from "graphile-build-pg";
const addTemporalAggregatesPlugin: Plugin = (builder) => {
builder.hook(
"build",
(build) => {
const { pgAggregateSpecs } = build;
const isNumberLikeOrTemporal = (pgType: PgType): boolean =>
pgType.category === "N" || pgType.category === "D";
// modify isSuitableType for max and min aggregates
// to include temporal types see: https://www.postgresql.org/docs/current/catalog-pg-type.html
const specs = (pgAggregateSpecs as AggregateSpec[] | undefined)?.map(
(spec) => {
if (spec.id === "min" || spec.id === "max") {
return {
...spec,
isSuitableType: isNumberLikeOrTemporal,
};
}
return spec;
}
);
if (!specs) {
throw Error(
"Please that the pg-aggregates plugin is present and that AddTemporalAggregatesPlugin is appended AFTER it!"
);
}
const newBuild = build.extend(build, {});
// add modified aggregate specs to the build
newBuild.pgAggregateSpecs = specs;
return newBuild;
},
["AddTemporalAggregatesPlugin"],
// ensure this hook fires before other hooks in the pg-aggregates plugin
// that may depend on the "pgAggregatesSpecs" extension.
["AddGroupByAggregateEnumsPlugin"],
[]
);
};
export default addTemporalAggregatesPlugin;
Then just append this new plugin after the pg-aggregates plugin:
postgraphile(pool, "my_schema", {
pluginHook,
appendPlugins: [
PgAggregatesPlugin,
AddTemporalAggregatesPlugin,
],
// ...
})

Related

Eslint rule is running multiple times

I'm trying to write an eslint rule that enforces making sure the name property is defined on any classes that extend from other Error/Exception named classes (and fixes them).
As far as I can tell, it works in the astexplorer.net individually, but when I'm running it alongside other rules, it ends up getting ran multiple times, so the name property ends up being repeated multiple times in the resulting "fixed" file.
Is there anything in particular I can do to prevent it being run multiple times? I'm assuming what's happening is that it's inserting my name = 'ClassName';, then prettier is needing to reformat the code, which it does, but then maybe it's re-running my rule? I'm not sure.
Rule/fix code shown below. I've tried things like using *fix and yield, but that doesn't seem to help either (see commented code below, based on information in the eslint documentation)
module.exports = {
meta: {
hasSuggestions: true,
type: 'suggestion',
docs: {},
fixable: 'code',
schema: [], // no options,
},
create: function (context) {
return {
ClassDeclaration: function (node) {
const regex = /.*(Error|Exception)$/;
// If the parent/superClass is has "Error" or "Exception" in the name
if (node.superClass && regex.test(node.superClass.name)) {
let name = null;
const className = node.id.name;
// Test class object name
if (!regex.test(className)) {
context.report({
node: node,
message: 'Error extensions must end with "Error" or "Exception".',
});
}
// Find name ClassProperty
node.body.body.some(function (a) {
if (a.type === 'ClassProperty' && a.key.name === 'name') {
name = a.value.value;
return true;
}
});
// Name property is required
if (!name) {
context.report({
node: node,
message: 'Error extensions should have a descriptive name',
fix(fixer) {
return fixer.replaceTextRange(
[node.body.range[0]+1, node.body.range[0]+1],
`name = '${className}';`
);
},
// *fix(fixer) {
// name = className;
// yield fixer.replaceTextRange(
// [node.body.range[0]+1, node.body.range[0]+1],
// `name = '${className}';`
// );
//
// // extend range of the fix to the range of `node.parent`
// yield fixer.insertTextBefore(node.body, '');
// yield fixer.insertTextAfter(node.body, '');
// },
});
}
}
},
};
},
};
Turns out I had the AST Explorer set to the wrong parser, so it was showing me the wrong string name for the ClassProperty node. I should have been using PropertyDefinition instead.

Tiptap how to create a paragraph (p) on Shift-Enter, instead of a br?

Using TipTap, I'm trying to avoid adding a <br />, but create a <p></p> instead, with the focus inside that <p>|</p> when the user hit shift-Enter but I can't make it work.
Here's what I did so far:
new (class extends Extension {
keys () {
return {
'Shift-Enter' (state, dispatch, view) {
const { schema, tr } = view.state
const paragraph = schema.nodes.paragraph
console.log(tr.storedMarks)
const transaction = tr.deleteSelection().replaceSelectionWith(paragraph.create(), true).scrollIntoView()
view.dispatch(transaction)
return true
}
}
}
})()
How can I do this?
I don't know if this is still relevant but as I was looking for the same thing, I found two ways to make this work.
NOTE:
I'm using tiptap v2, if that's not a problem, then:
I overrode the HardBreak extension, since it's the one that use the Shift-Enter keybinding. It looks something like;
const CustomHardBreak = HardBreak.extend({
addKeyboardShortcuts() {
return {
"Mod-Enter": () => this.editor.commands.setHardBreak(),
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And used it like so;
editor = new Editor({
extensions: [
customNewline,
CustomHardBreak,
]
});
Use the default editor command createParagraphNear. E.g this.editor.commands.createParagraphNear()
I tried creating a custom extension from your code and ended up with something similar to the command above, i.e;
export const customNewline = Extension.create({
name: "newline",
priority: 1000, // Optional
addCommands() {
return {
addNewline:
() =>
({ state, dispatch }) => {
const { schema, tr } = state;
const paragraph = schema.nodes.paragraph;
const transaction = tr
.deleteSelection()
.replaceSelectionWith(paragraph.create(), true)
.scrollIntoView();
if (dispatch) dispatch(transaction);
return true;
},
};
},
addKeyboardShortcuts() {
return {
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And added this as an extension in my editor instance.
PS:
They both work, almost exactly the same, I haven't found a difference yet. But there's somewhat of a 'catch' if you would call it that; Both these methods don't work on empty lines/nodes, a character has to be added before the cursor for it to work, any character, even a space.
In TipTap 2.0 I am able to use this custom extension:
const ShiftEnterCreateExtension = Extension.create({
addKeyboardShortcuts() {
return {
"Shift-Enter": ({ editor }) => {
editor.commands.enter();
return true;
},
};
},
});
To make shift + enter behave like enter.
In my case I actually wanted enter to do something different. So I use prosemirror events to set a ref flag on whether shift was pressed. Than I check that flag under the "Enter" keyboard event -- which could be triggered normally or through the shift + enter extension.

Syncing React-Slick with Query Params

I'm using React-Slick to render <Report /> components in a carousel. I would like to sync each <Report />'s reportId with query params.
For example, a user would be able to see a specific report by going to myapp.com/reports?id=1 and it would take them to that specific "slide".
The problem I'm having is that the report data is being loaded before the slides are initialized. I can't find any good examples of react-slick's onInit or onReInit.
Instead of using onInit or onReInit, I just utilized the initialSlide setting and used componentDidUpdate().
componentDidUpdate = (prevProps, prevState) => {
const queryParams = qs.parse(this.props.location.search)
if (prevProps.reports !== this.props.reports) {
const sortedReports = this.sortReportsByDate(this.props.reports)
this.setSlideIndex(sortedReports.findIndex(i => i.id === parseInt(queryParams.reportId, 10)))
this.setQueryParams({
reportId: this.sortReportsByDate(this.props.reports)[this.state.slideIndex].id
})
}
if (prevState.slideIndex !== this.state.slideIndex) {
this.setQueryParams({
reportId: this.sortReportsByDate(this.props.reports)[this.state.slideIndex].id
})
}
}
And the settings:
const settings = {
...
initialSlide: reportsSortedByDate.findIndex(i => i.id === parseInt(queryParams.reportId, 10))
...
}
I hope someone finds this useful!

React-native and Redux healthy way to call actions on props change

I've been using react-native with redux for a while, and the way i learn to call actions when something change on prop is using the componentWillReceiveProps, but when I use it I need to pass between if's and some times it goes to the wrong if, then I need to add more stuff to prevent it.
Here's an example I have done. I know this is not the best way to do it, but it is what I could think of.
componentWillReceiveProps(newProps) {
if(Object.keys(newProps.selected_product).length > 0) {
if(Object.keys(this.props.current_location).length > 0 || Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
this.props.verifyProductById(newProps.selected_product, newProps.current_location, this.props.token);
} else {
this.props.statusScanner(false);
this._handleNextPage(1);
}
} else if(Object.keys(newProps.historic_product_confirm).length > 0) {
if(newProps.historic_product_confirm.location._id == newProps.current_location._id)
this.props.handleModalConfirmPrice(!this.props.modal_confirmPrice_status)
} else if(newProps.scanResult != "") {
this.props.statusScanner(false);
if(Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
} else {
this._handleNextPage(1);
}
} else {
this._handleNextPage(0);
}
}
What I need is a healthy way to call my actions when the props change.
Edit:
Here i have the full OfferScene and an action file example:
OfferScene:
https://gist.github.com/macanhajc/0ac98bbd2974d2f6fac96d9e30fd0642
UtilityActions:
https://gist.github.com/macanhajc/f10960a8254b7659457f8a09c848c8cf
As mentioned in another answer, componentWillReceiveProps is being phased out, so I would aim for trying to eliminate it where possible. You'll be future-proofing your code and keeping your component logic more declarative and easy to reason about. As someone who has been responsible for (and been frustrated by) lifecycle method abuse like this, here are some things that have helped me.
Remember that when using redux-thunk, along with passing dispatch as the first argument, you can also pass getState as the second. This allows you to access state values in your action logic instead of bringing them into your component's props and adding clutter. Something like:
export const ExampleAction = update =>
(dispatch, getState) => {
const { exampleBool } = getState().ExampleReducer
if (exampleBool) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update
})
}
}
Using async/await in action logic can be a lifesaver when your action depends upon fetched results from an API call:
export const ExampleAction = () =>
async (dispatch, getState) => {
const { valueToCheck } = getState().ExampleReducer
, result = await someAPICall(valueToCheck)
.catch(e => console.log(e))
if (result.length > 0) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update: result
})
}
}
For cases where your component's rendering behavior depends upon certain state values after your state has been updated, I highly recommend reselect. A very basic example would be something like:
component.js
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { shouldDisplayItems } from '../selectors'
import MyListviewComponent from './myListview'
class ItemList extends Component {
render() {
const { shouldDisplayItems, items } = this.props
return (
<>
{shouldDisplayItems && <MyListviewComponent items={items} />}
</>
)
}
}
const mapStateToProps = ({ ListItems }) => shouldDisplayItems(ListItems)
export default connect(mapStateToProps)(ItemList)
selectors.js:
(Assuming your ListItems reducer has the params items and visibilityFilter)
import { createSelector } from 'reselect'
export const shouldDisplayItems = createSelector(
[state => state],
({ items, visibilityFilter }) => {
return {
shouldDisplayItems: visibilityFilter && items.length > 0,
items
}
}
)
I should mention that another option would be using higher-order components, but it can be tricky to use this approach before having a good grasp on how to keep too much imperative logic out of your components (I learned this the hard way).
I agree with #AnuragChutani and #Goldy in terms of clarity of the code; break it down some more into more components or functions.
Now after some review of your componentWillReceiveProps function, it is definitely not specific enough to narrow down exactly which prop changes. If any connected redux variable changes, the componentWillReceiveProps function will be invoked each time.
So e.g. if 'token' or 'selected_product' updates, componentWillReceiveProps will be triggered, even though you did not want it to trigger for token updates.
You can use a comparison for a specific variable update in the props.
E.g Using lodash
if(!_.isEqual( nextProps.selected_product, this.props.selected_product ))
// if props are different/updated, do something
Secondly, you can call actions/callbacks in your actions to narrow down navigation.
E.g.
takePicture = (camera, options){
...
//on success
dispatch(handleModalConfirmPrice())
...
}}

How to prevent malicious Meteor subscriptions using check

I found an important security fault in my meteor app regarding subscriptions (maybe methods are also affected by this).
Even though I use the check package and check() assuring that the correct parameters data types are received inside the publication, I have realised that if a user maliciously subscribes to that subscription with wrong parameter data types it is affecting all other users that are using the same subscription because the meteor server is not running the publication while the malicious user is using incorrect parameters.
How can I prevent this?
Packages used:
aldeed:collection2-core#2.0.1
audit-argument-checks#1.0.7
mdg:validated-method
and npm
import { check, Match } from 'meteor/check';
Server side:
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: String,
section: String,
});
return Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
});
Client side:
// in the template:
Meteor.subscribe('postersPub', { size: 'large', section: 'movies' });
// Malicious user in the browser console:
Meteor.subscribe('postersPub', { size: undefined, section: '' });
Problem: The malicious user subscription is preventing all other users of getting answer from their postersPub subscriptions.
Extra note: I've also tried wrapping the check block AND the whole publication with a try catch and it doesn't change the effect. The error disappears from the server console, but the other users keep being affected and not getting data from the subscription that the malicious user is affecting.
Check method and empty strings
There is one thing to know about check and strings which is, that it accepts empty strings like '' which you basically showed in your malicious example.
Without guarantee to solve your publication issue I can at least suggest you to modify your check code and include a check for non-empty Strings.
A possible approach could be:
import { check, Match } from 'meteor/check';
const nonEmptyString = Match.Where(str => typeof str === 'string' && str.length > 0);
which then can be used in check like so:
check(params, {
size: nonEmptyString,
section: nonEmptyString,
});
Even more strict checks
You may be even stricter with accepted parameters and reduce them to a subset of valid entries. For example:
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
Note, that this also helps you to avoid query logic based on the parameter. You can change the following code
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
to
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
because the method does anyway accept only one of large or small as parameters.
Fallback on undefined cursors in publications
Another pattern that can support you preventing publication errors is to call this.ready() if the collection returned no cursor (for whatever reason, better is to write good tests to prevent you from these cases).
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
Combined code example
Applying all of the above mentioned pattern would make your function look like this:
import { check, Match } from 'meteor/check';
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
});
Summary
I for myself found that with good check matches and this.ready() the problems with publications have been reduced to a minimum in my applications.

Resources