A JavaScript app uses Web Audio API to create sounds from JSON data. I am fetching weather data, going through the JSON data and setting their properties to variables then using those variables to manipulate my application and create sounds. Each function in it's own JavaScript module script file. The main.js not shown here is the entry point to app.
A sample JSON that will get replaced with real weather data.
dummy-data.json
{
"weather": {
"temp": 4,
"rain": 1,
"wind": 1.2
}
}
The fetch API logic.
fetchWeather.js
import { manageData} from './manageScript.js';
const DUMMY = '../dummy-data.json';
const fetchWeather = () => {
fetch(DUMMY)
.then((res) => {
return res.json();
})
.then((data) => {
manageData(data); // attaches JSON weather properties to variables
})
.catch((error) => {
console.log(error);
});
};
export { fetchWeather };
Attaches the JSON data to variables.
manageScript.js
function manageData(data) {
let rain = data.weather.rain;
//let wind = data.weather.wind;
let rainProbability;
if (rain == 1) {
rainProbability = 1;
}
else {
rainProbability = 0;
}
return rainProbability; // not sure how to return the data....?
};
export { manageData };
I want the variables from manageData function above to work here
makeSynth.js
import { manageData } from './manageScript.js';
const createSynth = () => {
//Web Audio API stuff goes here to create sounds from the variables.
//How do I get the variables to work here. Code below does not work!
let soundOfRain = manageData().rainProbability;
console.log(soundOfRain);
};
You can achieve this by refactoring your promises into a async/await pattern then returning the result (a different method of dealing with promises). Also - your createSynth function should be calling fetchWeather, not manageScript
dummy-data.json
{
"weather": {
"temp": 4,
"rain": 1,
"wind": 1.2
}
}
manageScript.js
function manageData(data) {
let rain = data.weather.rain;
//let wind = data.weather.wind;
let rainProbability;
if (rain == 1) {
rainProbability = 1;
} else {
rainProbability = 0;
}
return rainProbability;
}
export { manageData };
fetchWeather.js
import { manageData } from "./manageScript.js";
const DUMMY = "../dummy-data.json";
// Use async/await to be able to return a variable out from the promise
const fetchWeather = async () => {
const raw = await fetch(DUMMY);
const json_data = await raw.json();
const rain = manageData(json_data);
// Now you should be able to return the variable back out of the function
return rain;
};
export { fetchWeather };
makeSynth.js
import { fetchWeather } from "./fetchWeather.js";
const createSynth = async () => {
//Web Audio API stuff goes here to create sounds from the variables.
//Need to call fetchWeather (which in turn will call manageData)
let soundOfRain = await fetchWeather();
console.log(soundOfRain);
};
createSynth();
// dummy-data.json
{
"weather": {
"temp": 4,
"rain": 1,
"wind": 1.2
}
}
// fetchWeather.js
import { getRainProbability } from './get-rain-probability.js'
import { createSynth } from './create-synth.js'
const DUMMY = '../dummy-data.json'
const fetchWeather = () => {
return fetch(DUMMY)
.then((res) => res.json())
.then((data) => {
createSynth({ rainProbability: getRainProbability(data) })
})
.catch((error) => {
console.log(error)
});
};
export { fetchWeather }
// get-rain-probability.js
function getRainProbability(data) {
let rain = data.weather.rain
let rainProbability;
if (rain == 1) {
rainProbability = 1;
}
else {
rainProbability = 0;
}
return rainProbability; // not sure how to return the data....?
};
// create-synth.js
const createSynth = ({ rainProbability }) => {
const soundOfRain = //WebAPI stuff for audio using `rainProbability`
console.log(soundOfRain);
};
export { createSynth }
You can add data as a property of manageData that would return this, and access it with manageData().data; :
fetchWeather.js
const fetchWeather = () => {
fetch(DUMMY)
.then(res => {
return res.json();
})
.then(data => {
manageData.data = data; // attaches JSON weather properties to variables
})
.catch(error => {
console.log(error);
});
};
manageScript.js
function manageData() {
// ...
return this;
}
makeSynth.js
let soundOfRain = manageData().data.rainProbability;
Related
I am not getting any clue how to mock a method. I have to write a unit test for this function:
index.ts
export async function getTenantExemptionNotes(platform: string) {
return Promise.all([(await getCosmosDbInstance()).getNotes(platform)])
.then(([notes]) => {
return notes;
})
.catch((error) => {
return Promise.reject(error);
});
}
api/CosmosDBAccess.ts
import { Container, CosmosClient, SqlQuerySpec } from "#azure/cosmos";
import { cosmosdbConfig } from "config/Config";
import { Workload } from "config/PlatformConfig";
import { fetchSecret } from "./FetchSecrets";
export class CosmoDbAccess {
private static instance: CosmoDbAccess;
private container: Container;
private constructor(client: CosmosClient) {
this.container = client
.database(cosmosdbConfig.database)
.container(cosmosdbConfig.container);
}
static async getInstance() {
if (!CosmoDbAccess.instance) {
try {
const connectionString = await fetchSecret(
"CosmosDbConnectionString"
);
const client: CosmosClient = new CosmosClient(connectionString);
// Deleting to avoid error: Refused to set unsafe header "user-agent"
delete client["clientContext"].globalEndpointManager.options
.defaultHeaders["User-Agent"];
CosmoDbAccess.instance = new CosmoDbAccess(client);
return CosmoDbAccess.instance;
} catch (error) {
// todo - send to app insights
}
}
return CosmoDbAccess.instance;
}
public async getAllNotesForLastSixMonths() {
const querySpec: SqlQuerySpec = {
// Getting data from past 6 months
query: `SELECT * FROM c
WHERE (udf.convertToDate(c["Date"]) > DateTimeAdd("MM", -6, GetCurrentDateTime()))
AND c.IsArchived != true
ORDER BY c.Date DESC`,
parameters: [],
};
const query = this.container.items.query(querySpec);
const response = await query.fetchAll();
return response.resources;
}
}
export const getCosmosDbInstance = async () => {
const cosmosdb = await CosmoDbAccess.getInstance();
return cosmosdb;
};
index.test.ts
describe("getExemptionNotes()", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("makes a network call to getKustoResponse which posts to axios and returns what axios returns", async () => {
const mockNotes = [
{
},
];
const cosmosDBInstance = jest
.spyOn(CosmoDbAccess, "getInstance")
.mockReturnValue(Promise.resolve(CosmoDbAccess.instance));
const kustoResponseSpy = jest
.spyOn(CosmoDbAccess.prototype, "getAllNotesForLastSixMonths")
.mockReturnValue(Promise.resolve([mockNotes]));
const actual = await getExemptionNotes();
expect(kustoResponseSpy).toHaveBeenCalledTimes(1);
expect(actual).toEqual(mockNotes);
});
});
I am not able to get instance of CosmosDB or spyOn just the getAllNotesForLastSixMonths method. Please help me code it or give hints. The complexity is because the class is singleton or the methods are static and private
I tried to do a for loop inside a mounted() function with nuxt.js. The data I tried to loop through was called with axios in created() but when I log the data in created() I get this object:
[__ob__: Observer]
mounted:
mounted() {
// creating FeaturedCasinos
for(let i = 0; i > this.casinos.length; i++) {
console.log("loop")
if(this.casinos[i].brand_tags[2].Brand_Tag_Name = "Featured") {
this.featuredCasinos.push(this.casinos[i]);
}
}
},
created:
created() {
return axios.get("http://xxx.xxx.xxx.xx/casinos/").then(res2 => (this.casinos = res2.data))
}
EDIT:
asyncData({ params }) {
return axios.get(casinoURL + params.casinos).then(res => {
return {
casino: res.data,
casinoID: res.data[0].id,
casinoBonus: res.data[0].bonuses,
casinoPros: res.data[0].brand_pros,
casinoCons: res.data[0].brand_cons,
casinoGames: res.data[0].verticals,
casinoTags: res.data[0].brand_tags,
casinoPayments: res.data[0].payment_methods,
casinoDeposits: res.data[0].Deposit_Methods,
casinoWithdrawals: res.data[0].Withdrawal_Methods,
casinoLanguages: res.data[0].languages,
casinoGamingProvider: res.data[0].gaming_provider,
casinoAnswers: res.data.map(item => { return {FAQ_Answer_One:item.FAQ_Answer_One, FAQ_Answer_Two:item.FAQ_Answer_Two, FAQ_Answer_Three:item.FAQ_Answer_Three, FAQ_Answer_Four:item.FAQ_Answer_Four, FAQ_Answer_Five:item.FAQ_Answer_Five, FAQ_Answer_Six:item.FAQ_Answer_Six}})
};
})
},
asyncData({ params }) {
return axios.get("http://xxx.xxx.xxx.xx/casinos/").then(res2 => {
return { casinos: res2.data }
});
},
As per the documentation:
You do NOT have access to the component instance through this inside asyncData because it is called before initializing the component.
So instead in asyncData you should return the data that will be merged with the component data as an object:
asyncData({ params }) {
return axios.get("http://xxx.xxx.xxx.xx/casinos/").then(res2 => {
return { casinos: res2.data }
}
}
EDIT: in this new case after you edited the question you should delete one of the asyncData and retrieve the unified data. You may use the async/await syntax to make the code more clear and easier to read:
asyncData({ params }) {
const res = await axios.get(casinoURL + params.casinos)
const res2 = await axios.get("http://xxx.xxx.xxx.xx/casinos/")
return {
casino: res.data,
casinoID: res.data[0].id,
casinoBonus: res.data[0].bonuses,
casinoPros: res.data[0].brand_pros,
casinoCons: res.data[0].brand_cons,
casinoGames: res.data[0].verticals,
casinoTags: res.data[0].brand_tags,
casinoPayments: res.data[0].payment_methods,
casinoDeposits: res.data[0].Deposit_Methods,
casinoWithdrawals: res.data[0].Withdrawal_Methods,
casinoLanguages: res.data[0].languages,
casinoGamingProvider: res.data[0].gaming_provider,
casinoAnswers: res.data.map(item => { return {FAQ_Answer_One:item.FAQ_Answer_One, FAQ_Answer_Two:item.FAQ_Answer_Two, FAQ_Answer_Three:item.FAQ_Answer_Three, FAQ_Answer_Four:item.FAQ_Answer_Four, FAQ_Answer_Five:item.FAQ_Answer_Five, FAQ_Answer_Six:item.FAQ_Answer_Six}})
casinos: res2.data
}
}
I'm coming from React/Redux-land and am slowly getting acquainted to Svelte design patterns using stores.
Currently I'm curious to figure out if this is an acceptable pattern or if not, what is a better way to pursue this kind of communication. The basic premise is I want to be able to update multiple custom stores (which are using writable) from an adjacent store.
In the example below I have "loading.js" and "error.js" stores which would be used globally, commented out in the "session.js" store. I'd like to update these based on the result of an API request to create a session, in order to keep most of my heavy lifting out side of components.
My current thinking is that I'd pass each store needed through the "createSessionStore" function, but it feels a little clunky as it would highly depend on the declaration order of each store within "store.js"
The long term intention for wishing to do it this way is so I can add any kind of communication layer (such as web sockets) in to the mix and update the global loading or error store from any layer.
Thanks for the help.
Component.svelte
<script>
import { onMount } from "svelte";
import { error, loading, session } from "./store";
onMount(() => {
session.fetchSession();
});
</script>
{#if $loading}
<div>Loading...</div>
{/if}
{#if $error}
<div>Something went wrong: {$error}</div>
{/if}
store.js
import { createErrorStore } from "./error";
import { createLoadingStore } from "./loading";
import { createSessionStore } from "./session";
export const error = createErrorStore();
export const loading = createLoadingStore();
export const session = createSessionStore();
session.js
import { writable } from "svelte/store";
const INITIAL_STORE = {
token: null
};
export const createSessionStore = (initialStore = INITIAL_STORE) => {
const { subscribe, set } = writable(initialStore);
const fetchSession = async () => {
// loading.set(true);
try {
const response = await fetch("MY_API_ENDPOINT/auth/token", {
method: "POST",
});
if (!response.ok) {
const err = new Error("Network response was not ok.");
// error.set(err);
// loading.set(false);
return;
}
const data = await response.json();
set(data.token);
// loading.set(false);
} catch (err) {
// error.set(err);
// loading.set(false);
}
};
const reset = () => {
set(initialStore);
};
return {
subscribe,
fetchSession,
reset
};
};
error.js
import { writable } from "svelte/store";
const INITIAL_STORE = false;
export const createErrorStore = (initialStore = INITIAL_STORE) => {
const { subscribe, set } = writable(initialStore);
const reset = () => {
set(initialStore);
};
return {
subscribe,
set,
reset
};
};
loading.js
import { writable } from "svelte/store";
const INITIAL_STORE = false;
export const createLoadingStore = (initialStore = INITIAL_STORE) => {
const { subscribe, set } = writable(initialStore);
const reset = () => {
set(initialStore);
};
return {
subscribe,
set,
reset
};
};
Interesting idea.
The problem here is that during the creation of the stores, not all of them exists yet. The only solution that I see for this is to add the references after creating them.
Here's my idea:
In the session.js:
import { writable } from "svelte/store";
const INITIAL_STORE = {
token: null
};
export const createSessionStore = (initialStore = INITIAL_STORE) => {
const { subscribe, set } = writable(initialStore);
const fetchSession = async () => {
// loading.set(true);
try {
otherStores.loading && otherStores.loading.set(true);
const response = await fetch("MY_API_ENDPOINT/auth/token", {
method: "POST",
});
if (!response.ok) {
const err = new Error("Network response was not ok.");
otherStores.error && otherStores.error.set(err);
otherStores.loading && otherStores.loading.set(false);
return;
}
const data = await response.json();
set(data.token);
} catch (err) {
otherStores.error && otherStores.error.set(err);
otherStores.loading && otherStores.loading.set(false);
}
};
const reset = () => {
set(initialStore);
};
let otherStores = {}
const setOtherStores = (stores) => {
otherStores=stores
};
return {
subscribe,
fetchSession,
reset,
setOtherStores
};
};
In the store.js:
import { createErrorStore } from "./error";
import { createLoadingStore } from "./loading";
import { createSessionStore } from "./session";
export const error = createErrorStore();
export const loading = createLoadingStore();
export const session = createSessionStore();
session.setOtherStores({error,loading})
You can use the same pattern for any of the other stores (if needed), and after creation pass them the references to the other stores.
I'm implementing service between a view and a Rest API.
Beside, i'm completly new to stamp programming and i'm in search of some advices about this sort of code:
import {compose, methods} from '#stamp/it'
import ArgOverProp from '#stamp/arg-over-prop'
import {template} from 'lodash'
const createLogger = name => (...args) => console.log('['+ name + ']', ...args)
const HasHttpClient = ArgOverProp.argOverProp('httpClient')
const HasApiVersion = ArgOverProp.argOverProp('apiVersion')
const HasUrl = ArgOverProp.argOverProp('url')
const UseRestApi = compose(HasHttpClient, HasApiVersion, HasUrl).init([
function () {
this.getUrl = template(this.url)
this.useRestApiLog = createLogger('UseRestApi')
}
]).methods({
query: function query(method, {params, headers, body}) {
const {apiVersion} = this
const q = {
baseURL: this.getUrl({apiVersion}),
method,
...params != null && {params},
...headers != null && {headers},
...body != null && {body}
}
this.useRestApiLog('request config:', q)
return q
}
})
const WithGetOperation = compose(UseRestApi).init([
function () {
this.withGetOperationLog = createLogger('WithGetOperation')
}
]).methods({
'get': function get ({params}) {
const q = this.query('get', {headers: {'Accept': 'application/json'}, params})
this.withGetOperationLog('getting data')
return this.httpClient(q)
}
})
const CustomerRestApi = compose(WithGetOperation).init([
function () {
this.customerRestApiLog = createLogger('CustomerRestApi')
}
]).methods({
all: function all() {
this.customerRestApiLog('get all customers')
return this.get({params: {page: 1, limit: 15}})
}
})
const customerProvider = CustomerRestApi({
url: 'http://sample.com/<%=apiVersion%>/customers',
apiVersion: 'v1',
httpClient: function(config) {
return Promise.resolve({
status: 200,
config
})
}
})
const appLog = createLogger('APP')
customerProvider.all()
.then(r => appLog('HTTP response code:', r.status))
Am i in the right directions?
Especially, the createLogger thing seems ugly!
How to inject a prefixed logger into each stamp ?
How to extend that to warn, error, ... methods ?
Your logger looks just fine. 👍 It is not necessary to create every bit as a stamp. However, if you want to make the logger as a reusable stamp then you can do the same way as ArgOverProp is implemented.
Ruffly ArgOverProp is done this way:
const ArgOverProp = stampit.statics({
argOverProp(...args) {
return this.deepConf({ArgOverProp: [...args]});
}
})
.init(function (options, {stamp}) {
const {ArgOverProp} = stamp.compose.deepConfiguration;
for (let assignableArgName of ArgOverProp) {
this[assignableArgName] = options[assignableArgName];
}
});
Your logger could look like this (not necessary exactly like this):
import {argOverProp} from '#stamp/arg-over-prop';
const Logger = stampit(
argOverProp('prefix'),
{
methods: {
log(...args){ console.log(this.prefix, ...args); },
error(...args){ console.error(this.prefix, ...args); },
warn(...args){ console.warn(this.prefix, ...args); }
}
}
);
const HasLogger = stampit.statics({
hasLogger(name) {
return this.conf({HasLogger: {name}});
}
})
.init(_, {stamp}) {
const {HasLogger} = stamp.compose.configuration;
if (HasLogger) {
this.logger = Logger({prefix: HasLogger.name});
}
});
And usage:
const CustomerRestApi = stampit(
WithGetOperation,
HasLogger.hasLogger('CustomerRestApi'),
{
methods: {
all() {
this.logger.log('get all customers');
return this.get({params: {page: 1, limit: 15}});
}
}
);
I always prefer readability. So, the code above, I hope, is readable to you and any stampit newbie.
PS: a tip. The stampit and the stampit.compose you imported above are the same exact function. :) See source code.
I am still trying to get in my head the object fundamentals in javascript which seems to be quite different than classical paradigm. I have written a toy example to fetch weather data, the code is below:
import axios from 'axios'
const weatherService = {
fetch: async endpoint => await axios.get(endpoint)
}
const weatherApi = {
currentWeather: async city => {
try {
const { data: { data } } = await this.service.fetch(this.config.endpoints.curr)
return this.handleCurrentData(data)
} catch(e) {
this.handleError(e)
}
},
hourlyForecast: async city => {
try {
const { data: { data } } = await this.service.fetch(this.config.endpoints.hour)
return this.handleHourlyData(data)
} catch(e) {
this.handleError(e)
}
}
};
const defaultConfig = {
key: process.env.API_KEY,
endpoints: {
curr: `/current/geosearch?key=${this.key}`,
hour: `/forecast/3hourly/geosearch?key=${this.key}`
}
};
const api = (api, config, service) => {
return {
Object.create(weatherApi),
Object.create(service),
Object.create(config),
...obj
}
};
// dependency injection for testing
module.exports = (obj, config=defaultConfig, service=weatherService) => {
return api(obj, config, service)
};
// Usage
const weatherAPI = require('api')({
handleCurrentData: (data) => console.log(data),
handleHourlyData: (data) => console.log(data)
});
weatherAPI.currentWeather('London');
weatherAPI.hourlyWeather('London');
I would like to know if I am going in the correct direction? If not what are improvement in thinking process as well as in code needed?
PS: I know I could have written the above api easily by exporting functions but this is an attempt to learn object composition.