Zapier JS conditional statement - javascript

I'm noob at JS, trying to write an APP for zapier. I have a test auth function that I can't get to fail when bad info is sent in.
Here is the test function:
require('should');
const zapier = require('zapier-platform-core');
const App = require('../../index');
const appTester = zapier.createAppTester(App);
describe('Triggers - Get Groups', () => {
zapier.tools.env.inject();
it('should get an array', done => {
const bundle = {
authData: { api_key: process.env.API_KEY },
inputData: {}
};
appTester(App.triggers['getgroup'].operation.perform, bundle)
.then(results => {
results.includes('id');
done();
})
.catch(results);
});
});
If successfull, a sample return should look like this:
{"id":1815,"name":"New Contacts","count":2}
A failure looks like this:
{"RESPONSE":"FAIL","REASON":"Invalid API key"}
Here is the getgroup function:
// Trigger stub created by 'zapier convert'. This is just a stub - you will need to edit!
const { replaceVars } = require('../utils');
const getList = (z, bundle) => {
let url = 'https://path.to/apisite?action=getGroups&apiKey={{api_key}}';
url = replaceVars(url, bundle);
const responsePromise = z.request({ url });
return responsePromise.then(response => {
response.throwForStatus();
return z.JSON.parse(response.content);
});
};
module.exports = {
key: 'getgroup',
noun: 'Getgroup',
display: {
label: 'Get Groups',
description: 'Triggers when loaded to pull groups.',
hidden: true,
important: false
},
operation: {
inputFields: [
{
key: 'group',
label: 'Groupget',
type: 'string',
required: false
}
],
outputFields: [
{
key: 'count',
type: 'string'
},
{
key: 'id',
type: 'string',
label: 'groupid'
},
{
key: 'name',
type: 'string',
label: 'groupname'
}
],
perform: getList,
sample: { count: 243, id: 27806, name: 'New Contacts' }
}
};
When I test auth on Zapier's website, I'd like auth to fail, and return the "REASON"
How do I do this?

Related

Why is cache.readQuery returning null?

I have a project management application built with React and GraphQL for which the Github repo can be found here. One of the functionalities allows for deleting a project.
I am trying to update the cache when I delete an individual project.
const [deleteProject] = useMutation(DELETE_PROJECT, {
variables: { id: projectId },
update(cache, { data: { deleteProject } }) {
const { projects } = cache.readQuery({ query: GET_PROJECTS });
cache.writeQuery({
query: GET_PROJECTS,
data: {
projects: projects.filter(
(project) => project.id !== deleteProject.id
),
},
});
},
onCompleted: () => navigate("/"),
});
However, when I attempt to do so, I am getting the following error: Error: Cannot destructure property 'projects' of 'cache.readQuery(...)' as it is null
Can someone help me figure out what's going on? This is what the getProjects query looks like:
const GET_PROJECTS = gql`
query getProjects {
projects {
id
name
description
status
}
}
`;
Here is the root query:
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
projects: {
type: new GraphQLList(ProjectType),
resolve(parent, args) {
return Project.find();
},
},
project: {
type: ProjectType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return Project.findById(args.id);
},
},
clients: {
type: new GraphQLList(ClientType),
resolve(parent, args) {
return Client.find();
},
},
client: {
type: ClientType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return Client.findById(args.id);
},
},
},
});

TypeScript functional programming patterns for comfortable object construction?

