I'm using a third-party API (so far so good, I get the result), however as I'm new I'm having difficulty manipulating these results for the frontend (EJS)** ...
What the system should do is:
The site is a graphics panel, where I use the third-party API (it blocks the origin if consumed by the frontend) and I populate the data received in JSON, using the graphics plugin called (CHARTJS) .. The problem is there, because the API should be consumed as soon as the page is accessed and the graphics populated after consumption, but for me to consume this JSON I need to pass some QS ... These QSs when accessing the page for the first graphic are already automatic and can be manipulated by the form that contains the same page, in order to be able to carry out other queries ..
I am totally, lost, I would love some help and explanation ..
CONTROLLERS/LOGIN/LOGIN.JS
module.exports.index = (_application, _request, _response) => {
_response.render('login/index');
}
module.exports.check_login = (_application, _request, _response) => {
const JSON_MODEL = new _application.app.models.jsonDAO('fatVenda.xsjs', '\'BSC\'', '201807', 'vendaabs,vendam2');
JSON_MODEL.request();
console.log(JSON_MODEL.jsonData)
const VENDA_MODEL = new _application.app.models.vendaDAO('', '', '', '');
_response.render('home/home');
}
ROUTES/LOGIN/LOGIN.JS
module.exports = (_application) => {
_application.get('/login', (_request, _response) => {
_application.app.controllers.login.login.index(_application, _request, _response);
});
_application.post('/check_login', (_request, _response) => {
_application.app.controllers.login.login.check_login(_application, _request, _response);
});
}
MODELS/JSONDAO.JS
const REQUEST = require('request');
class JsonRequestDAO {
constructor(_xjs, _shoppingID, _periodUntil, _kpi){
this.xjs = _xjs;
this.shoppingID = _shoppingID;
this.periodUntil = _periodUntil;
this.kpi = _kpi;
}
request(){
REQUEST.get({
uri: `URL${this.xjs}`,
json: true,
qs: {
Shop: this.shoppingID,
PeriodoAte: this.periodUntil,
Kpi: this.kpi
},
headers: {
Authorization: 'Basic KEY',
ApiKey: 'KEY',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
}
}, (_error, _response) => {
(_response.statusCode == 200) ? _response.body : console.log(_error);
});
}
}
module.exports = () => { return JsonRequestDAO; }
MODELS/VENDADAO.JS
class VendaDAO {
constructor(_shoppingName, _yearMonth, _competence, _sale, _salePercentage, _saleSquareMeter, _salePercentageSquareMeter){
this.shoppingName = _shoppingName;
this.yearMonth = _yearMonth;
this.competence = _competence;
this.sale = _sale;
this.salePercentage = _salePercentage;
this.saleSquareMeter = _saleSquareMeter;
this.salePercentageSquareMeter = _salePercentageSquareMeter;
}
static get shoppingName() { return this.shoppingName; }
static get yearMonth() { return this.yearMonth; }
static get competence() { return this.competence; }
static get sale() { return this.sale; }
static get salePercentage() { return this.salePercentage; }
static get saleSquareMeter() { return this.saleSquareMeter; }
static get salePercentageSquareMeter() { return this.salePercentageSquareMeter; }
}
module.exports = () => {
return VendaDAO;
}
Related
I'm trying to unit test a chunk of code that looks something like the snippet below.
const getSubscriptions = {
options: {
async handler({query}) {
const workType = searchParams.get('workType');
return axios.get(url, {
headers: {
Authorization: `Bearer ${clientCredentials.access_token}`
}
}).then((response) => {
if (workType && workType.length > 0) {
if (workType === 'Unknown') {
response.data._embedded = response.data._embedded.filter((subscriptions) => !subscriptions.systemProperties.workType);
} else {
response.data._embedded = response.data._embedded.filter((subscriptions) => subscriptions.systemProperties.workType && subscriptions.systemProperties.workType === workType);
}
}
return response.data;
})
}
},
method: 'GET',
path: '/subscriptions'};
I'm seeing errors such as "cannot read property 'filter' of undefined" in the existing tests after adding the .filter usage. How can I properly mock this in my unit tests so that filter is recognized as a function? Currently the tests are passing in a data object that looks like:
data: [chance.object(), chance.object()]
I've tried editing this data object to include _embedded and then I get an error that .filter is not a function.
I'm new to writing unit tests for JavaScript, so I've been stuck on this for a while. Any help is appreciated. I'm using Mocha for testing, but can likely interpret a Jasmine or Jest solution as well.
Here is a trimmed down version of the test suite:
describe('Get subscriptions', () => {
const workType = chance.word();
const getSubscriptionsResponse = {
data: [chance.object(), chance.object()]
};
const expectedRequest = {
auth: {
...expectedAuth
},
path: '/subscriptions',
query: {
workType
}
};
it('should call API with auth token for Subscriptions', async () => {
const axiosStub = sinon.stub(axios, 'get').resolves(getSubscriptionsResponse);
const expectedParams = new URLSearchParams(expectedRequest.query);
await getSubscriptionsRoute.options.handler(expectedRequest);
expect(axiosStub).to.have.callCount(1);
expect(axiosStub).to.be.calledWith(expectedUrl, {
headers: {
Authorization: `${accessToken}`
}
});
});
The code is trying to filter the response.data._embedded array — response.data is an object. You should mock the response data to be an object (with the _embedded property) and not an array.
{
data: [chance.object(), chance.object()]
}
should be
{
data: {
_embedded: [
// ...
]
}
}
I am trying to create an app and within the app the user can install a theme, however, I can't seem to work out why the theme is not being created. It keeps pulling the themes already installed on my store to the console, my code doesn't seem to create a theme that would show up on my shopify store.
server.js
router.post('/api/theme', async (ctx) => {
try {
const results = await fetch("https://" + ctx.cookies.get('shopOrigin') + "/admin/themes.json", {
headers: {
'X-Shopify-Access-Token': ctx.cookies.get('accessToken')
},
})
.then(response => response.json())
.then(json => {
console.log("https://" + ctx.cookies.get('shopOrigin') + "/admin/api/2020-01/themes.json", json);
});
ctx.body = {
data: results
};
} catch (err) {
console.log(err)
}
});
frontend .js file
async function getUser() {
var url = `/api/theme`;
var method = 'post';
const theme = {
theme: {
name: "Lemongrass",
src: "https://codeload.github.com/Shopify/skeleton-theme/zip/master"
}
};
const data = JSON.stringify(theme);
fetch(url, { method: method, body: data})
}
In order to create a theme you need a zip archive of the theme you like to create.
The end point should be /admin/api/2020-01/themes.json and the body should be something like this:
{
"theme": {
"name": "Theme name",
"src": "http://themes.shopify.com/theme.zip",
"role": "unpublished"
}
}
Please refer to https://shopify.dev/docs/admin-api/rest/reference/online-store/theme#create-2020-01 for more information.
At the moment from your code I don't see neither the correct POST request, neither the archive file.
I'm trying to use react-admin to provide a user CRUD from my API (express)
I followed the steps from react-admin documentation.
Creating my own DataProvider.
Inserting Admin component tells me it is properly setup.
Adding a child Ressource component with users as the ressource name and ListGuesser as the list.
At this point I get a toast saying response in undefined and a console error saying Warning: Missing translation for key: "response is undefined"
I can see in the network tabs that the request is properly sent and receives a 200 response with the data I expected
I cannot understand it and where it comes from
Here is my adminComponent
import React from 'react';
import { Admin, Resource, ListGuesser } from 'react-admin'
import myDataProvider from './myDataProvider'
import './adminHomepage.css'
let myProvider = myDataProvider('http://localhost:8666')
function AdminHomepage(props) {
return (
<Admin dataProvider={myProvider}>
<Resource name="users" list={ListGuesser} />
</Admin>
);
}
export default AdminHomepage;
Here is my dataProvider
import useAuth from "../../hooks/useAuth";
import { stringify } from 'query-string';
import {
fetchUtils,
GET_LIST,
GET_ONE,
CREATE,
UPDATE,
DELETE,
GET_MANY_REFERENCE
} from 'ra-core';
const { getToken } = useAuth();
export default (apiUrl, httpClient = fetchUtils.fetchJson) => {
const convertDataRequestToHttp = (type, resource, params) => {
let url = "";
const options = {};
options.headers = new Headers({ Authorization : getToken(), Accept: "application/json" })
switch (type) {
case GET_LIST: {
url = `${apiUrl}/${resource}/`;
break;
}
case GET_ONE: {
url = `${apiUrl}/${resource}/${params.id}`;
break;
}
case CREATE: {
url = `${apiUrl}/${resource}/${params.id}`;
options.method = "POST";
options.body = JSON.stringify(params.data);
break;
}
case UPDATE: {
url = `${apiUrl}/${resource}/${params.id}`;
options.method = "PUT";
options.body = JSON.stringify(params.data);
break;
}
case DELETE: {
url = `${apiUrl}/${resource}/${params.id}`;
options.method = "DEL";
break;
}
default: {
throw new Error(`Unsupported request type ${type}`);
}
}
return { url, options };
};
const convertHttpResponse = (response, type, resource, params) => {
const { headers, json } = response;
switch (type) {
case GET_LIST:
case GET_MANY_REFERENCE: {
if (!headers.has("content-range")) {
throw new Error(
"Content-Range is missing from header, see react-admin data provider documentation"
);
}
let ret = {
data: json.users,
total: parseInt(
headers
.get("Content-Range")
.split(" ")
.pop()
)
};
console.log("RETURN", ret)
return ret
}
case CREATE: {
return { data: { ...params.data, id: json.id } };
}
default: {
return { data: json };
}
}
};
return (type, resource, params) => {
const { url, options } = convertDataRequestToHttp(type, resource, params);
return httpClient(url, options).then(response => {
console.log(response)
convertHttpResponse(response, type, resource, params);
});
};
};
Screenshot of my error
Warning: Missing translation for key: "response is undefined"
in Notification (created by Connect(Notification))
in Connect(Notification) (created by WithStyles(Connect(Notification)))
in WithStyles(Connect(Notification)) (created by Context.Consumer)
in Context.Consumer (created by translate(WithStyles(Connect(Notification))))
in translate(WithStyles(Connect(Notification))) (created by Layout)
in Layout (created by WithStyles(Layout))
in WithStyles(Layout) (created by Route)
in Route (created by withRouter(WithStyles(Layout)))
in withRouter(WithStyles(Layout)) (created by Connect(withRouter(WithStyles(Layout))))
in Connect(withRouter(WithStyles(Layout))) (created by LayoutWithTheme)
in LayoutWithTheme (created by Route)
in Route (created by CoreAdminRouter)
in CoreAdminRouter (created by Connect(CoreAdminRouter))
in Connect(CoreAdminRouter) (created by getContext(Connect(CoreAdminRouter)))
in getContext(Connect(CoreAdminRouter)) (created by Route)
in Route (created by CoreAdminBase)
in CoreAdminBase (created by withContext(CoreAdminBase))
in withContext(CoreAdminBase) (at adminHomepage.js:11)
in AdminHomepage (created by Router.Consumer)
in Router.Consumer (created by Route)
in Route (at App.js:17)
in App (at src/index.js:7)
I think you're forgetting to return the actual result back:
Change your code to:
return (type, resource, params) => {
const { url, options } = convertDataRequestToHttp(type, resource, params);
return httpClient(url, options).then(response => {
console.log(response)
return convertHttpResponse(response, type, resource, params);
});
}
This bit is probably just to pad the answer.
A Promise in JavaScript is just an object which will at some point get resolved to a value. When you do Promise.then you're basically returning a new promise which will have a callback triggered when resolved and that callback will receive the resolved property. The new promise your make will have the return value based on the result of the callback given. In your case the final resolved promise would have been undefined because nothing was returned in the callback
I'm creating an Apollo Client like this:
var { ApolloClient } = require("apollo-boost");
var { InMemoryCache } = require('apollo-cache-inmemory');
var { createHttpLink } = require('apollo-link-http');
var { setContext } = require('apollo-link-context');
exports.createClient = (shop, accessToken) => {
const httpLink = createHttpLink({
uri: `https://${shop}/admin/api/2019-07/graphql.json`,
});
const authLink = setContext((_, { headers }) => {
return {
headers: {
"X-Shopify-Access-Token": accessToken,
"User-Agent": `shopify-app-node 1.0.0 | Shopify App CLI`,
}
}
});
return new ApolloClient({
cache: new InMemoryCache(),
link: authLink.concat(httpLink),
});
};
to hit the Shopify GraphQL API and then running a query like that:
return client.query({
query: gql` {
productVariants(first: 250) {
edges {
node {
price
product {
id
}
}
cursor
}
pageInfo {
hasNextPage
}
}
}
`})
but the returned object only contain data and no extensions which is a problem to figure out the real cost of the query.
Any idea why?
Many thanks for your help
There's a bit of a hacky way to do it that we wrote up before:
You'll need to create a custom apollo link (Apollo’s equivalent of middleware) to intercept the response data as it’s returned from the server, but before it’s inserted into the cache and the components re-rendered.
Here's an example were we pull metrics data from the extensions in our API:
import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from 'apollo-boost'
const link = new HttpLink({
uri: 'https://serve.onegraph.com/dynamic?show_metrics=true&app_id=<app_id>',
})
const metricsWatchers = {}
let id = 0
export function addMetricsWatcher(f) {
const watcherId = (id++).toString(36)
metricsWatchers[watcherId] = f
return () => {
delete metricsWatchers[watcherId]
}
}
function runWatchers(requestMetrics) {
for (const watcherId of Object.keys(metricsWatchers)) {
try {
metricsWatchers[watcherId](requestMetrics)
} catch (e) {
console.error('error running metrics watcher', e)
}
}
}
// We intercept the response, extract our extensions, mutatively store them,
// then forward the response to the next link
const trackMetrics = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
runWatchers(
response
? response.extensions
? response.extensions.metrics
: null
: null
)
return response
})
})
function create(initialState) {
return new ApolloClient({
link: trackMetrics.concat(link),
cache: new InMemoryCache().restore(initialState || {}),
})
}
const apolloClient = create(initialState);
Then to use the result in our React components:
import { addMetricsWatcher } from '../integration/apolloClient'
const Page = () => {
const [requestMetrics, updateRequestMetrics] = useState(null)
useEffect(() => {
return addMetricsWatcher(requestMetrics =>
updateRequestMetrics(requestMetrics)
)
})
// Metrics from extensions are available now
return null;
}
Then use a bit of mutable state to track each request and its result, and the use that state to render the metrics inside the app.
Depending on how you're looking to use the extensions data, this may or may not work for you. The implementation is non-deterministic, and can have some slight race conditions between the data that’s rendered and the data that you've extracted from the extensions.
In our case, we store performance metrics data in the extensions - very useful, but ancillary - so we felt the tradeoff was acceptable.
There's also an open issue on the Apollo client repo tracking this feature request
I dont have any idea of ApolloClient but i tried to run your query in shopify graphql app. It return results with extensions. Please find screenshot below. Also You can put questions in ApolloClient github.
i have a view with 3 combo boxes that get their options from a database. when an option in one of them is selected, the others may have to be filtered. im making the call to my controller with ajax:
$(".dropFilter").on('change', function () {
var data = {
'EventEmitter': $(this).attr("id"),
'SelectedValue': $(this).val()
}
$.ajax({
type: 'POST',
url: '../MyController/FilterCombos',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
success: function (msg) {
console.log(msg)
},
fail: function (msg) {
console.log(msg)
},
});
});
the controller method being called is the following:
[HttpPost]
public IActionResult FilterCombos([FromBody]FilterComboRequest fcr)
{
switch (fcr.EventEmitter)
{
case "combo1-dropdown":
return ViewComponent("MyViewComponent", new
{
firstFilter = fcr.SelectedValue,
secondFilter = 0,
thirdFilter = fcr.SelectedValue
});
case "combo2-dropdown":
return ViewComponent("MyViewComponent", new
{
firstFilter = 0,
secondFilter = fcr.SelectedValue,
thirdFilter = 0
});
}
return ViewComponent("MyViewComponent", new
{
firstFilter = 0,
secondFilter = 0,
thirdFilter = 0
});
}
my viewcomponent invokeAsync method is the following:
public async Task<IViewComponentResult> InvokeAsync(int firstFilter,int secondFilter,int thirdFilter)
{
var mOpciones = new MOpciones();
var lOpciones = new LOpciones(_config);
lOpciones.fill(mOpciones,firstFilter,secondFilter,thirdFilter);
return View(mOpciones);
}
the combos are filled like so:
#Html.DropDownList("combo1",
new SelectList(Model.First,"Id","Nombre"),
"",
new { #class = "col-6 form-control form-control-lg",
#id="combo1-dropdown" })
when debugging, i see that mOpciones is being filled correctly in InvokeAsync, and Model.First has the right options in Default.cshtml, but the view on the browser never changes. what am i doing wrong?
Feel drop-down values can be updated within Ajax call method in JS.
Example:
success: function (data) {
$.each(data, function (index, value) {
$("#Dropdown").append('<option value="'
+ value.Value + '">'
+ value.Value + '</option>');
});