How to make serial queue for a few URLSession.DataTaskPublisher? - multithreading

I have several datataskpublisher, which performs requests to the server, from different application screens. How to make them run serial?
Below will be a rough example
This is a service that performs requests and additional logic.
class Service {
var cacheResult: Data?
var cacnellable: AnyCancellable?
static let shared = Service()
func performPost() -> AnyPublisher<Data, URLError> {
let task = URLSession.shared.dataTaskPublisher(for: URL(string: "postURL")!)
.map { (data, _) in data}
.multicast(subject: PassthroughSubject())
cacnellable = task.eraseToAnyPublisher().sink(
receiveCompletion: {_ in },
receiveValue: { data in
self.cacheResult = data
})
return task.autoconnect().eraseToAnyPublisher()
}
func performGet() -> AnyPublisher<Data, URLError> {
let task = URLSession.shared.dataTaskPublisher(for: URL(string: "getURL")!)
.map { (data, _) in data}
.multicast(subject: PassthroughSubject())
cacnellable = task.eraseToAnyPublisher().sink(
receiveCompletion: {_ in },
receiveValue: { data in
self.cacheResult = data
})
return task.autoconnect().eraseToAnyPublisher()
}
}
This is views
final class GetVC: UIViewController {
let service = Service.shared
var cancellable: AnyCancellable?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
cancellable = service.performGet()
.sink(receiveCompletion: {_ in},
receiveValue: { data in
print("Present data")
})
}
}
final class PostVC: UIViewController {
let service = Service.shared
var cancellable: AnyCancellable?
lazy var button: UIButton = {
let btn = UIButton(primaryAction: UIAction(handler: { _ in
self.cancellable = self.service.performPost()
.sink(receiveCompletion: {_ in },
receiveValue: { data in
print("Some logic")
})
}))
return btn
}()
}
I need that requests never went to the server in parallel
If "post task" finished , "get task" start
Thank you!

I came with this decision
class Service {
var cacheResult: Data?
var cancellables = Set<AnyCancellable>()
var taskSubject = PassthroughSubject<AnyPublisher<Data, URLError>, Never>()
var taskInProcess: Bool = false
var tasks: [AnyPublisher<Data, URLError>] = [] {
didSet {
if !taskInProcess, let firstTask = tasks.first {
taskInProcess = true
taskSubject.send(firstTask)
}
}
}
static let shared = Service()
init() {
taskSubject
.flatMap { $0 }
.sink(receiveCompletion: { _ in },
receiveValue: { _ in
self.taskInProcess = false
self.tasks.removeFirst()
})
.store(in: &cancellables)
}
func performPost() -> AnyPublisher<Data, URLError> {
let task = URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
.map { (data, _) in data}
.multicast(subject: PassthroughSubject())
tasks.append(task.autoconnect().eraseToAnyPublisher())
return task.eraseToAnyPublisher()
}
func performGet() -> AnyPublisher<Data, URLError> {
let task = URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
.map { (data, _) in data}
.multicast(subject: PassthroughSubject())
tasks.append(task.autoconnect().eraseToAnyPublisher())
return task.eraseToAnyPublisher()
}
}

Related

How to speed up Fetching Google Place and Photos

I currently have the following code to fetch matching Google Places according to a received query as shown below:
async function searchGoogleBusiness(req, res) {
let { name } = req.query;
const apiKey = process.env.API_KEY;
const searchUrl = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=`;
try {
let { data } = await axios.get(`${searchUrl}${name}&key=${apiKey}`)
let { status, error_message, results } = data;
if (status === 'OK') {
let businessResults = [];
if ((results ?? []).length > 0) {
for (let business of results) {
let businessDetails = {
....
}
if ((business.photos ?? []).length > 0) {
let { width = 1200, height = 1200, photo_reference } = business.photos[0];
let photoUrl = `https://maps.googleapis.com/maps/api/place/photo?photoreference=${photo_reference}&sensor=false&maxheight=${height}&maxwidth=${width}&key=${apiKey}`
try {
let businessPhotoResponse = await axios.get(photoUrl, { responseType: 'arraybuffer' });
let imageBuffer = businessPhotoResponse.data;
let base64Image = Buffer.from(imageBuffer, 'binary').toString('base64');
businessDetails.photo = `data:${businessPhotoResponse.headers['content-type']};base64,${base64Image}`;
} catch (e) {
businessDetails.photo = business.icon;
}
} else {
businessDetails.photo = business.icon;
}
businessResults.push(businessDetails);
}
}
...//Omitted
}
...//Omitted
} catch (e) {
...//Omitted
}
}
As you can immediately notice, the function takes forever to return when the results are more than 5 and the reason is because I'm looping through each business to make another api call to fetch each photo.
I don't like this approach at all.
This idea of making another network call using photoReferences is really affecting my site speed and basically just makes my users angry.
Is there no way to automatically fetch the photo urls along just in the first request?

