SurveyJS Not Displaying - laravel

Trying to use surveyjs with laravel and vue, but when I try to get the survey from a api does not work.
I'm getting the data from a api in laravel controller.
surveyshow.vue
<template>
<div>
<survey :survey="survey"></survey>
</div>
</template>
created () {
...
let url = `/api/edit/i130`;
axios
.get(url)
.then(response => {
surveyJson = JSON.parse(response.data.data.json);
console.log(JSON.parse(response.data.data.json));
})
.catch(function (error) {
console.log(error);
});
this.survey = new SurveyVue.Model(surveyJson);
if I replace the variable using a constant works.
var surveyJson = {
pages: [
{
"name": "Address",
"title": "Address",
"questions": [
{
"type": "text",
"name": "address1",
"title": "Street Address",
"autocompleteAs": "placeautocomplete"
}, {
"type": "text",
"name": "address2",
"title": "Address Line 2"
}
]
}
]
};

You have a problem with the asynchronous aspect of your code. There is a callback function, which runs when the request to your api completes:
...
.then(response => {
surveyJson = JSON.parse(response.data.data.json);
console.log(JSON.parse(response.data.data.json));
})
...
but you are trying to load the survey json "outside" of it. In other words, this runs before the api request's callback has had a chance to load the survey's JSON:
...
this.survey = new SurveyVue.Model(surveyJson);
...
You should move the line that instantiates your survey inside the callback, like this:
...
.then(response => {
surveyJson = JSON.parse(response.data.data.json);
window.survey = new SurveyVue.Model(surveyJson);
})
...

Related

Vue3 apollo displaying query results using composition API

So I am using vue router and trying to display the graphql results on my page.
Here is the router link on index.js
{
path: '/clients/:id',
name: 'client_profile',
component: () => import('../views/client_profile.vue')
},
The link to the page is from a table with this code using the router-link, where an id is passed as a param:
<router-link :to="{ name:'client_profile', params: { id: data.id }}"> {{data.name}}
</router-link>
The dynamic link looks like this on the setup(), where the useQuery loads my query clientEntity:
setup(){
const route = useRoute(),
id = computed(() => route.params.id)
const { result } = useQuery(clientEntity,{
id: id.value,
})
const client = computed(() => result.value?.entities[0])
return{
client,
result
}
}
On the HTML if I put in {{client}} or {{result}} I get the following:
//results
{ "entities": [ { "address": "555 Fake Street", "name": "Test1", "notes": "", "phone": null } ] }
//client
{ "address": "555 Fake Street", "name": "Test1", "notes": "", "phone": null }
But if I try accessing some of the data like {{client.address}} or {{client.name}} the page turns blank and I get the following error:
[Vue warn]: Unhandled error during execution of render function
at <BaseTransition appear=false persisted=false mode=undefined ... >
at <Transition name="p-toggleable-content" >
at <Panel header="Description" toggleable=true style=
Object { "text-align": "left" }
>
at <ClientProfile onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > >
at <RouterView>
at <App>
at <App>

How to pass variables into a graphql query?

I am using the storefront API to create a headless e commerce site. I am having an issue passing variables into the query - where if I hard code the values it seems to work but as soon as I try and pass variables into the query it fails and says
Argument 'lineItems' on InputObject 'CheckoutCreateInput' has an invalid value ([[object, Object]]). Expected type '[CheckoutLineItemInput!]'
I am pretty sure the problem has to do with the passing of the variables becuase I have tried most things - and come to this conclusion.
Here is the query and action function
export const createCheckout = async (items: IProductVariant[]) => {
const query = `
mutation {
checkoutCreate(input:
lineItems: ${items}
}) {
checkout {
id
webUrl
lineItems(first: 5) {
edges {
node {
title
quantity
}
}
}
}
}
}
`;
export const action: ActionFunction = async ({request}) => {
// get the form data from the POST
const formData = await request.formData()
const id = Array.from(formData.getAll('id'))
const quantity = Array.from(formData.getAll('quantity'))
let items = id.map((item , idx) => {
const newObj: IProductVariant = {}
newObj["variantId"] = item as string
newObj["quantity"] = Number(quantity[idx]) as number
return newObj
})
const res = await createCheckout(items)
return {res}
}
I think i need to modify the query to take a variable ?
Hmm.. I'm not sure that's how you call the GraphQL end point.
https://shopify.dev/api/storefront/2022-01/mutations/checkoutCreate
import Shopify from '#shopify/shopify-api';
const client = new Shopify.Clients.Storefront('your-development-store.myshopify.com', storefrontAccessToken);
// define your mutation (note how you define the input type first, then call
// the mutation with the input variable
const query =
`mutation checkoutCreate($input: CheckoutCreateInput!) {
checkoutCreate(input: $input) {
checkout {
id
webUrl
lineItems(first: 5) {
edges {
node {
title
quantity
}
}
}
}
}
}`;
// define the input data (this is from the docs, pass what is needed here)
const input = {
{
"allowPartialAddresses": true,
"buyerIdentity": {
"countryCode": ""
},
"customAttributes": [
{
"key": "",
"value": ""
}
],
"email": "",
"lineItems": [
{
"customAttributes": {
"key": "",
"value": ""
},
"quantity": 1,
"variantId": ""
}
],
"note": "",
"presentmentCurrencyCode": "",
"shippingAddress": {
"address1": "",
"address2": "",
"city": "",
"company": "",
"country": "",
"firstName": "",
"lastName": "",
"phone": "",
"province": "",
"zip": ""
}
};
// this is where you call the API passing in the query and variables
const data = await client.query({
data: {
"query": query,
"variables": {
"input": input,
"queueToken": queueToken
},
},
});

UpdateActivity throws 400 "Unknown activity type"

I do have a Bot that is reachable via MS Teams. The bot sends an Adaptive Card with some Text and a submit-action. When the user clicks on this submit-action, I want to proceed the input and then update the prior sent Adaptive card via calling context.updateActivity. According to documentation, I can use activity.Id = turnContext.Activity.ReplyToId; to specify the message I want to update. But the call of context.updateActivity results in a 400 HTTP error, the message is "Unknown activity type".
Some investigation:
This error occurs when I want to send another Adaptive Card and when I want to send plain text
I verified, that the id of sendActivity is the same as turnContext.Activity.ReplyToId
Any idea?
Here is my code:
Adaptive Card
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "Some text",
"wrap": true
},
{
"type": "Input.ChoiceSet",
"id": "Feedback",
"value": "",
"style": "compact",
"placeholder": "Wie hilfreich war diese Antwort?",
"choices": [
{
"title": "⭐",
"value": "1"
},
{
"title": "⭐⭐",
"value": "2"
},
{
"title": "⭐⭐⭐",
"value": "3"
},
{
"title": "⭐⭐⭐⭐",
"value": "4"
},
{
"title": "⭐⭐⭐⭐⭐",
"value": "5"
}
]
}
],
"actions": [
{
"title": "Feedback absenden",
"type": "Action.Submit"
}
]
}
Sending the message:
private handleMessage = async (context: TurnContext, next: () => Promise<void>): Promise<void> => {
const adaptiveCard = AdaptiveCardFactory.createAdaptiveCardFromTemplateAndData(AnswerWithFeedbackCard);
const result = await context.sendActivity({ attachments: [adaptiveCard] });
console.error("send msg with id " + result?.id);
}
code to update the message:
private handleMessage = async (context: TurnContext, next: () => Promise<void>): Promise<void> => {
console.error("received msg with id " + context.activity.replyToId);
if (context.activity.value && !context.activity.text) {
const updatedCard = CardFactory.adaptiveCard(this.botConfig.updatedCard);
await context.updateActivity({ text: "updated :)", id: context.activity.replyToId});
//or
await context.updateActivity({ attachments: [updatedCard], id: context.activity.replyToId});
}
}
Got it!
const att = AdaptiveCardFactory.createAdaptiveCardFromTemplateAndData(AnswerWithAnsweredFeedbackCard, {
answer: "Mir geht's super, danke der Nachfrage!",
starsCount: context.activity.value.Feedback
});
const id = await context.updateActivity( { attachments: [ att ], id: context.activity.replyToId} );
This does not work, but this does:
const att = AdaptiveCardFactory.createAdaptiveCardFromTemplateAndData(AnswerWithAnsweredFeedbackCard, {
answer: "Mir geht's super, danke der Nachfrage!",
starsCount: context.activity.value.Feedback
});
const msg = MessageFactory.attachment( att )
msg.id = context.activity.replyToId;
const id = await context.updateActivity( msg )
So, you need to save the save the sent msg in a variable and set the id of this variable instead of using the "inplace"-implementation of updateActivity

