Why does my value in localstorage have speech marks? - javascript

I'm trying to retrieve some data from a mysql database with the code:
app.get("/api/getStudentsFromClass", async(req,res) => {
const currentClassClicked = req.query.currentClassClicked
connection.query(
"SELECT * FROM ?",
[currentClassClicked],
(err,result) => {
if(result){
console.log(result)
}
if(err){
console.log(err)
}
}
)
})
The front end:
const currentClassClicked = localStorage.getItem("currentClassClicked")
const [students, setStudents] = useState("")
Axios.get("http://localhost:1337/api/getStudentsFromClass", {
params: {currentClassClicked}
}).then((response) => {
setStudents(response.data.message)
console.log(students)
})
However it says there's an error in mysql statement and shows that the statement is this
sql: "SELECT * FROM '13a1'"
I don't know why it's '13a1' and not 13a1.
Thank you

Because you're telling that 13a1 is a string (as opposed to a table name):
"SELECT * FROM ?",
[currentClassClicked],
The whole point of prepared statements and bound parameters is to separate code from data.
The only way to secure your current design is to have a hard-coded list of tables, verify that received input maps a known table and build SQL dynamically, as in "SELECT * FROM " + currentClassClicked. But the overall idea looks strange. One would expect to find a master table with all classes and a child table with the information identified by class ID.
As per the follow-up comments, you don't create a new table for each student—there's no reason to handle classes different. However, if you want to keep your current design you'll have to hard-code tables in the application code or assume that it isn't secure and PII data could eventually be exposed.

Related

Using ExpressJS, how do I extract different data from a SQLite table using the URL?

I want to access resources from a SQLite database table. I have one table for accounts, one for movies and one for reviews. The reviews-table is constructed like this:
CREATE TABLE IF NOT EXISTS reviews (
id INTEGER PRIMARY KEY,
authorId INTEGER,
movieId INTEGER,
review TEXT,
rating INTEGER,
FOREIGN KEY('authorId') REFERENCES 'accounts'('id'),
FOREIGN KEY('movieId') REFERENCES 'movies'('id')
)
What I want to do is that I want to be able to get all reviews, made by one author. But I also want to be able to get all reviews, made for the same movie. Below is my code for getting all reviews made by the same user/author. The code looks the same when getting the reviews based on the movie, with a few changes.
Both of them does what I want them to do. But of course only the one written first in the file are running.
const authorId = request.params.authorId;
const query = "SELECT * FROM reviews WHERE authorId = ?";
const values = [authorId];
db.all(query, values, function (error, review) {
if (error) {
response.status(500).end();
} else if (!review) {
response.status(404).end();
} else {
response.status(200).json(review);
}
});
});
The url will look the same no matter which of them I want running; http://localhost:3000/reviews/3. How can I differentiate the url so that they know which one should run?
I have tried to experiment with query strings, but I'm not sure how that works, and after hours of searching for something that worked on my code, I gave up.
I have also been thinking about using something like
app.use(function (request, response, next) {
if (request.method == "GET" && request.uri == "/reviews/:authorId") {
response.send("Hello World");
} else {
next;
}
});
This didn't work, and it didn't work if I tried to remove ":authorId" from the url either. The page just keeps loading.
So how do I solve this?
The most dynamic would be a single route /reviews and use the query string with the params like ?author=123 or ?movie=123, they can be combined like ?author=123&movie=123. As you want to return JSON the code will be used via API, so the pretty path is usually not as important as when it is a web-url. To make the implementation effective, most people use a function where you can drop the query object in and get the where-clause, or use an ORM.
In express, when you have routers like '/reviews/:authorId' and then '/reviews/:movieId', then the second one will never be called, because the first one will always match. That is something to be careful about when organizing your express routes.

Populating a many - many doesn't populate, and throws no errors