Messages are large, You may want to consider smaller messages. node-ipc and swift (OSX)

I have a node app that is trying to communicate with a swift application using a unix socket.
I am broadcasting a Message ever 3 seconds using the following code with node-ipc:
const ipc = require('node-ipc').default;
// Run IPC server for testing
const first = "FIRST";
const second = "SECOND";
const ipcSocketPath = `/tmp/${ipcAppId}`;
async function setupCoreCommunicationIpc() {
await new Promise((resolve) => {
ipc.serve(ipcSocketPath, () => {
ipc.server.on('ping', (data, socket) => {
ipc.server.emit(socket, 'pong');
});
return resolve(true);
});
ipc.server.start();
});
}
function sendMessage(message, payload = {}) {
ipc.server.broadcast(message, { ...payload, "valueOne": first, "valueTwo": seconds });
}
(async () => {
try {
await setupCoreCommunicationIpc()
} catch (e) {
// Deal with the fact the chain failed
}
// `text` is not available here
})();
setInterval(async () => {
await sendMessage("first:message", {core_id: ipcAppId, app_id: ipcAppId, message_id: 5})
}, 3000);
The code on swift is a bit more complicated. But I am able to connect to the unix socket and receive the message. The Problem is the sending of the AckMessage. I tried different approaches for sending the message but it would not work. Here is the code in swift:
func startSocketStack(valueOne: String, valueTwo: String){
let MTU = 65536
let path = "/tmp/\(valueTwo)"
print("starting socket at: %\(path)%")
let client = socket(AF_UNIX, SOCK_STREAM, 0)
var address = sockaddr_un()
//address.sun_family = UInt8(AF_UNIX)
address.sun_family = sa_family_t(AF_UNIX)
//address.sun_len = UInt8(MemoryLayout<sockaddr_un>.size)
address.sun_len = UInt8(MemoryLayout<UInt8>.size + MemoryLayout<sa_family_t>.size + path.utf8.count + 1)
strlcpy(&address.sun_path.0, path, MemoryLayout.size(ofValue: address.sun_path))
var adressUnwrap = address
withUnsafePointer(to: &adressUnwrap) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
let connection = connect(client, $0, socklen_t(address.sun_len))
if (connection != 0) {
print("startSocket: could not connect to socket at \(path)")
}
}
}
var buffer = UnsafeMutableRawPointer.allocate(byteCount: MTU,alignment: MemoryLayout<CChar>.size)
while(true) {
let readResult = read(client, &buffer, MTU)
if (readResult == 0) {
break; // end of file
} else if (readResult == -1) {
print("Error reading form client\(client) - \(errno)")
break; // error
} else {
let strResult = withUnsafePointer(to: &buffer) {
$0.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size(ofValue: readResult)) {
String(cString: $0)
}
}
print("Received form client(\(client)): §\(strResult)§")
print("§\(strResult)§")
let trimmedString = strResult.components(separatedBy: .whitespacesAndNewlines).joined()
print("§\(trimmedString)§")
struct Message: Decodable {
let type: String
let data: MessageData
struct MessageData: Decodable {
var valueOne: String
var valueTwo: String
var message_id: String
}
}
struct AckMessage: Encodable {
let type: String
let data: Bool
}
do {
let jsonMessage = trimmedString.components(separatedBy: "}")[0] + "}" + "}"
let jsonData = jsonMessage.trimmingCharacters(in: CharacterSet.newlines).data(using: .utf8)!
let receivedMessage: Message = try JSONDecoder().decode(Message.self, from: jsonData)
let messageId = receivedMessage.data.message_id
let ackMessage = AckMessage(type: messageId, data: true)
let jsonAckMessage = try JSONEncoder().encode(ackMessage)
let delimiter = #"\f"#.bytes
let jsonAckMessageWithDelimiter = jsonAckMessage + delimiter
print("jsonAckMessageWithDelimiter")
print(jsonAckMessageWithDelimiter)
// first attempt
do {
try jsonAckMessageWithDelimiter.withUnsafeBytes() { [unowned self] (buffer: UnsafeRawBufferPointer) throws -> Int in
let buffer = buffer.baseAddress!
print(buffer)
let bufSize = jsonAckMessageWithDelimiter.count
print(bufSize)
var sent = 0
var sendFlags: Int32 = 0
while sent < bufSize {
var s = 0
s = send(client, buffer.advanced(by: sent), Int(bufSize - sent), sendFlags)
if s <= 0 {
if errno == EAGAIN{
// We have written out as much as we can...
return sent
}
}
sent += s
}
return sent
}
} catch {
print(error)
}
// second attempt
jsonAckMessageWithDelimiter.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
write(client, pointer, jsonAckMessageWithDelimiter.count)
}
} catch {
print("Error on received message \(error)")
}
}
}
}
In my node application I can not receive the AckMessage because I get the following error:
Messages are large, You may want to consider smaller messages.
Now from my experience from Windows and Linux I know that \f is the delimiter that should indicate the end of the message. I am sending it at the end of my json but it is somehow not recognized.
What could be the problem? Is the delimiter wrong? Is the buffersize a Problem?
When logging what I send it says that it has sent all 61 bytes as expected.
For anyone having this problem using "\u{000C}" instead of "\f" is the solution.