I'm having a hard time finding examples (videos or blogs) of functional programming object construction patterns.
I recently encountered the below snipped builder pattern and I like the experience it provides for constructing an object with nesting. When it's a flatter object, I'd normally just use a simple object factory with an opts param to spread with defaults, but passing in a nested array of objects starts to feel messy.
Are there FP patterns that can help make composing an object with nesting like the below comfortable while allowing for calling some methods n times, such as addStringOption?
const data = new SlashCommandBuilder()
.setName('echo')
.setDescription('Replies with your input!')
.addStringOption(option =>
option.setName('input')
.setDescription('The input to echo back')
.setRequired(true)
)
.addStringOption(option =>
option.setName('category')
.setDescription('The gif category')
.setRequired(true)
.addChoices(
{ name: 'Funny', value: 'gif_funny' },
{ name: 'Meme', value: 'gif_meme' },
{ name: 'Movie', value: 'gif_movie' },
));
data ends up looking something like:
{
name: "echo",
description: "Replies with your input!",
options: [
{
name: "input",
description: "The input to echo back",
type: 7, // string option id
required: true,
choices: null,
},
{
name: "category",
description: "The gif category",
required: true,
type: 7,
choices: [
{ name: "Funny", value: "gif_funny" },
{ name: "Meme", value: "gif_meme" },
{ name: "Movie", value: "gif_movie" },
],
},
],
};
Below is what I'm playing around with. I'm still working on learning how to type them in TS so I'm sharing the JS.
Allowing for method chaining in the below snippet is maybe contorting FP too much make it like OOP, but I haven't found an alternative that makes construction flow nicely.
An alternative could be standalone builders each returning a callback that returns the updated state then pipe these builders together, but with some builders being callable n times it's hard to make and provide the pipe ahead of time and without the dot notation providing intellisense it seems harder to know what the available functions are to build with.
const buildCommand = () => {
// data separate from methods.
let command = {
permissions: ['admin'],
foo: 'bar',
options: [],
};
const builders = {
setGeneralCommandInfo: ({ name, description }) => {
command = { ...command, name, description };
// trying to avoid `this`
return builders;
},
setCommandPermissions: (...permissions) => {
command = { ...command, permissions };
return builders;
},
addStringOption: (callback) => {
const stringOption = callback(buildStringOption());
command = { ...command, options: [...command.options, stringOption] };
return builders;
},
// can validate here
build: () => command,
};
return builders;
};
const buildStringOption = () => {
let stringOption = {
choices: null,
type: 7,
};
const builders = {
setName: (name) => {
stringOption = { ...stringOption, name };
return builders;
},
setDescription: (description) => {
stringOption = { ...stringOption, description };
return builders;
},
addChoices: (choices) => {
stringOption = { ...stringOption, choices };
return builders;
},
build: () => stringOption,
};
return builders;
};
const command1 = buildCommand()
.setGeneralCommandInfo({ name: 'n1', description: 'd1' })
.setCommandPermissions('auditor', 'moderator')
.addStringOption((option) =>
option.setName('foo').setDescription('bar').build()
)
.addStringOption((option) =>
option
.setName('baz')
.setDescription('bax')
.addChoices([
{ name: 'Funny', value: 'gif_funny' },
{ name: 'Meme', value: 'gif_meme' },
])
.build()
)
.build();
console.log(command1);
Why not simply create and use data constructors?
const SlashCommand = (name, description, options) =>
({ name, description, options });
const StringOption = (name, description, required, type = 7, choices = null) =>
({ name, description, required, type, choices });
const Choice = (name, value) => ({ name, value });
const data = SlashCommand('echo', 'Replies with your input!', [
StringOption('input', 'The input to echo back', true),
StringOption('category', 'The gif category', true, undefined, [
Choice('Funny', 'gif_funny'),
Choice('Meme', 'gif_meme'),
Choice('Movie', 'gif_movie')
])
]);
console.log(data);
TypeScript Playground Example
You can't get more functional than this. Intellisense will also help you with the constructor arguments.

How do I get response data out of the axios mock file and into the test?

I’m having trouble with the placement of my response data for an axios mock. If I include the data in the mock file, the test passes. If I include it in the test with axiosMock.get.mockResolvedValue(); it doesn’t. I don’t want the response in the mock file because that file needs to work with all axios tests.
If I include my response in the axios mock file like this, things work as expected.
// __mocks__/axios.js
export default {
create: jest.fn(() => ({
get: jest.fn().mockResolvedValue({
data: {
data: [
{
label: 'Started',
value: 'started',
type: 'Milestone',
},
{
label: 'Detected',
value: 'detected',
type: 'Milestone',
},
{
label: 'Acknowledged',
value: 'acknowledged',
type: 'Milestone',
},
],
},
}),
interceptors: {
request: { use: jest.fn(), eject: jest.fn() },
response: { use: jest.fn(), eject: jest.fn() },
},
})),
get: jest.fn(),
};
My test file:
// Condition.spec.js
test('fetches and displays data', async () => {
axiosMock.get.mockResolvedValue();
const { container } = render(
<Condition {...props} />
);
await wait(() => expect(container.textContent).toContain('Current milestone is Acknowledged'));
expect(container).toBeDefined();
});
If I remove it from the mock file and place it in the test, it fails:
// __mocks__/axios.js
export default {
create: jest.fn(() => ({
get: jest.fn().mockResolvedValue(),
interceptors: {
request: { use: jest.fn(), eject: jest.fn() },
response: { use: jest.fn(), eject: jest.fn() },
},
})),
get: jest.fn(),
};
// Condition.spec.js
test('fetches and displays data', async () => {
axiosMock.get.mockResolvedValue({
data: {
data: [
{
label: 'Started',
value: 'started',
type: 'Milestone',
},
{
label: 'Detected',
value: 'detected',
type: 'Milestone',
},
{
label: 'Acknowledged',
value: 'acknowledged',
type: 'Milestone',
},
],
},
});
const { container } = render(
<Condition {...props} />
);
await wait(() => expect(container.textContent).toContain('Current milestone is Acknowledged'));
expect(container).toBeDefined();
});
How do I get my response data out of the mock file and into the test itself?
Try this -
import firehydrantAPI from '../../helpers/firehydrantAPI'
jest.mock('../../helpers/firehydrantAPI');
const data = {
"data":[
{
"label":"Started",
"value":"started",
"type":"Milestone"
},
{
"label":"Detected",
"value":"detected",
"type":"Milestone"
},
{
"label":"Acknowledged",
"value":"acknowledged",
"type":"Milestone"
}
]
}
firehydrantAPI.get.mockImplementation(() => Promise.resolve({ data }));

