I'm new in Seneca. I have been trying to make two microservices to communicate each other but I keep failing and get this errors:
Error: Response Error: 404 Not Found
at module.exports.internals.Utils.internals.Utils.handle_response (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\seneca-transport\lib\transport-utils.js:71:11)
at c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\seneca-transport\lib\http.js:154:25
at read (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\wreck\lib\index.js:590:24)
at finish (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\wreck\lib\index.js:398:20)
at wrapped (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\hoek\lib\index.js:879:20)
at module.exports.internals.Recorder.onReaderFinish (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\wreck\lib\index.js:449:16)
at Object.onceWrapper (events.js:313:30)
at emitNone (events.js:111:20)
at module.exports.internals.Recorder.emit (events.js:208:7)
at finishMaybe (_stream_writable.js:614:14)
=== SENECA FATAL ERROR === MESSAGE: ::: seneca: Action failed: Response Error: 404 Not Found. CODE: ::: act_execute INSTANCE :::
Seneca/pcbyi7v5c76v/1534346071465/6536/3.7.0/- DETAILS ::: {
message: 'Response Error: 404 Not Found',
pattern: '',
fn: { [Function: transport_client] id: 'host:127.0.0.2,pg:,port:8080' },
callback:
{ [Function: bound action_reply]
seneca:
Seneca {
'private$':
{ act:
{ parent:
{ start: 1534346071559,
end: 1534346071561, and more...
this is my code:
orderIndex.ts
{
const orderPlugin = require('./orderManagerPlugin');
const express = require('express');
const SenecaWeb = require('seneca-web');
const seneca = require("seneca")();
let bodyParser = require('body-parser');
var Routes = [{
prefix: '/orders',
pin: 'area:order,action:*',
map: {
fetch: { GET: true },
create: { GET: false, POST: true },
delete: { GET: false, DELETE: true },
}
}]
var config = {
routes: Routes,
adapter: require('seneca-web-adapter-express'),
context: express().use(bodyParser.urlencoded({ 'extended': 'true' })).use(bodyParser.json()),
options: {parseBody: false}
}
seneca.use(SenecaWeb,config);
seneca.use( orderPlugin );
seneca.ready(function (err) {
const app = seneca.export('web/context')();
app.listen({ host: "127.0.0.4", port: 8081 });
});
}
orderPlugin.ts
{
var plugin = function orderPlugin(options) {
var seneca = this;
var senecaEmailer;
seneca.add({ area: "order", action: "fetch" }, function (args,
done) {
var orders = this.make("orders");
orders.list$({ id: args.id }, done);
});
seneca.add({ area: "order", action: "delete" }, function (args,
done) {
var orders = this.make("orders");
orders.remove$({ id: args.id }, function (err) {
done(err, null);
});
});
seneca.add({ area: "order", action: "create" }, function (args,
done) {
console.log('create order');
senecaEmailer.act( 'role:web', {area: 'email', action:'send'} , done);
});
this.add( { init: "orderPlugin" }, function (args, done) {
senecaEmailer = require("seneca")().client({ host: "127.0.0.2", port: 8080 });
done();
});
}
module.exports = plugin;
}
emailIndex.ts
{
const mailPlugin = require('./emailingPlugin');
const express = require('express');
const SenecaWeb = require('seneca-web');
const seneca = require("seneca")();
let bodyParser = require('body-parser');
var Routes = [{
prefix: '/emails',
pin: 'area:email, action:*',
map: {
send: { GET: true },
}
}]
var config = {
routes: Routes,
adapter: require('seneca-web-adapter-express'),
context: express().use(bodyParser.urlencoded({ 'extended': 'true' })).use(bodyParser.json()),
options: {parseBody: false}
}
seneca.use(SenecaWeb,config);
seneca.use( mailPlugin );
seneca.ready(function (err) {
const app = seneca.export('web/context')();
app.listen({ host: "127.0.0.2", port: 8080 } );
});
}
emailPlugin.ts
{
import {EmailService} from './emailService';
var plugin = function emailPlugin(options) {
var seneca = this;
let mailer :EmailService ;
seneca.add({area: "email", action: "send"}, function(args, done) {
mailer.sendMail('guzon56#gmail.com', done);
});
this.add( { init: "emailPlugin" }, function (args, done) {
console.log('before init');
mailer = require('./emailService')();
console.log('after init');
done();
});
};
module.exports = plugin;
}
please help me.
Tnx.
Seneca is explained by Richard Rodger in this post. The chapter "Service Discovery" talks about meshing the microservices in a network.
For my applications I use the seneca-mesh plugin. This plugin README says:
To join the network, all a service has to do is contact one other
service already in the network. The network then shares information
about which services respond to which patterns. There is no need to
configure the location of individual services anywhere.
Reading Richard's post and the plugin documentation could be a good starting point for your project. Hope it helps!
Related
I'm currently trying to implement pagination on my posts.
Using Apollo graphql here is my useQuery
const { data: postsData, fetchMore } = useQuery(POSTS_BY_USER_DRAFT, {
fetchPolicy: 'network-only',
variables: {
user: user.id,
start: 0,
limit: limit
},
onCompleted: () => {
setTotal(postsData[model].meta.pagination.total)
}})
and here is my onClick handler for fetching more posts
const loadMorePosts = async () => {
const nextStart = start + limit
setStart(nextStart);
await fetchMore({
variables: {
user: user.id,
offset: nextStart,
limit: limit,
},
updateQuery: (prevResult, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return prevResult
}
const prevData = prevResult[model].data
const moreData = fetchMoreResult[model].data
fetchMoreResult[model].data = [...prevData, ...moreData]
// fetchMoreResult[model].data = [...moreData]
return fetchMoreResult
},
})}
My queries are successful as I do get correctly the data, however postsData does not get updated
[NOTICED]: If I switch fetchMoreResult[model].data = [...prevData, ...moreData] for
fetchMoreResult[model].data = [...moreData] my postsData does get updated.
I have tried return { ...fetchMoreResult } and multiple ways of returning data fearing an immutability/comparaison issue but it does not seem to do the job.
I'm not sure why, but setting a fetchPolicy for Apollo will do the job
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Publication: {
merge: true,
},
Post: {
merge: true,
},
},
}),
defaultOptions: defaultOptions,
})
as per the title, I am having problem trying to enable graphql subscription in my loopback 4 application.
Here is my code that I've done so far.
index.ts
export async function main(options: ApplicationConfig = {}) {
const app = new BackendLb4Application(options)
await app.boot()
await app.start()
const url = app.restServer.url;
const oas: Oas3 = <Oas3><unknown>await app.restServer.getApiSpec()
const {schema} = await createGraphQLSchema(oas, {
operationIdFieldNames: true,
baseUrl: url,
createSubscriptionsFromCallbacks: true,
})
const handler = graphqlHTTP( (request:any, response:any, graphQLParams: any) => ({
schema,
pretty: true,
graphiql: true
}))
app.mountExpressRouter(graphqlPath, handler);
const pubsub = new PubSub()
const ws = createServer(app);
ws.listen(PORT, () => {
new SubscriptionServer(
{
execute,
subscribe,
schema,
onConnect: (params: any, socket: any, ctx: any) => {
console.log(params, 'here on onconnect')
// Add pubsub to context to be used by GraphQL subscribe field
return { pubsub }
}
},
{
server: ws,
path: '/subscriptions'
}
)
})
return app
}
Here is my schema
type Subscription {
"""
Equivalent to PATCH onNotificationUpdate
"""
postRequestQueryCallbackUrlApiNotification(secondInputInput: SecondInputInput): String
"""
Equivalent to PATCH onNotificationUpdate
"""
postRequestQueryCallbackUrlOnNotificationUpdate(firstInputInput: FirstInputInput): String
}
Here is an example of my controller
#patch('/notification-update', {
operationId: 'notificationUpdate',
description: '**GraphQL notificationUpdate**',
callbacks:[ {
onNotificationUpdate: {
//'{$request.query.callbackUrl}/onNotificationUpdate': {
post: {
requestBody: {
operationId: 'notificationUpdateCallback',
description: 'rasjad',
content: {
'application/json': {
schema: {
title: "firstInput",
type: 'object',
properties: {
userData: {
type: "string"
}
}
}
}
}
},
responses: {
'200': {
description: 'response to subscription',
}
}
}
},
// }
}],
responses: {
'200': {
description: 'Notification PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Notification, {partial: true}),
},
},
})
notification: Notification,
#param.where(Notification) where?: Where<Notification>,
): Promise<Count> {
return this.notificationRepository.update(notification, where);
}
Ive defined the callbacks object in my controller which will then create a subscription in my schema. Tested it out on graphiql but did not work.
I am not sure where to go from here. Do I need a custom resolver or something? Not sure.
Appreciate it if anyone could help on this.
Just in case someone else is looking to do the same thing.
I switched out graphqlHTTP with Apollo Server to create my graphql server.
So my final index.ts looks like this.
export async function main(options: ApplicationConfig = {}) {
const lb4Application = new BackendLb4Application(options)
await lb4Application.boot()
await lb4Application.migrateSchema()
await lb4Application.start()
const url = lb4Application.restServer.url;
const graphqlPath = '/graphql'
// Get the OpenApiSpec
const oas: Oas3 = <Oas3><unknown>await lb4Application.restServer.getApiSpec()
// Create GraphQl Schema from OpenApiSpec
const {schema} = await createGraphQLSchema(oas, {
strict: false,
viewer: true,
baseUrl: url,
headers: {
'X-Origin': 'GraphQL'
},
createSubscriptionsFromCallbacks: true,
customResolvers: {
"lb4-title": {
"your-path":{
patch: (obj, args, context, info) => {
const num = Math.floor(Math.random() * 10);
pubsub.publish("something", { yourMethodName: {count: num} }).catch((err: any) => {
console.log(err)
})
return {count: 1}
}
}
}
},
customSubscriptionResolvers: {
"lb4-title" : {
"yourMethodName": {
post: {
subscribe: () => pubsub.asyncIterator("something"),
resolve: (obj: any, args: any, context, info) => {
console.log(obj, 'obj')
}
}
}
}
}
})
const app = express();
const server = new ApolloServer({
schema,
plugins: [{
async serverWillStart() {
return {
async drainServer() {
subscriptionServers.close();
}
};
}
}],
})
const subscriptionServers = SubscriptionServer.create(
{
// This is the `schema` we just created.
schema,
// These are imported from `graphql`.
execute,
subscribe,
},
{
server: lb4Application.restServer.httpServer?.server,
path: server.graphqlPath,
//path: server.graphqlPath,
}
);
await server.start();
server.applyMiddleware({ app, path: "/" });
lb4Application.mountExpressRouter('/graphql', app);
return lb4Application
}
Also you will need to define the callbacks object in your controller like so.
#patch('/something-update', {
operationId: 'somethingUpdate',
description: '**GraphQL somethingUpdate**',
callbacks:[
{
yourMethodName: {
post: {
responses: {
'200': {
description: 'response to subscription',
content: {'application/json': {schema: CountSchema}},
}
}
}
},
}
],
responses: {
'200': {
description: 'Something PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Something, {partial: true}),
},
},
})
something: Something,
#param.where(Something) where?: Where<Something>,
): Promise<Count> {
return this.somethingRepository.updateAll(something, where);
}
And that is it. You can test it out from the GraphQL Playground and play around with the subscriptions.
For the time being, I am fine with defining customResolvers and customSubscriptionResolvers but I'm pretty sure I can automate this two objects from the controllers.
Cheers!
Packages:
redux-observable#2.0.0-rc.2
rxjs latest
universal-rxjs-ajax dev branch
next-redux-wrapper latest
next.js latest
I have a simple Page with getStaticProps:
export const getStaticProps = wrapper.getStaticProps((store) => async (ctx) => {
store.dispatch({ type: 'ADD_DATA' });
// const response = await fetch('https://rickandmortyapi.com/api');
// const data = await response.json();
// store.dispatch({ type: 'SERVER_ACTION', payload: data.characters });
return {
props: {},
};
});
Action 'ADD_DATA' triggers action 'SERVER_ACTION':
export const AddDataEpic: Epic = (action$) =>
action$.pipe(
ofType('ADD_DATA'),
mergeMap((action) =>
request({ url: 'https://rickandmortyapi.com/api' }).pipe(
map((response) => {
return {
type: 'SERVER_ACTION',
payload: response.response.characters,
};
})
)
)
);
Inside the reducer in the case 'SERVER_ACTION': clause I receive the payload:
const server = (state: State = { data: null }, action: AnyAction) => {
switch (action.type) {
case HYDRATE: {
console.log('HYDRATE >', action.payload); // logs out "HYDRATE > { server: { data: null } }"
return {
...state,
...state.server,
...action.payload.server,
};
}
case 'SERVER_ACTION': {
console.log('SERVER_ACTION >', action.payload); // logs out "SERVER_ACTION > https://rickandmortyapi.com/api/character"
return {
...state,
...state.server,
data: action.payload,
};
}
default:
return state;
}
};
But the payload isn't passed to HYDRATE action:
console.log('HYDRATE >', action.payload); // logs out "HYDRATE > { server: { data: null } }"
If I dispatch the 'SERVER_ACTION' action from inside the getStaticProps:
export const getStaticProps = wrapper.getStaticProps((store) => async (ctx) => {
// store.dispatch({ type: 'ADD_DATA' });
const response = await fetch('https://rickandmortyapi.com/api');
const data = await response.json();
store.dispatch({ type: 'SERVER_ACTION', payload: data.characters });
return {
props: {},
};
});
The HYDRATE action inside the reducer receive the payload:
HYDRATE > { server: { data: 'https://rickandmortyapi.com/api/character' } }
I don't understand what's wrong with my code.
May it be a bug in one of the libraries? Or is it a mistake in my code?
If anyone has any suggestions, PLEASE
#PYTHON DEVELOPER999 It might be due to the latest update on next-redux-wrapper, there are few migration steps =>
https://github.com/kirill-konshin/next-redux-wrapper#upgrade-from-6x-to-7x
I'm fairly new to GraphQL and currently familiarizing myself by making a quiz application using React on the front-end.
At the moment, I'm busy with my back-end. After successfully setting up queries and mutations, I am finding it difficult to get subscriptions working. When using GraphiQL, I am getting null as an output instead of "Your subscription data will appear here..."
Queries and the mutation for adding the quiz works.
The entry point of my server, app.js:
const express = require("express");
const mongoose = require("mongoose");
const { graphqlHTTP } = require("express-graphql");
const schema = require("./graphql/schema");
const cors = require("cors");
const port = 4000;
//Subscriptions
const { createServer } = require("http");
const { SubscriptionServer } = require("subscriptions-transport-ws");
const { execute, subscribe } = require("graphql");
const subscriptionsEndpoint = `ws://localhost:${port}/subscriptions`;
const app = express();
app.use(cors());
mongoose.connect("mongodb://localhost/quizify", {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false
});
mongoose.connection.once("open", () => console.log("connected to database"));
app.use("/graphql", graphqlHTTP({
schema,
graphiql: true,
subscriptionsEndpoint,
}));
const webServer = createServer(app);
webServer.listen(port, () => {
console.log(`GraphQL is now running on http://localhost:${port}`);
//Set up the WebSocket for handling GraphQL subscriptions.
new SubscriptionServer({
execute,
subscribe,
schema
}, {
server: webServer,
path: '/subscriptions',
});
});
Below is from the schema definitions, schema.js:
const { graphqlHTTP } = require("express-graphql");
const graphql = require("graphql");
const { PubSub } = require("graphql-subscriptions");
const pubsub = new PubSub();
//Import of Mongoose Schemas:
const Quiz = require("../models/quiz");
const {
GraphQLObjectType,
GraphQLList,
GraphQLSchema,
GraphQLNonNull,
GraphQLID,
GraphQLString,
GraphQLBoolean
} = graphql;
const QuizType = new GraphQLObjectType({
name: "Quiz",
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
questions: {
type: new GraphQLList(QuestionType),
resolve(parent, args) {
return Question.find({ quizId: parent.id });
}
},
creator: {
type: UserType,
resolve(parent, args) {
return User.findById(parent.creatorId);
}
}
})
});
const NEW_QUIZ_ADDED = "new_quiz_added";
const Subscription = new GraphQLObjectType({
name: "Subscription",
fields: {
quizAdded: {
type: QuizType,
subscribe: () => {
pubsub.asyncIterator(NEW_QUIZ_ADDED);
},
},
}
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
createQuiz: {
type: QuizType,
args: {
title: { type: new GraphQLNonNull(GraphQLString) },
creatorId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve(parent, args) {
const newQuiz = new Quiz({ //Quiz imported from Mongoose schema.
title: args.title,
creatorId: args.creatorId,
});
pubsub.publish(NEW_QUIZ_ADDED, { quizAdded }); //NEW_QUIZ_ADDED - a constant defined above for easier referencing.
return newQuiz.save();
}
},
},
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation,
subscription: Subscription,
});
I've looked around, however I'm not finding an method that works for this kind of site. I know it might sound like a simple problem. Any assistance would be greatly appreciated!
I've done this simple test
// Mocks
const loginResponseData: LoginResponseDto = {
auth: { id: 1, username: 'me', roles: [] },
token: {
token: 'abc',
expirationEpochSeconds: 12345
}
};
describe('AuthReducer', () => {
describe('loginSuccess', () => {
it('should show loginResponseData state', () => {
const createAction = loginSuccess({ payload: loginResponseData });
const result = reducer(initialState, createAction);
console.log('AUTH', result);
// How Can I test this?
//expect(result).toEqual(loginResponseData);
});
});
});
export const initialState: State = {
error: null,
loading: false,
registered: false,
payload: null
};
const authReducer = createReducer(
initialState,
on(AuthActions.loginSuccess, (state, { payload }) => {
return {
...state,
error: null,
loading: false,
payload
};
})
);
How I can test result with loginResponseData?
result of a reducer is a new state.
You need to share code of your reducer for the right answer. Or to share what console.log outputs.
Because in your question the code is correct
describe('AuthReducer', () => {
describe('loginSuccess', () => {
it('should show loginResponseData state', () => {
const actionPayload: LoginResponseDto = {
auth: { id: 1, username: 'me', roles: [] },
token: {
token: 'abc',
expirationEpochSeconds: 12345
}
};
// for the test it's fine to have an empty object
const initialState: any = {
};
// check what should be changed
const expectedState = {
payload: {
auth: { id: 1, username: 'me', roles: [] },
token: {
token: 'abc',
expirationEpochSeconds: 12345
},
},
error: null,
loading: false,
};
const createAction = loginSuccess({ payload: loginResponseData });
// returns updated state we should compare against expected one.
const actualState = reducer(initialState, createAction);
// assertions
expect(actualState).toEqual(expectedState);
});
});
});