How to edit/replace embeds in discordjs-commando

I am making a Type racing minigame with my discord bot, the code works... but I want to change the messages it sends to embeds, im new to Commando and it wont let me use the discord.js functions im used to using
I need to change all the bots responses to embeds, and make it so when it sends a new embed it just edits the old one so it isnt spamming messages. Here is my code:
const Commando = require('discord.js-commando')
const { words } = require('../../util/fast-type-words')
const example = {
channelId: {
message: 'message object',
stage: 'string',
counter: 'number',
currentWord: 'string',
remainingWords: ['words here'],
points: {
userId: 'points',
},
},
}
const games = {}
const stages = {
STARTING: (counter) => {
return `A new "fast type" game is starting in ${counter}s!`
},
IN_GAME: (word) => {
let spacedWord = ''
for (const character of [...word]) {
spacedWord += character
spacedWord += ' '
}
return `The new word is **${spacedWord}**!`
},
ENDING: (points) => {
const sorted = Object.keys(points).sort((a, b) => {
return points[b] - points[a]
})
let results = ''
for (const key of sorted) {
const amount = points[key]
results += `<#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
}
return `The game is now over Here's how everyone did:\n\n${results}------------------`
},
}
const selectWord = (game) => {
game.currentWord =
game.remainingWords[Math.floor(Math.random() * game.remainingWords.length)]
const index = game.remainingWords.indexOf(game.currentWord)
game.remainingWords.splice(index, 1)
}
const gameLoop = () => {
for (const key in games) {
const game = games[key]
const { message, stage } = game
if (stage === 'STARTING') {
let string = stages[stage](game.counter)
message.edit(string)
if (game.counter <= 0) {
game.stage = 'IN_GAME'
game.counter = 15
selectWord(game)
string = stages[game.stage](game.currentWord)
message.edit(string)
}
} else if (stage === 'IN_GAME') {
if (game.counter <= 0) {
game.stage = 'ENDING'
const string = stages[game.stage](game.points)
message.edit(string)
// Delete the game
delete games[key]
continue
}
}
--game.counter
}
setTimeout(gameLoop, 1000)
}
module.exports = class FastTypeGame extends Commando.Command {
constructor(client) {
super(client, {
name: 'fasttype',
group: 'games',
memberName: 'fasttype',
description: 'Starts a fast type game',
userPermissions: ['ADMINISTRATOR'],
})
client.on('message', (message) => {
const { channel, content, member } = message
const { id } = channel
const game = games[id]
if (game && game.currentWord && !member.user.bot) {
message.delete()
if (
game.stage === 'IN_GAME' &&
content.toLowerCase() === game.currentWord.toLowerCase()
) {
game.currentWord = null
const seconds = 2
const { points } = game
points[member.id] = points[member.id] || 0
message
.reply(`You won! +1 point (${++points[member.id]} total)`)
.then((newMessage) => {
newMessage.delete({
timeout: 1000 * seconds,
})
})
setTimeout(() => {
if (game.stage === 'IN_GAME') {
selectWord(game)
const string = stages[game.stage](game.currentWord)
game.message.edit(string)
}
}, 1000 * seconds)
}
}
})
gameLoop()
}
async run(message) {
const { channel } = message
message.delete()
channel.send('Preparing game...').then((message) => {
games[channel.id] = {
message,
stage: 'STARTING',
counter: 5,
remainingWords: [...words],
points: {
'719805930547445772': 4,
'723819104045105172': 1,
},
}
})
}
}
First change embeded content is not related to discord.js-commando to change the content of sended embeded message you need to get Message Object then using edit() method to pass the new embed content to it:
-Bonus: You can also edit text message into embed message.
Docs for edit method: https://discord.js.org/#/docs/main/stable/class/Message?scrollTo=edit
Example code:
let youCurrentMessage = await channel.send(embedContent);
yourCurrentMessage.edit(newEmbedContent);
yourCurrentMessage.edit(newEmbedContent2);
// If you edit message in other command , session.You need message id
let yourCurrentMessage = await msg.channel.messages.fetch(editMessageId);
yourCurrentMessage.edit(newEmbedContent);

How can I read and write data from a specific port to the console?

I'm writing a listener service. Data is streaming to me from a specific port. But I cannot read the incoming data and write to consola.
I have a listener service written in GoLang. I tried to extract the listener logic from these codes and adapt it for node.js. But I couldn't.
My service can listen to the port, there is no problem in this regard. Because the place where I get the data produces output because your service is running on its side.
Message returning to me from the place that sent data
These are my proto codes:
syntax = "proto3";
import "rpc_base_msg.proto";
//----------Remote Process Control Models BEGIN----------//
message RPCKeyboardActivityRequest {
RequestModel request_model = 1;
}
message RPCKeyboardActivityResponse {
ResponseModel response_model =1;
}
//----------Remote Process Control Models END-----------//
//---------------Data Tranfer Models BEGIN-------------//
message DTKeyboardActivityModel {
uint64 dt = 1; // Date-Time Stamp
enum Activity {
APPLICATION = 0;
WEB_BROWSER = 1;
}
Activity activity = 2; // Activity: Can be application or a web browser.
string activity_name = 3; //Activity is the name of the application or web page name
string key = 4; // This is the user's keystroke sequence. Changes for every different application or same application in every 60 seconds!!!
bool islive_data=5;
uint64 delete_id=6;
}
message DTKeyboardActivityRequest {
DTKeyboardActivityModel dt_keyboard_activity_model = 1;
}
message DTKeyboardActivityResponse {
bool ret_complete = 1; // Return True/False
}
//---------------Data Tranfer Models END-------------//
//----------------Service BEGIN------------------//
service ActivityService {
rpc SSPCKeyboardActivity (RPCKeyboardActivityRequest) returns (stream RPCKeyboardActivityResponse);
rpc UDTKeyboardActivity (DTKeyboardActivityRequest) returns (DTKeyboardActivityResponse);
rpc BDSDTKeyboardActivity (stream DTKeyboardActivityRequest) returns (stream DTKeyboardActivityResponse);
}
//----------------Service END------------------//
These are the proto import codes:
syntax = "proto3";
message RequestModel {
bool is_process_completed = 1;
}
message ResponseModel {
bool is_monitoring = 1;
}
These are my listen service codes:
const grpc = require('#grpc/grpc-js');
const protoLoader = require('#grpc/proto-loader');
const path = "./protos/test.proto";
const path2= "./protos/activity.proto";
const packageDefination = protoLoader.loadSync(path,{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const packageDef = protoLoader.loadSync(path2,{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const test_proto = grpc.loadPackageDefinition(packageDefination);
const activity_proto = grpc.loadPackageDefinition(packageDef);
function sayHello(call,callback) {
callback(null,{message:'Hello '+ call.request.name});
}
function sayHelloAgain(call,callback) {
callback(null,{message:'Hello Again ' + call.request.name});
}
function SSPCKeyboardActivity(call){
let rpcKeyboardActivityRequest = call.request.RPCKeyboardActivityRequest;
console.log("rpcKeyboardActivityRequest:" + rpcKeyboardActivityRequest);
let monitorStatusChange = true;
let whileControl = true;
console.log("SSPCKeyboardActivity client recv :" + GetRequestModel());
while (whileControl === true){
call.write({IsMonitoring: monitorStatusChange})
monitorStatusChange = !monitorStatusChange;
if (!monitorStatusChange){
setTimeout(function () {},30000)
}else {
setTimeout(function () {},10000)
}
}
}
function UDTKeyboardActivity(call,callback) {
let context = call.metadata.get('context.Context');
console.log("Context" + context);
if (context){
let udtKeyboardActivity = call.request.DTKeyboardActivityRequest;
console.log("udtKeyboardActivity" + udtKeyboardActivity);
let dtKeyboardActivityResponse = call.request.DTKeyboardActivityResponse;
console.log("dtKeyboardActivityResponse" + dtKeyboardActivityResponse);
callback(null , dtKeyboardActivityResponse);
console.log("CallBack" + callback);
}else {
console.log("Contex Error");
}
}
function BDSDTKeyboardActivity(call) {
call.on('data', function () {
let dtKeyboardActivityRequest = call.request.DTKeyboardActivityRequest;
console.log("dtKeyboardActivityRequest" + dtKeyboardActivityRequest);
})
call.on('end',function () {
call.write(DTKeyboardActivityResponse);
console.log("Write DTKeyboardActivityResponse"+DTKeyboardActivityResponse);
})
}
function main(){
const server = new grpc.Server();
server.bindAsync('0.0.0.0:5801',grpc.ServerCredentials.createInsecure(),()=>{
server.start();
console.log("Server listening at :5801");
});
server.addService(test_proto.Greeter.service,{
sayHello: sayHello,
sayAgainHello: sayHelloAgain
});
server.addService(activity_proto.ActivityService.service, {
SSPCKeyboardActivity : SSPCKeyboardActivity,
UDTKeyboardActivity: UDTKeyboardActivity,
BDSDTKeyboardActivity: BDSDTKeyboardActivity,
})
}
main();
"path" and testproto are not a problem for me. I ran it. I am having a problem with activity_proto.
I came across after the service was running:
➜ service node server/server.js
Server listening at :5801
As an example, I also share the functions in the service code block written in go.
package service
import (
"context"
"github.com/******/pb"
"log"
"time"
)
type ActivityServer struct {
}
type ActivityRpcUpdateModel struct {
IsMonitor bool
}
func NewActivityServer() *ActivityServer {
return &ActivityServer{}
}
func (a *ActivityServer) SSPCKeyboardActivity(request *pb.RPCKeyboardActivityRequest, server pb.ActivityService_SSPCKeyboardActivityServer)
error {
monitorStatusChange := true
log.Printf("SSPCKeyboardActivity client recv : %v", request.GetRequestModel())
for {
err := server.Send(&pb.RPCKeyboardActivityResponse{ResponseModel: &pb.ResponseModel{IsMonitoring: monitorStatusChange}})
if err != nil {
log.Printf("SSPCForegroundApp err : %v", err)
}
monitorStatusChange = !monitorStatusChange
if !monitorStatusChange {
time.Sleep(time.Second * 30)
} else {
time.Sleep(time.Second * 10)
}
}
}
func (a *ActivityServer) UDTKeyboardActivity(
ctx context.Context,
request *pb.DTKeyboardActivityRequest,
) (*pb.DTKeyboardActivityResponse, error) {
log.Println(request.GetDtKeyboardActivityModel())
return &pb.DTKeyboardActivityResponse{
RetComplete: true,
}, nil
}
func (a *ActivityServer) BDSDTKeyboardActivity(
server pb.ActivityService_BDSDTKeyboardActivityServer,
) error {
panic("implement me")
}
I cannot catch the incoming data. And I can't see in the consol. What kind of changes should I make to my functions on the node.js side of me?

Is there a more convenient way to map the plain js object to grcp response class?

Here is the RESTful API response json:
{
"success": true,
"data": {
"loginname": "mrdulin",
"avatar_url": "https://avatars1.githubusercontent.com/u/1147375?v=4&s=120",
"githubUsername": "mrdulin",
"create_at": "2012-09-09T05:26:58.319Z",
"score": 15835,
"recent_topics": [
{
"id": "5c6d11d033b0b629ac8434ef",
"author": {
"loginname": "mrdulin",
"avatar_url": "https://avatars1.githubusercontent.com/u/1147375?v=4&s=120"
},
"title": "grpc and Node.js",
"last_reply_at": "2019-05-11T04:22:18.616Z"
}
],
"recent_replies": []
}
}
UserServiceImpl.ts:
export class UserServiceImpl implements IUserServiceServer {
public async getUserByLoginname(call: ServerUnaryCall<GetUserByLoginnameRequest>, callback: sendUnaryData<GetUserByLoginnameResponse>) {
const loginname = call.request.getLoginname();
const url = `${config.CNODE_API_URL}/user/${loginname}`;
try {
const res = await axios.get(url);
const data = res.data.data;
// map API response js plain object to GetUserByLoginnameResponse
const grcpResponse = new GetUserByLoginnameResponse();
grcpResponse.setSuccess(res.data.success);
const user = new UserDetail();
user.setAvatarUrl(data.avatar_url);
user.setLoginname(data.loginname);
user.setGithubusername(data.githubUsername);
user.setCreateAt(data.create_at);
user.setScore(data.score);
const recentReplies = data.recent_replies.map((po) => {
const reply = new RecentReply();
reply.setId(po.id);
reply.setTitle(po.title);
const lastReplyAt = new google_protobuf_timestamp_pb.Timestamp();
lastReplyAt.fromDate(new Date(po.last_reply_at));
reply.setLastReplyAt(lastReplyAt);
const author = new UserBase();
author.setLoginname(po.author.loginname);
author.setAvatarUrl(po.author.avatar_url);
reply.setAuthor(author);
return reply;
});
const recentTopics = data.recent_topics.map((po) => {
const topic = new TopicBase();
topic.setId(po.id);
topic.setTitle(po.title);
topic.setLastReplyAt(po.last_reply_at);
const author = new UserBase();
author.setLoginname(po.author.loginname);
author.setAvatarUrl(po.author.avatar_url);
topic.setAuthor(author);
return topic;
});
user.setRecentRepliesList(recentReplies);
user.setRecentTopicsList(recentTopics);
grcpResponse.setData(user);
callback(null, grcpResponse);
} catch (error) {
console.error(error);
const metadata = new Metadata();
metadata.set('url', url);
const ErrGetUserByLoginname: ServiceError = {
code: status.INTERNAL,
name: 'getUserByLoginnameError',
message: 'call CNode API failed',
metadata,
};
callback(ErrGetUserByLoginname, null);
}
}
}
As you can see, I have to map the plain javascript object(the API response) to GetUserByLoginnameResponse class manually. This is cumbersome and inefficient.
I am using static codegen, so the GetUserByLoginnameResponse class is generated based on IDL defined in the .proto files.
user.service.proto:
service UserService {
rpc GetUserByLoginname(GetUserByLoginnameRequest)
returns (GetUserByLoginnameResponse);
}
message GetUserByLoginnameRequest { string loginname = 1; }
message GetUserByLoginnameResponse {
UserDetail data = 1;
bool success = 2;
}
message UserDetail {
string loginname = 1;
string avatar_url = 2;
string githubUsername = 3;
string create_at = 4;
int32 score = 5;
repeated share.TopicBase recent_topics = 6;
repeated reply.RecentReply recent_replies = 7;
}

Resources