My database looks like this:
Table: Guilds -> guild_id, guild_name, guild_owner, guild_owner_id, guild_member_count, guild_prefix.
Table: Members -> member_id, member_name, member_disc
Table: Guildmembers -> guild_id, member_id, warning_points, chat_points
So guild_id and member_id are the values from Discord and are both PK's. My aim is to tie each member to a guild specifically, to add warning_points and chat_points.
I can populate both the Guilds table and the Members table, however, it doesn't auto-populate the Guildmembers table with the relevant data.
Upon trying to populate the table with this code:
let guiMemSQL = 'INSERT INTO Guildmembers(guild_id, member_id) SELECT Guilds.guild_id, Members.member_id FROM Guilds INNER JOIN Members ON Guilds.guild_id = Members.member_id'
con.query(guiMemSQL, (err, result) => {
if(err){ throw err }
console.log(`Updated GuildMembers Table`);
})
I get no errors. The table doesn't populate either. So either there is a flaw in my code/logic or I have not understood what I am doing correctly.
The information I am trying to get is guild_id and member_id, they are BIGINTs.
mysql will not auto-populate a many-many table, you have to INSERT the values as you would any other table. The way I did this was with the below code:
// Update GuildMembers Table
let guiID = MemberInfo.userGuildID;
const memGuiID = BigInt(guiID); // Convert String to Number
const updGMT = {guild_id: memGuiID, member_id: memIdNo };
let guiMemSQL = 'INSERT INTO Guildmembers SET ?';
con.query(guiMemSQL, updGMT, (err, result) => {
if(err){ throw err }
console.log(`Updated GuildMembers Table`);
})
The use case of parameterised queries here, although not question specific are important to help combat the risk of sql injection attacks. And would be seen as good practise.
If anyone does know of a way to get the fields to auto-populate, please share!.
Currently I shall leave what I did to resolve this, In case others have a similar issue.

Firebase Real time database execute conditional queries

