<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: abdullah khrais</title>
    <description>The latest articles on DEV Community by abdullah khrais (@abdullah_khrais_97a2c908d).</description>
    <link>https://dev.to/abdullah_khrais_97a2c908d</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1743221%2Fb39d824b-ae01-419a-82cb-dc0b2dbf5a87.png</url>
      <title>DEV Community: abdullah khrais</title>
      <link>https://dev.to/abdullah_khrais_97a2c908d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abdullah_khrais_97a2c908d"/>
    <language>en</language>
    <item>
      <title>In this guide, I’ll show you how to create a new user in Oracle and give them the right permissions.</title>
      <dc:creator>abdullah khrais</dc:creator>
      <pubDate>Sun, 11 Aug 2024 17:09:51 +0000</pubDate>
      <link>https://dev.to/abdullah_khrais_97a2c908d/in-this-guide-ill-show-you-how-to-create-a-new-user-in-oracle-and-give-them-the-right-permissions-1pb6</link>
      <guid>https://dev.to/abdullah_khrais_97a2c908d/in-this-guide-ill-show-you-how-to-create-a-new-user-in-oracle-and-give-them-the-right-permissions-1pb6</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE USER C##YOUR_NAME IDENTIFIED BY YOUR_PASSWORD;
grant unlimited tablespace to  C##YOUR_NAME;
grant resource, connect, dba to  C##YOUR_NAME;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  This code needs to be executed by an admin user.
&lt;/h2&gt;

</description>
    </item>
    <item>
      <title>Hey everyone, Check out this SQL script to delete all tables and indexes in a schema. Great for fast cleanup in dev/testing!</title>
      <dc:creator>abdullah khrais</dc:creator>
      <pubDate>Thu, 11 Jul 2024 06:46:14 +0000</pubDate>
      <link>https://dev.to/abdullah_khrais_97a2c908d/hey-everyonecheck-out-this-sql-script-to-delete-all-tables-and-indexes-in-a-schema-great-for-fast-cleanup-in-devtesting-3dd1</link>
      <guid>https://dev.to/abdullah_khrais_97a2c908d/hey-everyonecheck-out-this-sql-script-to-delete-all-tables-and-indexes-in-a-schema-great-for-fast-cleanup-in-devtesting-3dd1</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN
    FOR t IN (SELECT table_name FROM user_tables) LOOP
        EXECUTE IMMEDIATE 'DROP TABLE ' || t.table_name || ' CASCADE CONSTRAINTS';
    END LOOP;

    FOR i IN (SELECT index_name FROM user_indexes) LOOP
        EXECUTE IMMEDIATE 'DROP INDEX ' || i.index_name;
    END LOOP;
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;Explanation:&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Tables: The script iterates through all tables in the current schema (user_tables) and drops each table using DROP TABLE ... CASCADE CONSTRAINTS to handle dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indexes: Similarly, it iterates through all indexes (user_indexes) and drops each one using DROP INDEX.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>ntegrating SignalR with Angular for Seamless Video Calls: A Step-by-Step Guide</title>
      <dc:creator>abdullah khrais</dc:creator>
      <pubDate>Mon, 08 Jul 2024 18:52:51 +0000</pubDate>
      <link>https://dev.to/abdullah_khrais_97a2c908d/ntegrating-signalr-with-angular-for-seamless-video-calls-a-step-by-step-guide-mdh</link>
      <guid>https://dev.to/abdullah_khrais_97a2c908d/ntegrating-signalr-with-angular-for-seamless-video-calls-a-step-by-step-guide-mdh</guid>
      <description>&lt;p&gt;Hi everyone! Today, we'll explore how to create a simple video call web app using WebRTC, Angular, and ASP.NET Core. This guide will walk you through the basics of setting up a functional application with these technologies. WebRTC enables peer-to-peer video, voice, and data communication, while SignalR will handle the signaling process needed for users to connect. We'll start with the backend by creating a .NET Core web API project and adding the SignalR NuGet package. Check out the repository links at the end for the complete code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt; *&lt;em&gt;Step1: Create .NET Core API Project *&lt;/em&gt;