prisma2: how to fetch nested fields?

In prisma 1 I have used fragment to fetch the nested fields.
For example:
const mutations = {
async createPost(_, args, ctx) {
const user = await loginChecker(ctx);
const post = await prisma.post
.create({
data: {
author: {
connect: {
id: user.id,
},
},
title: args.title,
body: args.body,
published: args.published,
},
})
.$fragment(fragment);
return post;
},
};
but seems like in prisma2 it is not supported. because by running this on playground,
mutation CREATEPOST {
createPost(
title: "How to sleep?"
body: "Eat, sleep, repaet"
published: true
) {
title
body
published
author {
id
}
}
}
I am getting,
"prisma.post.create(...).$fragment is not a function",
The include option is used to eagerly load relations in Prisma.
Example from docs:
const result = await prisma.user.findOne({
where: { id: 1 },
include: { posts: true },
})
Assuming a user table with a one-to-many posts relation, this will return back the user object with the posts field as well.
Prisma also supports nesting as well, for example:
const result = await prisma.user.findOne({
where: { id: 1 },
include: {
posts: {
include: {
author: true,
}
},
},
})

How to make a custom route for users? And how to add hooks to it?

I'm trying to add a route /me to get user authenticated information. This is what I have at my files.
I've tried adding a route /me at users.services file, but I'm getting this error: "error: MethodNotAllowed: Method find is not supported by this endpoint."
I want to get response with a user object (based on token) to a GET method to route '/me'.
users.service.js
// Initializes the `users` service on path `/users`
const createService = require('feathers-sequelize');
const createModel = require('../../models/users.model');
const hooks = require('./users.hooks');
module.exports = function (app) {
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'users',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/users', createService(options));
app.use('/me', {
get(id, params) {
return Promise.resolve([
{
id: 1,
text: 'Message 1'
}
])
}
})
// Get our initialized service so that we can register hooks and filters
const service = app.service('users');
service.hooks(hooks);
};
users.hooks.js
const { authenticate } = require('#feathersjs/authentication').hooks;
const {
hashPassword, protect
} = require('#feathersjs/authentication-local').hooks;
module.exports = {
before: {
all: [ ],
find: [ authenticate('jwt') ],
get: [],
create: [ hashPassword() ],
update: [ hashPassword() ],
patch: [ hashPassword() ],
remove: []
},
after: {
all: [
// Make sure the password field is never sent to the client
// Always must be the last hook
protect('password')
],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
users.model.js
// See http://docs.sequelizejs.com/en/latest/docs/models-definition/
// for more of what you can do here.
const Sequelize = require('sequelize');
const DataTypes = Sequelize.DataTypes;
module.exports = function (app) {
const sequelizeClient = app.get('sequelizeClient');
const users = sequelizeClient.define('users', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
},
}, {
hooks: {
beforeCount(options) {
options.raw = true;
}
}
});
users.associate = function (models) { // eslint-disable-line no-unused-vars
// Define associations here
// See http://docs.sequelizejs.com/en/latest/docs/associations/
};
return users;
};
What you did through
app.use('/me', {
get(id, params) {
return Promise.resolve([
{
id: 1,
text: 'Message 1'
}
])
}
})
Was implement routes for /me/:id. The find method is what runs for the base route of /me.
I don't think a separate service is really necessary though. An easier solution would be to use a before all hook that changes the id if you are accessing /users/me:
module.exports = function() {
return async context => {
if(context.id === 'me') {
context.id = context.params.user._id;
}
}
}

Categories

Resources