I am using Express and Firebase Real time database Admin SDK to manage database. I need to get all the tickets whose status are pending or open from the logged in user, but I am not sure how to get this data and I am not finding the solution in the documentation. In SQL would be something like:
SELECT *
FROM tickets as t
INNER JOIN users as u
WHERE t.owner = u.id
AND (
status = 'pending'
OR status = 'open'
);
In case you need to see some code to understand what I am talking about this is what so far I've tried:
router.post('/', async (req: Request, res: Response) => {
const existingRef = database.ref('tickets')
.orderByChild('owner')
.equalTo(req.user.key)
.orderByChild('status')
.equalTo('open')
.orderByChild('status')
.equalTo('pending);
const existingSnapshot = await existingRef.on('child_added');
if (existingSnapshot.val() !== null) {
return res.json(existingSnapshot.val());
}
}
The previous code will return the tickets whose status are pending and open which does not make sense since there should be only one status per ticket.
So, what should I do? Thanks in advance
The Realtime Database can only query on one child field at a time, so the query you're proposing won't work. You'd need to filter the results after fetching them, something like:
const snap = await database.ref('tickets')
.orderByChild('user').equalTo(user.uid)
.once('value');
const out = {};
snap.forEach(t => {
if (['open','pending'].includes(t.child('status').val())) {
out[t.key] = t.val();
}
});
res.json(out);
You may want to consider using Cloud Firestore instead, as it can do more complex queries:
firebase.firestore().collection('tickets')
.where('user','==',user.uid)
.where('status','in',['open','pending'])

How to handle foreign key constraint with Jest/Supertest/Knex/Postgres

I'm trying to start writing tests for my application. I'm using Jest & Supertest to run all of my tests. When I try and run my test suite, I'm getting an error regarding a foreign key constraint.
The error:
error: truncate "users" restart identity - cannot truncate a table referenced in a foreign key constrainterror: cannot truncate a table referenced in a foreign key constraint
This is my server.spec.js file:
const request = require('supertest');
const server = require('./server.js');
const db = require('../data/db-config.js');
describe('server.js', () => {
describe('POST /register', () => {
it('should return 201 created', async () => {
const user =
{
name: "test",
username: "test",
email: "test77#test.com",
password: "password"
}
const res = await request(server).post('/api/auth/register').send(user);
expect(res.status).toBe(201);
})
beforeEach(async () => {
await db("graphs").truncate();
await db("users").truncate();
});
})
})
And here is my knex migration file:
exports.up = function(knex) {
return (
knex.schema
.createTable('users', tbl => {
tbl.increments();
tbl.string('username', 255).notNullable();
tbl.string('password', 255).notNullable();
tbl.string('name', 255).notNullable();
tbl.string('email', 255).unique().notNullable();
})
.createTable('graphs', tbl => {
tbl.increments();
tbl.string('graph_name', 255).notNullable();
tbl.specificType('dataset', 'integer[]').notNullable();
tbl
.integer('user_id')
.unsigned()
.notNullable()
.references('id')
.inTable('users')
.onDelete('CASCADE')
.onUpdate('CASCADE');
})
)
};
exports.down = function(knex) {
return (
knex.schema
.dropTableIfExists('graphs')
.dropTableIfExists('users')
)
};
I came across this answer in my research: How to test tables linked with foreign keys?
I'm new to both Postgres as well as testing. It makes sense that I would need to drop the tables in the reverse order like I have in my migration. But when I try to truncate them in the beforeEach section of my test, it doesn't seem to matter what order the tables are listed in.
I'm not sure where exactly to go from here. Any help would be greatly appreciated.
I think the trick here will be to resort to a bit of knex.raw:
await db.raw('TRUNCATE graphs, users RESTART IDENTITY CASCADE');
CASCADE because you don't want foreign key constraints getting in the way, and RESTART IDENTITY because the default Postgres behaviour is not to reset sequences. See TRUNCATE.
While we're on a related subject, allow me to introduce something that might make your life a lot easier: template databases! Templates are databases that Postgres can use to very rapidly recreate a database from a known state (by copying it). This can be faster than even truncating tables, and it allows us to skip all the annoying foreign key stuff when testing.
For example, you could use raw to do the following:
DROP DATABASE testdb;
CREATE DATABASE testdb TEMPLATE testdb_template;
This is a surprisingly inexpensive operation, and is great for testing because you can begin with a known state (not necessarily an empty one) each time you do a test run. I guess the caveats are that your knexfile.js will need to specify a connection with a user sufficiently credentialled to create and delete databases (so maybe an 'admin' connection that knows about localhost only) and that the template must be created and maintained. See Template Databases for more.

Node mssql: return id after SQL insertion

I am working on an integration between Mongodb and SQL Server, inserting and updating registers from MongoDb to SQL Server database with a scheduled process, using the mssql package to achieve this.
Each time a new register is inserted, I want the new row id to be retrieved back, because I need it to perform another insertions of subregisters.
I tried the following, expecting to have some information retrieved in the result of the query. The query actually inserts the row, but result is returned as undefined:
var sql = require('mssql');
var connectionConfig = 'xxxxxxxxxxxx';
var insertionQuery = 'xxxxxxxxxxxx';
sql.connect(connectionConfig).then(pool => {
pool
.request()
.query(insertionQuery);
}).then(result => {
//I expect 'result' to have information of the inserted row, but it comes undefined instead
cosole.log(result);
}).catch(err => {
console.err(err);
});
I think using stored procedures may work around this, but I just want to avoid using a stored procedure for simple inserts.
Does anyone knows how to have the new row id back after insertion?
EDIT
I did include OUTPUT SCOPE_IDENTITY() in my SQL statement - my query now looks like:
INSERT INTO TABLE (columns...)
OUTPUT SCOPE_IDENTITY()
VALUES (values...)
If I run the query in my SQL Server Management Studio, it works just fine and I got my new row id returned, but when running the query through the mssql package's query method, result is returned like this:
[ { '': null } ]
I quote from node-mssql insert returning undefined recordset:
INSERT statement doesn't return a recordset so recordset is
undefined. Please see this section of the docs to learn more
about how to get number of affected rows.
An option to solve this is to add a SELECT statement after your INSERT and ask for SCOPE_IDENTITY() or ##IDENTITY. That will get you the ID of the last inserted objects, i.e.:INSERT INTO table (...) VALUES (...); SELECT SCOPE_IDENTITY() AS id;
If anyone comes having problems with GUID, since you cannot set it as an Identity column, you must do something like:
const result = await pool
.request()
.input('orgName', sql.VarChar, orgName)
.input('tier', sql.Int, tier)
.input('status', sql.Int, status)
.input('createDate', sql.Date, new Date())
.input('statusChangeDate', sql.Date, new Date())
.query(`INSERT INTO app.Organizations (OrgName,
Tier, Status, CreateDate, StatusChangeDate)
OUTPUT inserted.OrgID
VALUES (#orgName, #tier, #status, #createDate, #statusChangeDate);
`)
Where the orgID is the auto generated GUID. Also don't follow my new Date() example as it inserts the date as tomorrow... I need to fix that.

Categories

Resources