Creating a custom adapter in Botframework for Actions on Google

I'm currently writing a custom adapter in Typescript to connect Google Assistant to Microsoft's Botframework. In this adapter I'm attempting to capture the Google Assistant conversation object through a webook call and change it using my bot.
At this moment the only thing that my bot is doing is receive the request from Actions on Google and parsing the request body into an ActionsOnGoogleConversation object. After this I call conv.ask() to try a simple conversation between the two services.
Api Endpoint:
app.post("/api/google", (req, res) => {
googleAdapter.processActivity(req, res, async (context) => {
await bot.run(context);
});
});
Adapter processActivity function:
public async processActivity(req: WebRequest, res: WebResponse, logic: (context: TurnContext) => Promise<void>): Promise<void> {
const body = req.body;
let conv = new ActionsSdkConversation();
Object.assign(conv, body);
res.status(200);
res.send(conv.ask("Boo"));
};
When I try to start the conversation I get the following error in the Action on Google console.
UnparseableJsonResponse
API Version 2: Failed to parse JSON response string with
'INVALID_ARGUMENT' error: "availableSurfaces: Cannot find field." HTTP
Status Code: 200.
I've already checked the response and I can find a field called availableSurfaces in the AoG console and when I call my bot using Postman.
Response:
{
"responses": [
"Boo"
],
"expectUserResponse": true,
"digested": false,
"noInputs": [],
"speechBiasing": [],
"_responded": true,
"_ordersv3": false,
"request": {},
"headers": {},
"_init": {},
"sandbox": false,
"input": {},
"surface": {
"capabilities": [
{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
},
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.ACCOUNT_LINKING"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
}
]
},
"available": {
"surfaces": {
"list": [],
"capabilities": {
"surfaces": []
}
}
},
"user": {
"locale": "en-US",
"lastSeen": "2019-11-14T12:40:52Z",
"userStorage": "{\"data\":{\"userId\":\"c1a4b8ab-06bb-4270-80f5-958cfdff57bd\"}}",
"userVerificationStatus": "VERIFIED"
},
"arguments": {
"parsed": {
"input": {},
"list": []
},
"status": {
"input": {},
"list": []
},
"raw": {
"list": [],
"input": {}
}
},
"device": {},
"screen": false,
"body": {},
"version": 2,
"action": "",
"intent": "",
"parameters": {},
"contexts": {
"input": {},
"output": {}
},
"incoming": {
"parsed": []
},
"query": "",
"data": {},
"conversation": {
"conversationId": "ABwppHEky66Iy1-qJ_4g08i3Z1HNHe2aDTrVTqY4otnNmdOgY2CC0VDbyt9lIM-_WkJA8emxbMPVxS5uutYHW2BzRQ",
"type": "NEW"
},
"inputs": [
{
"intent": "actions.intent.MAIN",
"rawInputs": [
{
"inputType": "VOICE",
"query": "Talk to My test app"
}
]
}
],
"availableSurfaces": [
{
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
},
{
"name": "actions.capability.WEB_BROWSER"
}
]
}
]
}
Does anyone know what might be causing this? I personally feel that creating the ActionsSdkConversation could be the cause, but I've not found any examples of using Google Assistant without getting the conv object from the standard intent handeling setup.
So I managed to fix it by changing the approach, instead of having an API point that fits the structure of bot framework I changed it to the intenthandler of AoG.
Google controller
export class GoogleController {
public endpoint: GoogleEndpoint;
private adapter: GoogleAssistantAdapter;
private bot: SampleBot;
constructor(bot: SampleBot) {
this.bot = bot;
this.adapter = new GoogleAssistantAdapter();
this.endpoint = actionssdk();
this.setupIntents(this.endpoint);
};
private setupIntents(endpoint: GoogleEndpoint) {
endpoint.intent(GoogleIntentTypes.Start, (conv: ActionsSdkConversation) => {
this.sendMessageToBotFramework(conv);
});
endpoint.intent(GoogleIntentTypes.Text, conv => {
this.sendMessageToBotFramework(conv);
});
};
private sendMessageToBotFramework(conv: ActionsSdkConversation) {
this.adapter.processActivity(conv, async (context) => {
await this.bot.run(context);
});
};
};
interface GoogleEndpoint extends OmniHandler, BaseApp , ActionsSdkApp <{}, {}, ActionsSdkConversation<{}, {}>> {};
Once the conv object was in the adapter, I used the conv object to create an activity which the bot used to do its things and saved it in state using context.turnState()
Adapter ProcessActivity
public async processActivity(conv: ActionsSdkConversation, logic: (context: TurnContext) => Promise<void>): Promise<ActionsSdkConversation> {
const activty = this.createActivityFromGoogleConversation(conv);
const context = this.createContext(activty);
context.turnState.set("httpBody", conv);
await this.runMiddleware(context, logic);
const result = context.turnState.get("httpBody");
return result;
};
Bot
export class SampleBot extends ActivityHandler {
constructor() {
super();
this.onMessage(async (context, next) => {
await context.sendActivity(`You said: ${context.activity.text}`);
await next();
});
}
Once the bot send a response, I used the result to modify the conv object, save it and then return it in processActivity().
private createGoogleConversationFromActivity(activity: Partial<Activity>, context: TurnContext) {
const conv = context.turnState.get("httpBody");
if (activity.speak) {
const response = new SimpleResponse({
text: activity.text,
speech: activity.speak
});
conv.ask(response);
} else {
if (!activity.text) {
throw Error("Activity text cannot be undefined");
};
conv.ask(activity.text);
};
context.turnState.set("httpBody", conv);
return;
};
That resulted into a simple conversation between Google Assistant and Bot Framework.

GraphQL resolver with DataLoader and nested endpoints

Adapting the example of GraphQL best practices created by the Apollo Team (https://github.com/apollographql/GitHunt-API/tree/master/api), I'm having hard time to come up with a resolver that would result in a list of Person using DataLoaders.
Here's an example of the api (data from: https://github.com/steveluscher/zero-to-graphql/tree/master/zero-node)
Given the output of /people/ endpoint like:
{
"people": [
{
"username": "steveluscher",
"id": "1",
},
{
"username": "aholovaty",
"id": "2",
},
{
"username": "swillison",
"id": "3",
},
{
"username": "gvr",
"id": "4",
}
]
}
And a person from the endpoint /people/1/
{
"person": {
"last_name": "Luscher",
"username": "steveluscher",
"friends": [
"/people/2/",
"/people/3/"
],
"id": "1",
"email": "steveluscher#fb.com",
"first_name": "Steven"
}
I would like to have a resolver what would give me a list of Person like:
[
{
"person": {
"last_name": "Luscher",
"username": "steveluscher",
"friends": [
"/people/2/",
"/people/3/"
],
"id": "1",
"email": "steveluscher#fb.com",
"first_name": "Steven"
}
},
{
"person": {
"last_name": "Holovaty",
"username": "aholovaty",
"friends": [
"/people/1/",
"/people/4/"
],
"id": "2",
"email": "a.holovaty#django.com",
"first_name": "Adrian"
}
},
...
]
This is what I got so far:
server.js
import { ApiConnector } from './api/connector';
import { People } from './api/models';
import schema from './schema';
export function run() {
const PORT = 3000;
const app = express();
app.use(bodyParser.json());
app.use('/graphql', graphqlExpress((req) => {
const query = req.query.query || req.body.query;
if (query && query.length > 2000) {
throw new Error('Query too large.');
}
const apiConnector = new ApiConnector();
return {
schema,
context: {
People: new People({ connector: apiConnector }),
},
};
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
}));
const server = createServer(app);
server.listen(PORT, () => {
console.log(`API Server is now running on http://localhost:${PORT}`);
});
return server;
}
models.js
export class People {
constructor({ connector }) {
this.connector = connector;
}
getPeople() {
return this.connector.get(`/people/`);
}
getPerson(id) {
return this.connector.get(`/people/${id}/`);
}
}
connector.js
const API_ROOT = 'http://localhost:8080';
export class ApiConnector {
constructor() {
this.rp = rp;
this.loader = new DataLoader(this.fetch.bind(this));
}
fetch(urls) {
const options = {
json: true,
resolveWithFullResponse: true,
headers: {
'user-agent': 'Request-Promise',
},
};
return Promise.all(urls.map((url) => {
return new Promise((resolve) => {
this.rp({
uri: url,
...options,
}).then((response) => {
const body = response.body;
resolve(body);
}).catch((err) => {
console.error(err);
resolve(null);
});
});
}));
}
get(path) {
return this.loader.load(API_ROOT + path);
}
And the resolver in the schema would have something like:
const rootResolvers = {
Query: {
people(root, args, context) {
return context.People.getPeople();
},
person(root, { id }, context) {
return context.People.getPerson(id)
}
},
};
Until now I can get the first endpoint /people/ and a person from /people/id/. But how to change it to have a list of person? I'm not quite sure how/where should this code be.
Thanks a lot!
You could change your people resolver to something like the code bellow:
const rootResolvers = {
Query: {
people(root, args, context) {
const list = context.People.getPeople();
if (list && list.length > 0) {
return list.map(item => context.People.getPerson(item.id))
}
},
...
},
};
Ps: You said that you are using dataLoader, so i think your API calls is just being cached, but if it is not the case, you need to implement some cache to avoid calling same endpoints a lot times.

Resources