First, create a .NET Core web API project and install the SignalR package:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;dotnet add package Microsoft.AspNetCore.SignalR.Core&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 2: Create the VideoCallHub Class&lt;/strong&gt;
Next, create a class VideoCallHub:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace Exam_Guardian.API
{
    public class VideoCallHub : Hub
    {
        private static readonly ConcurrentDictionary&amp;lt;string, string&amp;gt; userRooms = new ConcurrentDictionary&amp;lt;string, string&amp;gt;();

        public override async Task OnConnectedAsync()
        {
            await base.OnConnectedAsync();
            await Clients.Caller.SendAsync("Connected", Context.ConnectionId);
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            if (userRooms.TryRemove(Context.ConnectionId, out var roomName))
            {
                await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
            }
            await base.OnDisconnectedAsync(exception);
        }

        public async Task JoinRoom(string roomName)
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
            userRooms.TryAdd(Context.ConnectionId, roomName);
            await Clients.Group(roomName).SendAsync("RoomJoined", Context.ConnectionId);
        }

        public async Task SendSDP(string roomName, string sdpMid, string sdp)
        {
            if (userRooms.ContainsKey(Context.ConnectionId))
            {
                await Clients.OthersInGroup(roomName).SendAsync("ReceiveSDP", Context.ConnectionId, sdpMid, sdp);
            }
            else
            {
                await Clients.Caller.SendAsync("Error", "You are not in a room");
            }
        }

        public async Task SendICE(string roomName, string candidate, string sdpMid, int sdpMLineIndex)
        {
            if (userRooms.ContainsKey(Context.ConnectionId))
            {
                await Clients.OthersInGroup(roomName).SendAsync("ReceiveICE", Context.ConnectionId, candidate, sdpMid, sdpMLineIndex);
            }
            else
            {
                await Clients.Caller.SendAsync("Error", "You are not in a room");
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**- Step 3: Register the Hub in Program.cs&lt;br&gt;
Register the SignalR hub and configure CORS in Program.cs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddSignalR();
builder.Services.AddCors(options =&amp;gt;
{
    options.AddPolicy("AllowAngularDev", builder =&amp;gt;
    {
        builder.WithOrigins("http://localhost:4200", "http://[your_ip_address]:4200")
               .AllowAnyHeader()
               .AllowAnyMethod()
               .AllowCredentials();
    });
});

app.UseCors("AllowAngularDev");

app.UseEndpoints(endpoints =&amp;gt;
{
    endpoints.MapHub&amp;lt;VideoCallHub&amp;gt;("/videoCallHub");
    endpoints.MapControllers();
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;With this, the backend setup for SignalR is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;- Step 1: Create Angular Project&lt;/strong&gt;&lt;br&gt;
Create an Angular project and install the required packages:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @microsoft/signalr cors express rxjs simple-peer tslib webrtc-adapter zone.js&lt;/code&gt;&lt;br&gt;
**- Step 2: Create Service Called SignalRService,&lt;br&gt;
inside this service set this code,&lt;/p&gt;

&lt;p&gt;inside this service set this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@angular/core';
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SignalRService {
  private hubConnection: HubConnection;
  private sdpReceivedSource = new Subject&amp;lt;any&amp;gt;();
  private iceReceivedSource = new Subject&amp;lt;any&amp;gt;();
  private connectionPromise: Promise&amp;lt;void&amp;gt;;

  sdpReceived$ = this.sdpReceivedSource.asObservable();
  iceReceived$ = this.iceReceivedSource.asObservable();

  constructor() {
    this.hubConnection = new HubConnectionBuilder()
      .withUrl('http://[your_local_host]/videoCallHub')
      .build();

    this.connectionPromise = this.hubConnection.start()
      .then(() =&amp;gt; console.log('SignalR connection started.'))
      .catch(err =&amp;gt; console.error('Error starting SignalR connection:', err));

    this.hubConnection.on('ReceiveSDP', (connectionId: string, sdpMid: string, sdp: string) =&amp;gt; {
      this.sdpReceivedSource.next({ connectionId, sdpMid, sdp });
    });

    this.hubConnection.on('ReceiveICE', (connectionId: string, candidate: string, sdpMid: string, sdpMLineIndex: number) =&amp;gt; {
      this.iceReceivedSource.next({ connectionId, candidate, sdpMid, sdpMLineIndex });
    });
  }

  private async ensureConnection(): Promise&amp;lt;void&amp;gt; {
    if (this.hubConnection.state !== 'Connected') {
      await this.connectionPromise;
    }
  }

  async joinRoom(roomName: string): Promise&amp;lt;void&amp;gt; {
    await this.ensureConnection();
    return this.hubConnection.invoke('JoinRoom', roomName)
      .then(() =&amp;gt; console.log(`Joined room ${roomName}`))
      .catch(err =&amp;gt; console.error('Error joining room:', err));
  }

  async sendSDP(roomName: string, sdpMid: string, sdp: string): Promise&amp;lt;void&amp;gt; {
    await this.ensureConnection();
    return this.hubConnection.invoke('SendSDP', roomName, sdpMid, sdp)
      .catch(err =&amp;gt; {
        console.error('Error sending SDP:', err);
        throw err;
      });
  }

  async sendICE(roomName: string, candidate: string, sdpMid: string, sdpMLineIndex: number): Promise&amp;lt;void&amp;gt; {
    await this.ensureConnection();
    return this.hubConnection.invoke('SendICE', roomName, candidate, sdpMid, sdpMLineIndex)
      .catch(err =&amp;gt; {
        console.error('Error sending ICE candidate:', err);
        throw err;
      });
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**- Step 3: create your component called VideoCallComponent&lt;br&gt;
inside VideoCallComponent.ts&lt;br&gt;
set this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { SignalRService } from '../../../core/services/video-call-signal-r.service';

@Component({
  selector: 'app-video-call',
  templateUrl: './video-call.component.html',
  styleUrls: ['./video-call.component.css']
})
export class VideoCallComponent implements OnInit, OnDestroy {
  roomName: string = 'room1'; // Change this as needed
  private sdpSubscription: Subscription;
  private iceSubscription: Subscription;
  private localStream!: MediaStream;
  private peerConnection!: RTCPeerConnection;

  constructor(private signalRService: SignalRService) {
    this.sdpSubscription = this.signalRService.sdpReceived$.subscribe(data =&amp;gt; {
      console.log('Received SDP:', data);
      this.handleReceivedSDP(data);
    });

    this.iceSubscription = this.signalRService.iceReceived$.subscribe(data =&amp;gt; {
      console.log('Received ICE Candidate:', data);
      this.handleReceivedICE(data);
    });
  }

  async ngOnInit(): Promise&amp;lt;void&amp;gt; {
    await this.signalRService.joinRoom(this.roomName);
    this.initializePeerConnection();
  }

  ngOnDestroy(): void {
    this.sdpSubscription.unsubscribe();
    this.iceSubscription.unsubscribe();
    this.endCall();
  }

  async startCall() {
    try {
      await this.getLocalStream();

      if (this.peerConnection.signalingState === 'stable') {
        const offer = await this.peerConnection.createOffer();
        await this.peerConnection.setLocalDescription(offer);
        await this.signalRService.sendSDP(this.roomName, 'offer', offer.sdp!);
        console.log('SDP offer sent successfully');
      } else {
        console.log('Peer connection not in stable state to create offer');
      }
    } catch (error) {
      console.error('Error starting call:', error);
    }
  }

  async getLocalStream() {
    this.localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    const localVideo = document.getElementById('localVideo') as HTMLVideoElement;
    localVideo.srcObject = this.localStream;

    this.localStream.getTracks().forEach(track =&amp;gt; this.peerConnection.addTrack(track, this.localStream));
  }

  initializePeerConnection() {
    this.peerConnection = new RTCPeerConnection();

    this.peerConnection.ontrack = (event) =&amp;gt; {
      const remoteVideo = document.getElementById('remoteVideo') as HTMLVideoElement;
      if (remoteVideo.srcObject !== event.streams[0]) {
        remoteVideo.srcObject = event.streams[0];
        console.log('Received remote stream');
      }
    };

    this.peerConnection.onicecandidate = (event) =&amp;gt; {
      if (event.candidate) {
        this.signalRService.sendICE(this.roomName, event.candidate.candidate, event.candidate.sdpMid!, event.candidate.sdpMLineIndex!)
          .then(() =&amp;gt; console.log('ICE candidate sent successfully'))
          .catch(error =&amp;gt; console.error('Error sending ICE candidate:', error));
      }
    };
  }

  async handleReceivedSDP(data: any) {
    const { connectionId, sdpMid, sdp } = data;

    try {
      const remoteDesc = new RTCSessionDescription({ type: sdpMid === 'offer' ? 'offer' : 'answer', sdp });
      await this.peerConnection.setRemoteDescription(remoteDesc);

      if (sdpMid === 'offer') {
        const answer = await this.peerConnection.createAnswer();
        await this.peerConnection.setLocalDescription(answer);
        await this.signalRService.sendSDP(this.roomName, 'answer', answer.sdp!);
        console.log('SDP answer sent successfully');
      }
    } catch (error) {
      console.error('Error handling received SDP:', error);
    }
  }

  async handleReceivedICE(data: any) {
    const { connectionId, candidate, sdpMid, sdpMLineIndex } = data;

    try {
      await this.peerConnection.addIceCandidate(new RTCIceCandidate({ candidate, sdpMid, sdpMLineIndex }));
      console.log('ICE candidate added successfully');
    } catch (error) {
      console.error('Error handling received ICE candidate:', error);
    }
  }

  endCall() {
    if (this.peerConnection) {
      this.peerConnection.close();
      console.log('Call ended');
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**- Step 4: inside html&lt;br&gt;
set this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;div&amp;gt;
  &amp;lt;button (click)="startCall()"&amp;gt;Start Call&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;video id="localVideo" autoplay muted&amp;gt;&amp;lt;/video&amp;gt;
&amp;lt;video id="remoteVideo" autoplay&amp;gt;&amp;lt;/video&amp;gt;




&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>signalr</category>
      <category>api</category>
      <category>angular</category>
      <category>videocall</category>
    </item>
  </channel>
</rss>
